1 (изменено: Alectric, 2025-04-16 19:53:22)

Тема: AHK v2: Class Рисование графиков из массива данных

Конвертация этой версии.

Тема для обсуждения.

Описание и примеры добавлю позже, сейчас сил нет, читайте описание для первой версии там почти тоже самое.

Неоформленный пример:


arr3:=[]
loop 7
  arr3.Push([])
c:=1
d:=0
e:=1
a:=0
t:=0
b:=0
arr:=[]

settimer ddd,150
settimer aaa,500

gra:=Graph()
gra.show()

gra.ArrName[1]:="кг"
gra.ArrName[2]:="Двигатель"
gra.ArrName[3]:="Температура"
gra.ArrName[4]:="Разряжение"
gra.ArrName[5]:="Натяжение"
gra.ArrName[6]:="Угол"
gra.ArrName[7]:="Ничего"

gra.DataArray:=arr3

d:=e:=-1300

f1::
{
  gra.show()
}

f2::
{
  if (gra.DataArray=arr3)
    gra.DataArray:=arr
  else
    gra.DataArray:=arr3
}

ddd()
{
  global
  a+=0.1
  if (b<-1 or b>1)
    t:=!t
  if t
    b+=0.005
  else
    b-=0.005
  if (b>1)
  {
    d:=random(-1000000000,1000000000)
    e:=random(-1000000000,1000000000)
    c:=random(0.5,1.5)
  }
  var:=random(50.5,100.5)
  shift:=0
  Val:=(sin(a)*var*b+shift)

  arr.Push(sin(a))

  arr3[1].Push(Val-30)
  arr3[4].Push([Val,A_Now,A_MSec])
  arr3[3].Push([-Val,A_Now,A_MSec])
  arr3[5].Push([cos(a*a)*0.1,A_Now,A_MSec])
  arr3[6].Push([cos(-a*2)*10*b+10,A_Now,A_MSec])
  arr3[7].Push([random(-1000000000,1000000000),A_Now,A_MSec])
}

aaa()
{
  static bool:=1
  bool:=!bool
  arr3[2].Push([bool,A_Now,A_MSec])

}

Esc::
{
global
gra:=""
exitapp
}

Class:

class Graph
{
  ; #requires AutoHotkey v2.0.19
  static hModule:=DllCall("LoadLibrary","str","gdiplus","Ptr")
  static pToken:=0, InstanceCount:=0
  static WinText:="Special text to identify the ownership of the Graph window."
  ;gdiplus\
  static hGdipCreateFont:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreateFont","Ptr")
  static hGdipCreateFontFamilyFromName:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreateFontFamilyFromName","Ptr")
  static hGdipCreateFromHDC:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreateFromHDC","Ptr")
  static hGdipCreatePen1:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreatePen1","Ptr")
  static hGdipCreateSolidFill:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreateSolidFill","Ptr")
  static hGdipCreateStringFormat:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreateStringFormat","Ptr")
  static hGdipDeleteBrush:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDeleteBrush","Ptr")
  static hGdipDeleteFont:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDeleteFont","Ptr")
  static hGdipDeleteFontFamily:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDeleteFontFamily","Ptr")
  static hGdipDeletePen:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDeletePen","Ptr")
  static hGdipDeleteStringFormat:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDeleteStringFormat","Ptr")
  static hGdipDrawEllipse:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDrawEllipse","Ptr")
  static hGdipDrawLine:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDrawLine","Ptr")
  static hGdipDrawLines:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDrawLines","Ptr")
  static hGdipDrawString:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDrawString","Ptr")
  static hGdipFillRectangle:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipFillRectangle","Ptr")
  static hGdiplusShutdown:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdiplusShutdown","Ptr")
  static hGdiplusStartup:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdiplusStartup","Ptr")
  static hGdipMeasureString:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipMeasureString","Ptr")
  static hGdipSetSmoothingMode:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipSetSmoothingMode","Ptr")
  static hGdipSetStringFormatAlign:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipSetStringFormatAlign","Ptr")
  static hGdipSetTextRenderingHint:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipSetTextRenderingHint","Ptr")
  static hGdipReleaseDC:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipReleaseDC","Ptr")
  static hGdipDeleteGraphics:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDeleteGraphics","Ptr")
  static hGdipCreateBitmapFromHBITMAP:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreateBitmapFromHBITMAP","Ptr")
  static hGdipCreateTexture2:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipCreateTexture2","Ptr")
  static hGdipDisposeImage:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDisposeImage","Ptr")
  static hGdipDrawRectangle:=DllCall("GetProcAddress","Ptr",Graph.hModule,"AStr","GdipDrawRectangle","Ptr")

  static min:=-9223372036854775808
  static max:= 9223372036854775807
  static MaxTime:=30*24*3600000 ; миллисекунд
  static WheelUp:=false
  static WheelDown:=false
  static HotKeymW:=0

  __Delete()
  {
    if this.Deleted
      return
    this.Deleted:=true
    loop this.RefCount
      ObjAddRef(ObjPtr(this))
;    tooltip "Деструктор вызван.`n" Graph.InstanceCount "`n" this.Name
;    sleep 1000
;    tooltip
    settimer this.UpdateTimer,0
    this.UpdateTimer:=""
    DllCall(Graph.hGdipDeleteGraphics,"UPtr",this.pGraphics)
    DllCall("SelectObject","Ptr",this.hDC,"Ptr",this.obm)
    DllCall("DeleteObject","UPtr",this.hBitmap)
    DllCall("ReleaseDC","Ptr",this.GuiGDIP.Hwnd,"Ptr",this.hDC)
    this.GuiHelp.Destroy()
    this.GuiGoToT.Destroy()
    this.GuiGDIP.Destroy()
    this.GuiGraph.Destroy()
    this.GraphMenu.Delete()
    this.GraphMenuSettings.Delete()
    loop this.Child.Length
    {
      ObjAddRef(ObjPtr(this))
      this.Child[a_index]:=""
    }
    Graph.InstanceCount--
    if (Graph.InstanceCount=0)
    {
      DllCall(Graph.hGdiplusShutdown,"Ptr",Graph.pToken)
      Graph.pToken:=""
    }
  }

  mi:={MenuAlwaysOnTop:"Всегда поверх окон"
	,MenuAutoScale:"Автомасштаб"
	,MenuSmooth:"Сглаживать ступеньки"
	,MenuFlFt:"Количество знаков после запятой..."
	,MenuScaleXByTime:"Масштабировать X по времени"
	,MenuScaleXByDots:"Масштабировать X по точкам"
	,MenuResetFont:"Сбросить размер шрифта"
	,MenuGColor:"Выбрать цвет графика..."
	,MenuName:"Изнменить имя выбранного графика..."
	,MenuSave:"Сохранить график в файл..."
	,MenuLoad:"Загрузить график из файла..."
	,MenuClose:"Х "
	,MenuPause:"(||)"
	,MenuForward:"(>|)"
	,MenuBackward:"(|<)"
	,MenuGoTo:"Перейти"
	,MenuMarkers:"Маркеры"
	,MenuHelp:"Справка"
	,Settings:"Настр."}

  __New(Name:="",UpdatePeriod:=50)
  {
    ; Запустить библиотеку гдиплюс
    if !Graph.pToken
    {
      si:=Buffer(A_PtrSize=8 ? 24 : 16,0)
      NumPut("UChar",1,si,0)
      DllCall(Graph.hGdiplusStartup,"UPtr*",&pToken:=0,"Ptr",si.Ptr,"Ptr",0)
      Graph.pToken:=pToken
    }
    Graph.InstanceCount++

    if !Name
      this.Name:="График " Graph.InstanceCount

    ; Инициализация переменных

    this.ArrName:=[]
    loop 10
      this.ArrName.Push("")
    this.IsChild:=false
    this.Child:=[]
    this.Parent:=0
    this.Deleted:=false
    this.SaveArrName:=[]
    this.SetParentOnce:=false
    this.CreateTime:=[A_Now,A_msec]
    this.Data:=[]
    this.NewData:=""
    this.Loaded:=0
    this.FloatFormat:=3
    this.Drawing:=false
    this.UpdatePeriod:=UpdatePeriod
    this.hFormat:=0
    this.hFamily:=0
    this.hFont:=0
    this.prevRenderingHint:=""
    this.prevBrushColor:=0
    this.prevPenColor:=0
    this.prevNoWrap:=0
    this.prevVertical:=0
    this.prevFont:=""
    this.prevSize:=0
    this.prevfStyle:=""
    this.prevRenderingHint:=""
    this.prevColour:=0
    this.pBrush:=0
    this.pPen:=0
    this.FontpBrush:=0
    this.GPosW:=0
    this.GPosH:=0
    this.GPosH_:=0
    this.Pause:=false
    this.OScale:=true

    this.GetmCoordOnce:=false
    this.GetStartPosRectOnce:=false

    this.ZoomX:=100
    this.ZoomT:=30000
    this.OldZoomX:=this.ZoomX
    this.OldZoomT:=this.ZoomT
    this.SaveZoomX:=this.ZoomX
    this.SaveZoomT:=this.ZoomT
    this.ShowZoomTip:=0
    this.XTOffset:=0
    this.X_Offset:=0
    this.ZeroPos:=30
    this.ZeroPosU:=1
    this.GPosH:=160

    this.AStartTime:=0
    this.StampOffset:=0
    this.MarkerOffset:=0
    this.StampsNum:=5
    this.XMarker:=[-9999,-9999]
    this.MenuOffset:=0
    this.Old_XTOffset:=0
    this.OldXTOffset:=0

    this.Sel:=1
    this.Proc:=0
    this.StartTime:=0
    this.OldmX:=0
    this.OldmY:=0
    this.FontpBrush:=0
    this.SaveStrt:=[0,0]
    this.ShowMarkers:=false
    this.OffsetSetting:=false
    this.ShiftStrt:=false
    this.MouseMoved:=false
    this.OldX_Offset:=0
    this.StampPos:={X:0,Y:0,W:0,H:this.GPosH,Chars:0,Lines:0}
    this.ETPos:={X:0,Y:0,W:0,H:0,Chars:0,Lines:0}
    this.TipPos:={X:0,Y:0,W:0,H:0,Chars:0,Lines:0}
    this.iZoomPos:={X:0,Y:0,W:0,H:0,Chars:0,Lines:0}
    this.OScalePos:={X:0,Y:0,W:0,H:0,Chars:0,Lines:0}
    this.Setting:=false
    this.RectZoom:=false
    this.XMarkerOnce:=false
    this.AlwaysOnTop:=false
    this.PrevW:=0
    this.PrevH:=0
    this.TimeOut:=false
    this.TOZoomX:=0
    this.TOZoomT:=0

    ; Создать графику
    this.Create()
    this.ReInit()

    ; Настройки по умолчанию
    this.Font:="Lucida Console" ; шрифт
    this.FontSize:=12 ; размер шрифта
    this.SetFont(this.Font,this.FontSize)
    this.Font_Colour:=0xff000000 ; цвет шрифта
    this.Font_Back:=0xd0ffffff ; цвет фона шрифта
    this.WhiteBrush:=0xffffffff ; для общего фона
    this.GrayPen:=0xaa808080 ; для оси
    this.GrayPen2:=0xaae0e0e0 ; для оси
    this.RedPen:=0xffff0000 ; для маркера
    this.LightGreenBrush:=0xffd5ffd5 ; для фона информации
    this.LightBlueBrush:=0xffd5d5ff ; для фона Меню
    this.BluePen:=0xff0000ff ; для графика

    s:=this.mi.MenuClose this.mi.MenuPause this.mi.MenuForward this.mi.MenuBackward this.mi.MenuGoTo this.mi.MenuMarkers this.mi.Settings this.mi.MenuHelp
    loop 12
      s.="_"
    w:=this.MeasureString(s,this.CreateRectF(0,0,1400,100))
    ; смещение исходной высоты если нет данных

    this.MessagePos:={X:0,Y:0,W:0,H:w.H,Chars:0,Lines:0}

; Добавление меню
    ; Меню настроек
    this.GraphMenuSettings:=Menu()

    obm:=ObjBindMethod(this,"MenuSave")
    this.GraphMenuSettings.add(this.mi.MenuSave,obm)

    obm:=ObjBindMethod(this,"MenuLoad")
    this.GraphMenuSettings.add(this.mi.MenuLoad,obm)

    this.GraphMenuSettings.add()

    obm:=ObjBindMethod(this,"MenuAlwaysOnTop")
    this.GraphMenuSettings.add(this.mi.MenuAlwaysOnTop,obm)

    obm:=ObjBindMethod(this,"MenuAutoScale")
    this.GraphMenuSettings.add(this.mi.MenuAutoScale,obm)
    this.GraphMenuSettings.check(this.mi.MenuAutoScale)

    obm:=ObjBindMethod(this,"MenuSmooth")
    this.GraphMenuSettings.add(this.mi.MenuSmooth,obm)
    this.GraphMenuSettings.check(this.mi.MenuSmooth)

    obm:=ObjBindMethod(this,"MenuFlFt")
    this.GraphMenuSettings.add(this.mi.MenuFlFt,obm)

    this.GraphMenuSettings.add()

    obm:=ObjBindMethod(this,"MenuScaleXByTime")
    this.GraphMenuSettings.add(this.mi.MenuScaleXByTime,obm,"+Radio")
    this.GraphMenuSettings.check(this.mi.MenuScaleXByTime)
    this.ScaleXByTime:=true

    obm:=ObjBindMethod(this,"MenuScaleXByDots")
    this.GraphMenuSettings.add(this.mi.MenuScaleXByDots,obm,"+Radio")

    this.GraphMenuSettings.add()

    obm:=ObjBindMethod(this,"MenuName")
    this.GraphMenuSettings.add(this.mi.MenuName,obm)

    obm:=ObjBindMethod(this,"MenuGColor")
    this.GraphMenuSettings.add(this.mi.MenuGColor,obm)

    obm:=ObjBindMethod(this,"MenuResetFont")
    this.GraphMenuSettings.add(this.mi.MenuResetFont,obm)

    ; основное меню
    this.GraphMenu:=MenuBar()
;    obm:=ObjBindMethod(this,"MenuClose")
;    this.GraphMenu.add(this.mi.MenuClose,obm)

    this.GraphMenu.add(this.mi.Settings,this.GraphMenuSettings)

    obm:=ObjBindMethod(this,"MenuBackward")
    this.GraphMenu.add(this.mi.MenuBackward,obm)

    obm:=ObjBindMethod(this,"MenuPause")
    this.GraphMenu.add(this.mi.MenuPause,obm)

    obm:=ObjBindMethod(this,"MenuForward")
    this.GraphMenu.add(this.mi.MenuForward,obm)

    obm:=ObjBindMethod(this,"MenuGoTo")
    this.GraphMenu.add(this.mi.MenuGoTo,obm)

    obm:=ObjBindMethod(this,"MenuMarkers")
    this.GraphMenu.add(this.mi.MenuMarkers,obm)

    obm:=ObjBindMethod(this,"MenuHelp")
    this.GraphMenu.add(this.mi.MenuHelp,obm)

; Создание основного окна
    this.GuiGraph:=Gui("+MinSize400x100 +Resize",this.Name)
    this.GuiGraph.MarginX:=0
    this.GuiGraph.MarginY:=0
    this.GuiGraph.MenuBar:=this.GraphMenu
    this.GuiGraph.Add("Text","w0 h0",Graph.WinText)
    obm:=ObjBindMethod(this,"DropFiles")
    this.GuiGraph.OnEvent("DropFiles",obm)
    obm:=ObjBindMethod(this,"Hide")
    this.GuiGraph.OnEvent("Close",obm)

; Создание окна отрисовки графики
    this.GuiGDIP:=Gui("+owner" this.GuiGraph.Hwnd " -Caption +ToolWindow +E0x08080020") ; WS_EX_LAYERED := 0x80000
    this.GuiGDIP.Show("NoActivate x0 y0 w1 h1")

; Создание окна перехода по времени
    this.GuiGoToT:=Gui("+owner" this.GuiGraph.Hwnd " -sysmenu -Resize","Перейти на время")
    this.GuiGoToT.SetFont("s10")
    this.hTT:=this.GuiGoToT.Add("DateTime","xm+1","HH:mm:ss        dd.MM.yyyy")
    obm:=ObjBindMethod(this,"TTOk")
    this.GuiGoToT.Add("Button","xm+5 y+5","Ok").OnEvent("Click",obm)
    obm:=ObjBindMethod(this,"TTCancel")
    this.GuiGoToT.Add("Button","x+10","Отмена").OnEvent("Click",obm)

; Создание окна перехода к точке
    this.GuiGoToX:=Gui("+owner" this.GuiGraph.Hwnd " -sysmenu -Resize","Перейти к точке")
    this.GuiGoToX.SetFont("s10")
    this.hTX:=this.GuiGoToX.Add("Edit","xm+1 w150 Number",1)
    obm:=ObjBindMethod(this,"TXOk")
    this.GuiGoToX.Add("Button","xm+5 y+5","Ok").OnEvent("Click",obm)
    obm:=ObjBindMethod(this,"TXCancel")
    this.GuiGoToX.Add("Button","x+10","Отмена").OnEvent("Click",obm)

; Создание окна справки
    this.GuiHelp:=Gui("+owner" this.GuiGraph.Hwnd " -sysmenu -Resize","Справка")
    this.GuiHelp.SetFont("s10")
    HelpText:=
    (
      "Левая кнопка мыши - перемещение графика.
      Правая кнопка мыши - выделение и увеличение выделенного участка.
      Клик правой кнопкой - возврат к предыдущему масштабу после увеличения."
    )
    this.GuiHelp.Add("Text",,HelpText)
    obm:=ObjBindMethod(this,"HelpClose")
    this.GuiHelp.Add("Button","xm+10 y+5","Закрыть").OnEvent("Click",obm)

; Создание окна изменения имени выбранного графика
    this.GuiName:=Gui("+owner" this.GuiGraph.Hwnd " -sysmenu -Resize","Имя графика")
    this.GuiName.SetFont("s10")
    this.GuiNameVal:=this.GuiName.Add("Edit","xm+1 w150",3)
    obm:=ObjBindMethod(this,"NameOk")
    this.GuiName.Add("Button","xm+5 y+5","Ok").OnEvent("Click",obm)
    obm:=ObjBindMethod(this,"NameCancel")
    this.GuiName.Add("Button","x+10","Отмена").OnEvent("Click",obm)

; Создание окна изменения значения настройки
    this.GuiVal:=Gui("+owner" this.GuiGraph.Hwnd " -sysmenu -Resize","Изменить")
    this.GuiVal.SetFont("s10")
    this.GuiValText1:=this.GuiVal.Add("Text","xm+10 w350")
    this.GuiValVal:=this.GuiVal.Add("Edit","xm+5 y+5 wp Number",3)
    this.GuiValText2:=this.GuiVal.Add("Text","xm+10 y+1 wp")
    obm:=ObjBindMethod(this,"ValAcc")
    this.GuiVal.Add("Button","xm+5 y+5","Применить").OnEvent("Click",obm)
    obm:=ObjBindMethod(this,"ValOk")
    this.GuiVal.Add("Button","x+10","Ok").OnEvent("Click",obm)
    obm:=ObjBindMethod(this,"ValCancel")
    this.GuiVal.Add("Button","x+10","Отмена").OnEvent("Click",obm)
    this.Whichval:=""

; Запомнить метку для таймера
    this.UpdateTimer:=ObjBindMethod(this,"UpdateGraph")
    this.GuiHiden:=true
;    this.UpdateGraph()

    this.RefCount:=30
    loop this.RefCount
      ObjRelease(ObjPtr(this))

    /*
    форматы обрабатывемых данных

    Data:=[1,2,3,4,5,6,76,56,3,45,6,22,45,234]
     - простой массив значений

    Data:=[[1,A_Now],[2,A_Now],[3,A_Now],[4,A_Now]]
     - массив с меткой времени [[значение,время],[значение,время]...]

    Data:=[[1,A_Now,A_MSec],[2,A_Now,A_MSec],[3,A_Now,A_MSec],[4,A_Now,A_MSec]]
     - массив с меткой времени с миллисекундами [[значение,время,миллисекунды],[значение,время,миллисекунды]...]

    Data:=[[1,2],[[1,A_Now],[2,A_Now]],[[1,A_Now,A_MSec],[2,A_Now,A_MSec]]]
     - массив любых выше перечисленных массивов
    */
  }

  DataArray
  {
    set
    {
      if this.SaveArrName.Length
      {
        this.ArrName:=this.SaveArrName.Clone()
        this.SaveArrName:=[]
      }
      this.Loaded:=2
      this.NewData:=Value
    }
    get
    {
      return this.Data
    }
  }

  HitBox(X,Y,Rect)
  {
    return (X>Rect.X and X<Rect.X+Rect.W and Y>Rect.Y and Y<Rect.Y+Rect.H)
  }

;/////////////////////////////////////////////////////////////////////////////////////////////////
;|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|
;||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////||
;|||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||
;||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/||||
  ReInit()
  {
    this.prevArrSize:=[]
    this.SearchStop:=[]
    this.SearchSkip:=[]
    this.LineW:=[]
    this.Transp:=[]
    this.Smooth:=[]
    this.AutoScale:=[]
    this.SaveAutoScale:=[]
    this.SaveYScale:=[]
    this.GHide:=[]
    this.YScale:=[]
    this.Pen:=[]
    this.Hue:=[]
    this.ArrSize:=[]
    this.prev_ArrSize:=[]
    this.StampT:=[]
    this.LockYP:=[]
    this.LockYM:=[]
    this.Lock_hi_lim:=[]
    this.Lock_lo_lim:=[]
    this.Val_UnderGraph_Drawed:=[]
    this.UnderGX:=[]
    this.UnderGY:=[]
    this.UnderGV:=[]
    this.Sel:=1
    this.ZeroPos:=this.GPosH//3
    this.TimeStampExist:=[]
  }
  GetTime()
  {
    t1:=A_Now
    t2:=A_MSec
    while (t2<5)
    {
      t1:=A_Now
      t2:=A_MSec
    }
    return [t1,t2]
  }
  UpdateGraph()
  {
    ListLines 0
    ObjAddRef(ObjPtr(this))
    if this.GuiHiden
    {
      ObjRelease(ObjPtr(this))
      return
    }
    this.Drawing:=true
    if (this.Loaded and !this.NewData)
    {
      this.MenuForward()
      if (this.Loaded=1)
        this.Pause:=true
      this.Loaded:=0
    }
    if this.NewData
    {
      this.ReInit()
      this.Data:=this.NewData
      this.NewData:=""
    }

    DllCall("QueryPerformanceFrequency", "Int64*",&frequency:=0)
    DllCall("QueryPerformanceCounter","Int64*",&EndTime:=0)
    CycleTime:=round((EndTime-this.StartTime)/frequency*1000,1)
    DllCall("QueryPerformanceCounter", "Int64*",&StartTime:=0)
    this.StartTime:=StartTime

; Взять координаты мыши
    SaveCoordMode:=A_CoordModeMouse
    CoordMode "Mouse","Client"
    MouseGetPos(&mX,&mY,&mW,&mC,2)
    CoordMode "Mouse",SaveCoordMode
    MouseIn:=(this.GuiGraph.hwnd=mW and mY>0)
    DeltaX:=this.OldmX-mX
    DeltaY:=this.OldmY-mY

    this.Pos:=this.DrawString(0,0,0,,,,this.Font_Colour,this.Font_Back)
; очистить поле
    this.FillRectangle(this.WhiteBrush,0,0,this.GPosW,this.GPosH_)

; Добавление координаты маркеру
    if (MouseIn and getkeystate("RButton","P") and mY<this.GPosH and !this.XMarkerOnce)
    {
      this.XMarkerOnce:=true
      this.XMarker[2]:=mX
    }
    else if (this.XMarkerOnce and !getkeystate("RButton","P"))
      this.XMarkerOnce:=false
; Пауза скролинга для режима отображения по времени
    if this.Pause
    {
      this.XTOffset+=CycleTime
      this.OldXTOffset+=CycleTime
    }

; Определение массива массивов
    ArAr:=false
    if (this.Data.Length and isObject(this.Data[1])) ; and this.Data[1].Length)
    {
      if !this.Data[1].Length
      {
        this.MessagePos:=this.DrawString("Нет данных",this.GPosW//2-this.MessagePos.W//2,this.GPosH//2-this.MessagePos.H//2,,,,this.Font_Colour,this.Font_Back)
        if !this.GuiHiden
          this.Update()
        ObjRelease(ObjPtr(this))
        return
      }
      ArAr:=isObject(this.Data[1][1])
      if (!ArAr and this.Data[1].Has(2))
        ArAr:=strlen(this.Data[1][2])!=14
    }
    Ars:=ArAr?this.Data.Length:1

    loop Ars
    {
      this.Proc:=a_index
      if (this.prevArrSize.Length<this.Proc)
      {
        if (this.Proc>100)
          this.ArrName.Push("")
        this.prevArrSize.Push(1)
        this.SearchStop.Push(true)
        this.SearchSkip.Push(0)
        this.LineW.Push(1.0)
        this.Transp.Push(255)
        this.Smooth.Push(true)
        this.AutoScale.Push(true)
        this.SaveAutoScale.Push(false)
        this.GHide.Push(false)
        this.YScale.Push(1)
        this.SaveYScale.Push(1)
        this.Pen.Push("")
        this.Hue.Push(512)
        this.ArrSize.Push(0)
        this.prev_ArrSize.Push(0)
        this.LockYP.Push(0)
        this.LockYM.Push(0)
        this.Val_UnderGraph_Drawed.Push(false)
        this.UnderGX.Push(0)
        this.UnderGY.Push(0)
        this.UnderGV.Push(0)
        this.Lock_hi_lim.Push(false)
        this.Lock_lo_lim.Push(false)
        this.StampT.Push([this.CreateTime])
        this.TimeStampExist.Push(false)

        this.UpdateMenu()
      }

      if (this.Pen[this.Proc]="")
      {
        Hue:=random(0,255)
        if (this.Proc>1 and this.Hue[this.Proc-1])
        {
          loop 100
          {
            Hue:=random(0,255)
            free:=true
            for v in this.Hue
            {
              if (abs(v-Hue)<30)
              {
                free:=false
                break
              }
            }
            if free
              break
          }
        }
        this.Pen[this.Proc]:=this.AHSVToARGB(this.Transp[this.Proc],Hue,255,192)
        this.Hue[this.Proc]:=Hue
      }

      if (this.SearchSkip[this.Proc] and this.Old_XTOffset>this.XTOffset)
      {
        this.SearchStop[this.Proc]:=true
        this.SearchSkip[this.Proc]:=0
      }
    }
    this.Old_XTOffset:=this.XTOffset


    XMarker:=[false,false]
    XMarkeri:=[0,0]
    MarkerTime:=[1601,1601]
    MarkerTimeMs:=[0,0]
    XMarkerCoord:=[0,0]
    MarkerValue:=["",""]
    MarkerText:=["",""]
    MarkerDText:=""
    MarkerMin:=0
    MarkerMax:=0
    StDeviation:=""
    MarkerMean:=""
    StampPos:={X:0,Y:0,W:0,H:this.GPosH+this.MessagePos.H*2,Chars:0,Lines:0}
    prevStampPos:={X:0,Y:0,W:0,H:this.GPosH,Chars:0,Lines:0}
    Stamp:=false
    GetSizeOnce:=false
    prevCoordY:=0
    ValNum:=0
    Deviation:=0

    this.ValTipPos:={X:0,Y:0,W:0,H:0,Chars:0,Lines:0}
    PrevtmpY:=0
    loop________Start:
    loop Ars
    {
      this.Proc:=a_index
      if ArAr
        this.pData:=this.Data[this.Proc]
      else
        this.pData:=this.Data

      ArrSize:=this.pData.Length
      this.ArrSize[this.Proc]:=ArrSize

      this.TimeStampMillis:=false
      if (ArrSize and IsObject(this.pData[ArrSize]))
      {
        if this.pData[ArrSize].Has(2)
          this.TimeStampExist[this.Proc]:=strlen(this.pData[ArrSize][2])=14

        if this.pData[ArrSize].Has(3)
          this.TimeStampMillis:=this.pData[ArrSize][3]!=""
      }
; Добавление временной метки массиву без неё
      if !this.TimeStampExist[this.Proc]
      {
        if (ArrSize>this.prevArrSize[this.Proc] and ArrSize)
        {
          tmp2:=this.StampT[this.Proc][this.prevArrSize[this.Proc]]
          tmp3:=this.TimeSub([A_Now,A_MSec],tmp2)
          tmp1:=ArrSize-this.prevArrSize[this.Proc]
          tmp3:=tmp3//tmp1
          if (tmp3=0)
            tmp3:=1
          loop tmp1
          {
            add:=a_index*tmp3
            this.StampT[this.Proc].Push(this.TimeAddMs(tmp2,add))
          }
          this.prevArrSize[this.Proc]:=ArrSize
        }
      }

      Period:=this.GPosW/this.ZoomX ; количество пикселей на точку по X
      PerioT:=this.GPosW/this.ZoomT ; количество пикселей на миллисекунду по X
      this.PerioT:=PerioT
      if this.YScale[this.Proc]
        Pixel:=this.GPosH/this.YScale[this.Proc] ; сколько пикселей занимает 1 единица по Y
      else
        Pixel:=1
      ZeroPos:=this.ZeroPosU
; Пауза скролинга для режима отображения по точкам
      if (this.Pause and this.Proc=this.Sel and this.prev_ArrSize[this.Proc] and ArrSize!=this.prev_ArrSize[this.Proc])
      {
        tmp1:=ArrSize-this.prev_ArrSize[this.Proc]
        this.X_Offset+=tmp1
        this.OldX_Offset+=tmp1
      }
      this.prev_ArrSize[this.Proc]:=ArrSize

; Перемещение графика
      L_Button:
      if (MouseIn and mY<this.GPosH and getkeystate("LButton","P") and !this.Setting)
      {
        if !this.GetmCoordOnce
        {
          this.LButtonStart:=a_tickcount
          this.Old_mX:=mX
          this.Old_mY:=mY
          this.OldX_Offset:=this.X_Offset
          this.OldXTOffset:=this.XTOffset
          this.GetmCoordOnce:=true
          this.OffsetSetting:=true
          this.ShiftStrt:=true
        }
        this.X_Offset:=this.OldX_Offset-round((this.Old_mX-mX)/Period)
        this.XTOffset:=this.OldXTOffset-round((this.Old_mX-mX)/PerioT)

        DeltaY:=this.Old_mY-mY
        if (this.Sel=this.Proc)
          this.ZeroPos+=DeltaY
        if (this.Sel=this.Proc)
          this.Old_mY:=mY

        if (this.OldX_Offset-this.X_Offset!=0)
          this.MouseMoved:=true
      }
      else if (this.GetmCoordOnce and !getkeystate("LButton","P"))
      {
        this.OffsetSetting:=false
        ; Добавление координаты маркеру
        if !this.MouseMoved
          this.XMarker[1]:=mX
        this.MouseMoved:=false
        this.GetmCoordOnce:=false
      }
      if !getkeystate("LButton","P")
        this.SearchStop[this.Proc]:=false

; Выделение участка
      R_Button:
      if (MouseIn and mY<this.GPosH and getkeystate("RButton","P") and !this.Setting)
      {
        if !this.GetStartPosRectOnce
        {
          this.GetStartPosRectOnce:=true
          this.RectXs:=mX
          this.RectYs:=mY
        }
        if (this.RectXs>mX)
          this.RectX:=mX
        else
          this.RectX:=this.RectXs
        if (this.RectYs>mY)
          this.RectY:=mY
        else
          this.RectY:=this.RectYs
        this.RectW:=abs(this.RectXs-mX)
        this.RectH:=abs(this.RectYs-mY)
        if (this.RectW>10 and this.RectH>10)
          this.DrawRectangle(this.Font_Colour,this.RectX,this.RectY,this.RectW,this.RectH)
        else
          this.DrawRectangle(this.GrayPen2,this.RectX,this.RectY,this.RectW,this.RectH)
      }
      else if (this.GetStartPosRectOnce and !getkeystate("RButton","P"))
      {
        this.GetStartPosRectOnce:=false
        if (this.RectW>10 and this.RectH>10)
        {
          this.RectZoom:=true
          this.SaveZoomX:=this.ZoomX
          this.SaveZoomT:=this.ZoomT
          this.SaveX_Offset:=this.X_Offset
          this.SaveXTOffset:=this.XTOffset
          this.SavePause:=this.Pause
          this.Pause:=true
          this.ShiftStrt:=true
          loop Ars
          {
            this.SaveAutoScale[a_index]:=this.AutoScale[a_index]
            this.SaveZeroPos:=this.ZeroPos
            this.SaveYScale[a_index]:=this.YScale[a_index]
            if this.AutoScale[a_index]
            {
              this.AutoScale[a_index]:=false
            }
            P:=this.GPosH/this.YScale[a_index] ; сколько пикселей занимает 1 единица по Y
            this.YScale[a_index]:=this.RectH/P
          }
          this.UpdateMenu()
          U:=this.SaveYScale[this.Sel]/this.GPosH ; сколько единиц занимает 1 пиксель по Y
          P:=this.GPosH/this.YScale[this.Sel] ; сколько пикселей занимает 1 единица по Y
          RectY2:=(this.RectY+this.RectH)
          this.ZeroPos-=((this.ZeroPosU-RectY2)*U)*P+(this.GPosH-this.ZeroPosU)
          this.ZeroPos:=round(this.ZeroPos)
          if !this.ScaleXByTime
          {
            this.ZoomX:=round(this.RectW/Period)+1
            this.X_Offset+=round((this.GPosW-(this.RectX+this.RectW))/Period)
          }
          else
          {
            this.ZoomT:=round(this.RectW/PerioT)
            this.XTOffset+=round((this.GPosW-(this.RectX+this.RectW))/PerioT)
          }
        }
        else if this.RectZoom
        {
          this.RectZoom:=false
          this.ZoomX:=this.SaveZoomX
          this.ZoomT:=this.SaveZoomT
          this.X_Offset:=this.SaveX_Offset
          this.XTOffset:=this.SaveXTOffset
          this.Pause:=this.SavePause
          this.ShiftStrt:=true
          this.ZeroPos:=this.SaveZeroPos
          loop Ars
          {
            this.YScale[a_index]:=this.SaveYScale[a_index]
            if ((this.SaveAutoScale[a_index] and !this.AutoScale[a_index]) or (!this.SaveAutoScale[a_index] and this.AutoScale[a_index]))
            {
              this.AutoScale[a_index]:=true
              this.UpdateMenu()
            }
          }
        }
      }

; Обработать колесо мыши
      Whell:
      if (MouseIn and (Graph.WheelUp or Graph.WheelDown) and Graph.HotKeymW=this.GuiGraph.Hwnd and this.Proc=1)
      {
        CoefHi:=mY/this.GPosH
        CoefLo:=(this.GPosH-mY)/this.GPosH
        tmp1:=round(this.ZoomX/3)+1
        tmt1:=round(this.ZoomT/3)+1
        if Graph.WheelUp
        {
          this.ZoomX-=tmp1
          this.ZoomT-=tmt1
        }
        else if Graph.WheelDown
        {
          this.ZoomX+=tmp1
          this.ZoomT+=tmt1
        }
        Graph.WheelUp:=false
        Graph.WheelDown:=false
        if (this.ZoomX<10)
          this.ZoomX:=10
        if (this.ZoomX>100000)
          this.ZoomX:=100000
        if (this.ZoomT<1000)
          this.ZoomT:=1000
        if (this.ZoomT>Graph.MaxTime)
          this.ZoomT:=Graph.MaxTime
        this.ShiftStrt:=true
        this.Pause:=true
        tmp1:=(this.GPosW-mX)/Period
        tmp2:=(this.GPosW-mX)/(this.GPosW/this.ZoomX)
        this.X_Offset+=round(tmp1-tmp2)
        tmp1:=(this.GPosW-mX)/PerioT
        tmp2:=(this.GPosW-mX)/(this.GPosW/this.ZoomT)
        this.XTOffset+=round(tmp1-tmp2)
      }

; ограничить перемещение графика
      if (this.Proc=this.Sel)
      {
        if !this.ScaleXByTime
        {
          tmp1:=this.ZoomX//2
          if (this.X_Offset<-tmp1)
            this.X_Offset:=-tmp1
          else if (this.X_Offset>0 and this.X_Offset>this.ArrSize[this.Proc]-tmp1)
            this.X_Offset:=this.ArrSize[this.Proc]-tmp1
        }
        else
        {
          tmp1:=this.ZoomT//2
          if this.TimeStampExist[this.Proc]
          {
            if this.TimeStampMillis
            {
              tmp2:=this.TimeSub([A_Now,A_MSec],[this.pData[this.pData.Length][2],this.pData[this.pData.Length][3]])
              tmp3:=this.TimeSub([A_Now,A_MSec],[this.pData[1][2],this.pData[1][3]])
            }
            else
            {
              tmp2:=this.TimeSub([A_Now,A_MSec],[this.pData[this.pData.Length][2],0])
              tmp3:=this.TimeSub([A_Now,A_MSec],[this.pData[1][2],0])
            }
          }
          else
          {
            tmp2:=this.TimeSub([A_Now,A_MSec],[this.StampT[this.Proc][this.StampT[this.Proc].Length][1],this.StampT[this.Proc][this.StampT[this.Proc].Length][2]])
            tmp3:=this.TimeSub([A_Now,A_MSec],[this.StampT[this.Proc][1][1],this.StampT[this.Proc][1][2]])
          }
          tmp2-=tmp1
          tmp3-=tmp1
          if (this.XTOffset<-tmp1)
          {
            this.XTOffset:=-tmp1
            this.ShiftStrt:=true
          }
          else if (this.XTOffset<tmp2)
          {
            this.XTOffset:=tmp2
            this.ShiftStrt:=true
          }
          else if (this.XTOffset>0 and this.XTOffset>tmp3)
          {
            this.XTOffset:=tmp3
            this.ShiftStrt:=true
          }
;          this.XTOffset:=Integer(this.XTOffset)
        }
      }

; отрисовать сетку
      if (this.Sel=this.Proc)
      {
        tmpc:=this.AHSVToARGB(200,this.Hue[this.Proc],this.Hue[this.Proc]>255?0:100,this.Hue[this.Proc]>255?(this.Hue[this.Proc]>>8):255)
        LineH:=this.Pos.H*2
        RoundTo:=1
        GoAgain:
        Drawed:=0
        if (ZeroPos<=this.GPosH+1 and ZeroPos>=0)
        {
          loop ZeroPos//LineH
          {
            tmp1:=a_index*LineH
            tmp2:=tmp1/Pixel
            Val:=round(tmp2,-(strlen(round(tmp2))-RoundTo))
            if (Val=0)
              continue
            CoordY:=ZeroPos-Val*Pixel
            if (CoordY<0)
              break
            this.DrawString(" " Val,0,CoordY,,,,this.Font_Colour,tmpc)
            this.DrawLine(this.GrayPen2,0,CoordY,this.GPosW,CoordY)
          }
          loop (this.GPosH-ZeroPos)//LineH
          {
            tmp1:=a_index*LineH
            tmp2:=tmp1/Pixel
            Val:=round(tmp2,-(strlen(round(tmp2))-RoundTo))
            if (Val=0)
              continue
            CoordY:=ZeroPos+Val*Pixel
            if (CoordY>this.GPosH)
              break
            this.DrawString("-" Val,0,CoordY-this.Pos.H,,,,this.Font_Colour,tmpc)
            this.DrawLine(this.GrayPen2,0,CoordY,this.GPosW,CoordY)
          }
        }
        else if (ZeroPos>this.GPosH)
        {
          loop this.GPosH//LineH
          {
            tmp1:=ZeroPos-a_index*LineH
            tmp2:=tmp1/Pixel
            Val:=round(tmp2,-(strlen(round(tmp2))-RoundTo))
            CoordY:=ZeroPos-Val*Pixel
            if (prevCoordY!=CoordY and CoordY<=this.GPosH-this.Pos.H and CoordY>=0)
            {
              this.DrawString(" " Val,0,CoordY,,,,this.Font_Colour,tmpc)
              this.DrawLine(this.GrayPen2,0,CoordY,this.GPosW,CoordY)
              Drawed++
            }
            prevCoordY:=CoordY
          }
          if (Drawed<1)
          {
            RoundTo++
            if (RoundTo<10)
              goto GoAgain
            tmp1:=ZeroPos-this.GPosH/2
            tmp2:=tmp1/Pixel
            Val:=round(tmp2)
            CoordY:=ZeroPos-Val*Pixel
            this.DrawString(" " Val,0,CoordY,,,,this.Font_Colour,tmpc)
            this.DrawLine(this.GrayPen2,0,CoordY,this.GPosW,CoordY)
          }
        }
        else if (ZeroPos<0)
        {
          loop this.GPosH//LineH
          {
            tmp1:=-ZeroPos+a_index*LineH
            tmp2:=tmp1/Pixel
            Val:=round(tmp2,-(strlen(round(tmp2))-RoundTo))
            CoordY:=ZeroPos+Val*Pixel
            if (prevCoordY!=CoordY and CoordY<=this.GPosH+1 and CoordY>=this.Pos.H)
            {
              this.DrawString("-" Val,0,CoordY-this.Pos.H,,,,this.Font_Colour,tmpc)
              this.DrawLine(this.GrayPen2,0,CoordY,this.GPosW,CoordY)
              Drawed++
            }
            prevCoordY:=CoordY
          }
          if (Drawed<1)
          {
            RoundTo++
            if (RoundTo<10)
              goto GoAgain
            tmp1:=-ZeroPos+this.GPosH/2
            tmp2:=tmp1/Pixel
            Val:=round(tmp2)
            CoordY:=ZeroPos+Val*Pixel
            this.DrawString("-" Val,0,CoordY-this.Pos.H,,,,this.Font_Colour,tmpc)
            this.DrawLine(this.GrayPen2,0,CoordY,this.GPosW,CoordY)
          }
        }
; отрисовать линию и текст нуля
        if (ZeroPos<=this.GPosH+1 and ZeroPos>=0)
        {
          this.DrawLine(this.GrayPen,0,ZeroPos,this.GPosW,ZeroPos)
          tmp0:=ZeroPos+this.Pos.H//2
          tmp1:=(this.GPosH>tmp0) ? ZeroPos-this.Pos.H//2 : this.GPosH-this.Pos.H
          this.DrawString("0",0,tmp1,,,,this.Font_Colour,tmpc)
        }
      }
; сетка отрисована

      if (!ArrSize and this.Proc=this.Sel)
        this.MessagePos:=this.DrawString("Нет данных",this.GPosW//2-this.MessagePos.W//2,this.GPosH//2-this.MessagePos.H//2,,,,this.Font_Colour,this.Font_Back)
      if (this.SearchSkip[this.Proc]>0)
        this.SearchSkip[this.Proc]--

; Отрисовка графика ----------------------------------------------------------------
      hi_lim:=Graph.min
      lo_lim:=Graph.max
;      Oldi:=1
      NotScaleXByTime:
      this.LockYP[this.Proc]:=0
      this.LockYM[this.Proc]:=0
      StampLine:=this.StampPos.W/2
      this.Val_UnderGraph_Drawed[this.Proc]:=false
      if (!this.ScaleXByTime and ArrSize)
      {
        Old_CoordY:=ZeroPos
        Old_CoordX:=this.GPosW+10
        GPoints:=[[Old_CoordX,Old_CoordY]]
        Aindex:=0
        Oldi:=ArrSize
        if !this.GHide[this.Proc]
        loop this.GPosW
        {
          iLast:=a_index=this.GPosW
          i:=Floor(ArrSize-(a_index/Period+1))-this.X_Offset
          if (i>ArrSize and !iLast)
            continue
          if (i<1)
            i:=1
          if iLast
          {
            if (i-1>0)
              i--
            else
              i:=Oldi
            if (i+1>ArrSize)
              continue
          }
          Aindex++
          if (Aindex=1 and i+1<=ArrSize and i>0)
          {
            if !this.TimeStampExist[this.Proc]
              Value:=this.pData[i]
            else
              Value:=this.pData[i][1]
            tmp1:=Value?Value:0
            Value:=Value!=""?Value:"Null"
            if (tmp1!="")
            {
              hi_lim:=tmp1>hi_lim ? tmp1 : hi_lim
              lo_lim:=tmp1<lo_lim ? tmp1 : lo_lim
            }
            CoordY:=ZeroPos-tmp1*Pixel
            if (CoordY<-1)
              CoordY:=0
            else if (CoordY>this.GPosH+1)
              CoordY:=this.GPosH
            GPoints.Push([this.GPosW,CoordY])
          }
          if !this.TimeStampExist[this.Proc]
            Value:=this.pData[i]
          else
            Value:=this.pData[i][1]
          tmp1:=Value?Value:0
          Value:=Value!=""?Value:"Null"
          if (tmp1!="")
          {
            hi_lim:=tmp1>hi_lim ? tmp1 : hi_lim
            lo_lim:=tmp1<lo_lim ? tmp1 : lo_lim
          }
          CoordX:=this.GPosW-a_index
          CoordY:=ZeroPos-tmp1*Pixel
          SkipLine:=false
          if (CoordY<-1)
          {
            CoordY:=0
            SkipLine:=true
            this.LockYP[this.Proc]++
          }
          else if (CoordY>this.GPosH+1)
          {
            CoordY:=this.GPosH
            SkipLine:=true
            this.LockYM[this.Proc]++
          }
          NewStamp:=false
          NewXMarker:=false
          if (Oldi!=i)
          {
            ; линия графика
            if !this.Smooth[this.Proc]
            {
              GPoints.Push([Old_CoordX,CoordY])
              Old_CoordY:=CoordY
            }
            GPoints.Push([CoordX,CoordY])
            ; Отрисовка подробностей при малом увеличении
            if (Oldi-i>1 and !this.TimeOut)
            {
              max:=Graph.min
              min:=Graph.max
              loop Oldi-i+1
              {
                j:=i+a_index-1
                if (j<1 or j>ArrSize)
                  continue
                if !this.TimeStampExist[this.Proc]
                  tmp1:=this.pData[j]
                else
                  tmp1:=this.pData[j][1]
                min:=min<tmp1?min:tmp1
                max:=max>tmp1?max:tmp1
                if (tmp1!="")
                {
                  hi_lim:=tmp1>hi_lim ? tmp1 : hi_lim
                  lo_lim:=tmp1<lo_lim ? tmp1 : lo_lim
                }
              }
              min:=ZeroPos-min*Pixel
              max:=ZeroPos-max*Pixel
              if (min<0)
                min:=1
              if (min>this.GPosH)
                min:=this.GPosH
              if (max<0)
                max:=0
              if (max>this.GPosH)
                max:=this.GPosH-1
              GPoints.Push([CoordX,min])
              GPoints.Push([CoordX,max])
              GPoints.Push([CoordX,CoordY])
            }
            ; Дорисовать точки при большом увеличении
            if (Old_CoordX-CoordX>3)
              this.DrawEllipse(this.Pen[this.Proc],CoordX-1,CoordY-1,2,2,this.LineW[this.Proc])

            ; текст значения под курсором
            if (!this.Val_UnderGraph_Drawed[this.Proc] and MouseIn)
            if (mX>=CoordX and mX<=CoordX+20)
            if (mY>CoordY and mY<CoordY+20)
            {
              this.Val_UnderGraph_Drawed[this.Proc]:=true
              this.UnderGX[this.Proc]:=CoordX
              this.UnderGY[this.Proc]:=CoordY
              this.UnderGV[this.Proc]:=Value
            }
            Old_CoordX:=CoordX
            Old_CoordY:=CoordY
            Oldi:=i
            NewStamp:=true
            if (this.Sel=this.Proc)
              NewXMarker:=true
          }
          ; линия и текст временой шкалы
          if (this.Sel=this.Proc)
          {
            if Period>=1
              StampLine++
            else
              StampLine+=1
            tmp1:=this.StampPos.W+2
            if (NewStamp and StampLine>tmp1) or (!Stamp and a_index=this.GPosW)
            {
              StampLine:=0
              Stamp:=true
              this.DrawLine(this.GrayPen2,CoordX,0,CoordX,this.GPosH)
              if !this.TimeStampExist[this.Proc]
              {
                tmp10:=FormatTime(this.StampT[this.Proc][i][1],"HH:mm:ss")
                tmp10.="`n" format("{:03}",this.StampT[this.Proc][i][2]) "ms"
              }
              else
              {
                tmp10:=FormatTime(this.pData[i][2],"dd.MM.yyyy'`n'HH:mm:ss")
                if (this.TimeStampMillis and this.pData[i][3]!="")
                  tmp10.="." format("{:03}",this.pData[i][3])
              }
              StampPos:=this.DrawString(tmp10,CoordX-this.StampPos.W/2,this.GPosH,,,,this.Font_Colour,this.GrayPen2)
              if !GetSizeOnce
              {
                GetSizeOnce:=true
                this.StampPos:=StampPos
                this.StampOffset:=this.StampPos.H
              }
              if (prevStampPos.w)
              {
                if (this.StampPos.X+this.StampPos.w>prevStampPos.X)
                  this.StampsNum--
                if (this.StampPos.X+this.StampPos.w*2+5<prevStampPos.X)
                  this.StampsNum++
                if this.StampsNum<2
                  this.StampsNum:=2
              }
              prevStampPos:=this.StampPos
            }
            ; маркеры
            tmp1:=this.GPosW-a_index
            loop 2
            {
              if (NewXMarker and !XMarker[a_index] and tmp1<this.XMarker[a_index])
              {
                XMarker[a_index]:=true
                XMarkeri[a_index]:=i
                XMarkerCoord[a_index]:=tmp1
              }
            }
            NewXMarker:=false
          }
        }
        ; рисовать линию графика
        if !this.GHide[this.Proc]
          this.DrawLines(this.Pen[this.Proc],GPoints,this.LineW[this.Proc])
        ; блокировать перемещение по Y
        this.LockYP[this.Proc]:=(this.LockYP[this.Proc]>=Aindex-3)?1:0
        this.LockYM[this.Proc]:=(this.LockYM[this.Proc]>=Aindex-3)?1:0
      }
      ScaleXByTime:
      if (this.ScaleXByTime and ArrSize)
      {
        Strt:=this.TimeSubMs([A_Now,A_MSec],this.XTOffset)
        if (this.Pause and (!this.SaveStrt[1] or this.OffsetSetting or this.ShiftStrt))
        {
          this.SaveStrt:=Strt
          this.ShiftStrt:=false
        }
        else if (!this.Pause and this.SaveStrt[1])
          this.SaveStrt:=[0,0]
        if this.Pause
          Strt:=this.SaveStrt
        Endt:=this.TimeSubMs(Strt,this.ZoomT)
        ; линия и текст временой шкалы
        if (this.Sel=this.Proc)
        {
          loop this.GPosW
          {
            CoordX:=this.GPosW-(a_index-1)
            StampLine++
            tmp1:=this.StampPos.W+2
            if (StampLine>tmp1) or (!Stamp and a_index=this.GPosW)
            {
              Stamp:=true
              StampLine:=0
              this.DrawLine(this.GrayPen2,CoordX,0,CoordX,this.GPosH)
              tmp2:=(this.ZoomT/this.GPosW)*(a_index-1)
              tmp1:=this.TimeSubMs(Strt,tmp2)
              tmp10:=FormatTime(tmp1[1],"dd.MM.yyyy'`n'HH:mm:ss")
              tmp10.="." tmp1[2]
              StampPos:=this.DrawString(tmp10,CoordX-this.StampPos.W/2,this.GPosH,,,,this.Font_Colour,this.GrayPen2)
              if !GetSizeOnce
              {
                GetSizeOnce:=true
                this.StampPos:=StampPos
                this.StampOffset:=this.StampPos.H
              }
              if (prevStampPos.w)
              {
                if (this.StampPos.X+this.StampPos.w>prevStampPos.X)
                  this.StampsNum--
                if (this.StampPos.X+this.StampPos.w*2+5<prevStampPos.X)
                  this.StampsNum++
                if this.StampsNum<2
                  this.StampsNum:=2
              }
              prevStampPos:=this.StampPos
            }
          }
        }
        ; найти первую точку
        iStart:=1
        Same:=0
        if !this.GHide[this.Proc]
        loop ArrSize
        {
          i:=ArrSize-(a_index-1)-this.SearchSkip[this.Proc]
          if (i<1)
          {
            iStart:=1
            break
          }
          if !this.TimeStampExist[this.Proc]
          {
            iTime:=this.StampT[this.Proc][i][1]
            iTimeMs:=this.StampT[this.Proc][i][2]
            if (iTime>Strt[1] or (iTime=Strt[1] and iTimeMs>=Strt[2]))
              continue
            else
            {
              iStart:=i=ArrSize?i:i+1
              break
            }
          }
          else
          {
            iTime:=this.pData[i][2]
            if this.TimeStampMillis
              iTimeMs:=this.pData[i][3]
            else
            {
              if !Same
              {
                iTimeMs:=0
                loop 1000
                {
                  j:=1+i-a_index
                  if (j>0 and iTime=this.pData[j][2])
                    Same++
                  else
                    break
                }
                SameT:=1000/(Same+1)
              }
              if (Same>0)
              {
                iTimeMs:=round(SameT*Same)
                Same--
              }
            }
            if (iTime>Strt[1] or (this.TimeStampMillis and iTime=Strt[1] and iTimeMs>=Strt[2]))
              continue
            else
            {
              iStart:=i=ArrSize?i:i+1
              break
            }
          }
        }
        Old_CoordY:=ZeroPos
        Old_CoordX:=this.GPosW+10
        GPoints:=[[Old_CoordX,Old_CoordY]]
        SearchSkip:=this.SearchSkip[this.Proc]?this.SearchSkip[this.Proc]:0
        Same:=0
        Aindex:=0
        Oldi:=iStart
        iLast:=false
        if !this.GHide[this.Proc]
        while !iLast ;loop iStart
        {
          i:=iStart-(a_index-1)
          if (i<1)
          {
            i:=1
            iLast:=true
          }
          if !this.TimeStampExist[this.Proc]
          {
            iTime:=this.StampT[this.Proc][i][1]
            iTimeMs:=this.StampT[this.Proc][i][2]
          }
          else
          {
            iTime:=this.pData[i][2]
            if this.TimeStampMillis
              iTimeMs:=this.pData[i][3]
            else
            {
              if !Same
              {
                iTimeMs:=0
                loop 1000
                {
                  j:=i-a_index
                  if (j>0 and iTime=this.pData[j][2])
                    Same++
                  else
                    break
                }
                SameT:=1000/(Same+1)
              }
              if (Same>0)
              {
                iTimeMs:=round(SameT*Same)
                Same--
              }
            }
          }
          if (iTime<Endt[1])
            iLast:=true
          mms:=Strt[2]-iTimeMs
          if (mms<0)
          {
            mms+=1000
            iTime:=DateAdd(iTime,1,"Seconds")
          }
          ttt:=DateDiff(Strt[1],iTime,"Seconds")
          CoordX:=this.GPosW-round(PerioT*(ttt*1000+mms))
          Aindex++
          if (Aindex=1 and !this.SearchStop[this.Proc])
          {
            tmp1:=ArrSize-i
            if (SearchSkip<tmp1)
              SearchSkip:=tmp1
          }
          if (CoordX!=Old_CoordX or iLast)
          {
            if !this.TimeStampExist[this.Proc]
              Value:=this.pData[i]
            else
              Value:=this.pData[i][1]
            tmp1:=Value?Value:0
            if (Value!="" and Aindex)
            {
              hi_lim:=Value>hi_lim ? Value : hi_lim
              lo_lim:=Value<lo_lim ? Value : lo_lim
            }
            Value:=Value!=""?Value:"Null"
            CoordY:=ZeroPos-tmp1*Pixel
            SkipLine:=false
            if (CoordY<-1)
            {
              CoordY:=0
              SkipLine:=true
              this.LockYP[this.Proc]++
            }
            else if (CoordY>this.GPosH+1)
            {
              CoordY:=this.GPosH
              SkipLine:=true
              this.LockYM[this.Proc]++
            }
            ; линия графика
            if !this.Smooth[this.Proc]
            {
              GPoints.Push([Old_CoordX,CoordY])
              Old_CoordY:=CoordY
            }
            GPoints.Push([CoordX,CoordY])
            ; Отрисовка подробностей при малом увеличении
            if (Oldi-i>1 and !this.TimeOut)
            {
              max:=Graph.min
              min:=Graph.max
              loop Oldi-i
              {
                j:=i+a_index-1
                if (j<1 or j>ArrSize)
                  continue
                if !this.TimeStampExist[this.Proc]
                  tmp1:=this.pData[j]
                else
                  tmp1:=this.pData[j][1]
                min:=min<tmp1?min:tmp1
                max:=max>tmp1?max:tmp1
                if (tmp1!="")
                {
                  hi_lim:=tmp1>hi_lim ? tmp1 : hi_lim
                  lo_lim:=tmp1<lo_lim ? tmp1 : lo_lim
                }
              }
              min:=ZeroPos-min*Pixel
              max:=ZeroPos-max*Pixel
              if (min<0)
                min:=1
              if (min>this.GPosH)
                min:=this.GPosH
              if (max<0)
                max:=0
              if (max>this.GPosH)
                max:=this.GPosH-1
              GPoints.Push([CoordX,min])
              GPoints.Push([CoordX,max])
              GPoints.Push([CoordX,CoordY])
            }
            ; Дорисовать точки при большом увеличении
            if (Old_CoordX-CoordX>3)
              this.DrawEllipse(this.Pen[this.Proc],CoordX-1,CoordY-1,2,2,this.LineW[this.Proc])

            ; текст значения под курсором
            if (!this.Val_UnderGraph_Drawed[this.Proc] and MouseIn)
            if (mX>=CoordX and mX<=CoordX+20)
            if (mY>CoordY and mY<CoordY+20)
            {
              this.Val_UnderGraph_Drawed[this.Proc]:=true
              this.UnderGX[this.Proc]:=CoordX
              this.UnderGY[this.Proc]:=CoordY
              this.UnderGV[this.Proc]:=Value
            }
            Old_CoordX:=CoordX
            Old_CoordY:=CoordY
            Oldi:=i
            if iLast
              break
            ; маркеры
            if (this.Sel=this.Proc)
            {
              loop 2
              {
                if (!XMarker[a_index] and CoordX<this.XMarker[a_index])
                {
                  XMarker[a_index]:=true
                  XMarkeri[a_index]:=i
                  if !this.TimeStampMillis
                    MarkerTimeMs[a_index]:=format("{:03}",iTimeMs)
                  XMarkerCoord[a_index]:=CoordX
                }
              }
            }
          }
        }
        ; рисовать линию графика
        if !this.GHide[this.Proc]
          this.DrawLines(this.Pen[this.Proc],GPoints,this.LineW[this.Proc])
        this.ZoomX:=Aindex?Aindex:1
        this.SearchSkip[this.Proc]:=SearchSkip

        ; блокировать перемещение по Y
        this.LockYP[this.Proc]:=(this.LockYP[this.Proc]>=Aindex-3)?1:0
        this.LockYM[this.Proc]:=(this.LockYM[this.Proc]>=Aindex-3)?1:0
      }
      ; Автомасштаб
      if (this.AutoScale[this.Proc] and !this.GHide[this.Proc] and (!this.OScale or this.Proc=this.Sel))
      {
        tmp1:=this.GPosH//2
        if (this.ZeroPosU>tmp1 and this.ZeroPosU<this.GPosH+1)
        {
          if (hi_lim>0)
            this.YScale[this.Proc]:=abs(hi_lim)+(this.GPosH-this.ZeroPosU+10)/Pixel
          else
            this.YScale[this.Proc]:=-lo_lim+(this.ZeroPosU+10)/Pixel
        }
        else if (this.ZeroPosU<=tmp1 and this.ZeroPosU>0)
        {
          if (lo_lim<0)
            this.YScale[this.Proc]:=-lo_lim+(this.ZeroPosU+10)/Pixel
          else
            this.YScale[this.Proc]:=abs(hi_lim)+(this.GPosH-this.ZeroPosU+10)/Pixel
        }
        if (this.YScale[this.Proc]=0)
          this.YScale[this.Proc]:=1
      }
      this.DrawLine(this.GrayPen2,0,this.GPosH,this.GPosW,this.GPosH)
      this.DrawLine(this.GrayPen2,0,this.GPosH+StampPos.H,this.GPosW,this.GPosH+StampPos.H)

      ; текст значения под курсором
      if this.Val_UnderGraph_Drawed[this.Proc]
      {
        this.DrawEllipse(0xffffffff,this.UnderGX[this.Proc]-3,this.UnderGY[this.Proc]-3,6,6,this.LineW[this.Proc])
        this.DrawEllipse(0xff000000,this.UnderGX[this.Proc]-2,this.UnderGY[this.Proc]-2,4,4,this.LineW[this.Proc])
        this.DrawEllipse(0xffffffff,this.UnderGX[this.Proc]-1,this.UnderGY[this.Proc]-1,2,2,this.LineW[this.Proc])
        tmpX:=this.UnderGX[this.Proc]+(5<this.GPosW-this.ValTipPos.W)?this.UnderGX[this.Proc]+5:this.GPosW-this.ValTipPos.W
        tmpY:=(this.UnderGY[this.Proc]-this.Pos.H<0)?0:(this.UnderGY[this.Proc]-this.Pos.H-5)
        if (tmpY-PrevtmpY<this.ValTipPos.H)
        {
          if (mY>this.GPosH//2)
            tmpY:=PrevtmpY-this.ValTipPos.H
          else
          {
            tmpY:=PrevtmpY+this.ValTipPos.H
            tmpX+=8
          }
        }
        tmpC:=this.AHSVToARGB(200,this.Hue[this.Proc],this.Hue[this.Proc]>255?0:100,this.Hue[this.Proc]>255?(this.Hue[this.Proc]>>8):255)
        this.ValTipPos:=this.DrawString((IsInteger(this.UnderGV[this.Proc])?this.UnderGV[this.Proc]:Format("{:0." this.FloatFormat "f}",this.UnderGV[this.Proc])) " " this.ArrName[this.Proc],tmpX,tmpY,,,,this.Font_Colour,tmpC)
        PrevtmpY:=tmpY
      }
      if (this.ShowMarkers and this.Sel=this.Proc)
      {
        if (XMarkerCoord[1] or XMarkerCoord[2])
        {
          MarkerText[1]:=(this.ArrName[this.Proc]!=""?this.ArrName[this.Proc]:"Линия №" this.Proc) " Точек = " abs(XMarkeri[1]-XMarkeri[2])+1 "`nВремя        "
          if this.TimeStampExist[this.Proc]
            loop 11
              MarkerText[1].=" "
          MarkerText[1].="Значение`n"
        }
        loop 2
        {
          if XMarkerCoord[a_index]
          {
            if !this.TimeStampExist[this.Proc]
            {
              MarkerTime[a_index]:=this.StampT[this.Proc][XMarkeri[a_index]][1]
              MarkerTimeMs[a_index]:=this.StampT[this.Proc][XMarkeri[a_index]][2]
              tmp10:=FormatTime(MarkerTime[a_index],"HH:mm:ss") "." MarkerTimeMs[a_index]
              MarkerValue[a_index]:=this.pData[XMarkeri[a_index]]
              MarkerText[a_index].=tmp10 " " Format("{:0." this.FloatFormat "f}",MarkerValue[a_index])
            }
            else
            {
              MarkerTime[a_index]:=this.pData[XMarkeri[a_index]][2]
              if (this.TimeStampMillis and this.pData[XMarkeri[a_index]][3]!="")
                MarkerTimeMs[a_index]:=this.pData[XMarkeri[a_index]][3]
              tmp10:=FormatTime(MarkerTime[a_index],"dd.MM.yyyy HH:mm:ss") "." MarkerTimeMs[a_index]
              MarkerValue[a_index]:=this.pData[XMarkeri[a_index]][1]
              MarkerText[a_index].=tmp10 " " Format("{:0." this.FloatFormat "f}",this.pData[XMarkeri[a_index]][1])
            }
          }
        }
        if (XMarkerCoord[1] and XMarkerCoord[2])
        {
          i:=1, j:=2
          if (XMarkerCoord[1]<XMarkerCoord[2])
            i:=2, j:=1
          ms:=MarkerTimeMs[i]-MarkerTimeMs[j]
          if (ms<0)
            MarkerTime[j]:=DateAdd(MarkerTime[j],1,"Seconds")
          MarkerDeltaT:=DateDiff(MarkerTime[i],MarkerTime[j],"Seconds")
          MarkerDeltaT:=DateAdd(1601,MarkerDeltaT,"Seconds")
          Y:=FormatTime(MarkerDeltaT,"yyyy")
          M:=FormatTime(MarkerDeltaT,"MM")
          D:=FormatTime(MarkerDeltaT,"dd")
          Y:=format("{:04}",Y-1601)
          M:=format("{:02}",M-1)
          D:=format("{:02}",D-1)
          if this.TimeStampExist[this.Proc]
            MarkerDText.=D "." M "." Y " "
          tmp10:=FormatTime(MarkerDeltaT,"HH:mm:ss")
          MarkerDText.=tmp10
          if MarkerTimeMs[i]
            MarkerDText.="." format("{:03}",abs(ms))

          MarkerMax:=Graph.min
          MarkerMin:=Graph.max
          ValNum:=XMarkeri[i]-XMarkeri[j]+1
          Summ:=0
          loop ValNum
          {
            if !this.TimeStampExist[this.Proc]
              Val:=this.pData[XMarkeri[j]+A_Index-1]
            else
              Val:=this.pData[XMarkeri[j]+A_Index-1][1]
            Summ+=Val
            if (Val>MarkerMax)
              MarkerMax:=Val
            if (Val<MarkerMin)
              MarkerMin:=Val
          }
          MarkerMean:=Summ/ValNum
          loop ValNum
          {
            if !this.TimeStampExist[this.Proc]
              Val:=this.pData[XMarkeri[j]+A_Index-1]
            else
              Val:=this.pData[XMarkeri[j]+A_Index-1][1]
            Deviation+=(Val-MarkerMean)**2
          }
        }
      }
    } ; Конец цикла
    Loop________End:

; общий масштаб
    if this.OScale
    {
      loop this.YScale.Length
      {
        if (a_index=this.Sel)
          continue
        this.YScale[a_index]:=this.YScale[this.Sel]
      }
    }

; Отрисовать меню
    OnGraphMenu:
    SelPos:=[]
    tmpc:=this.AHSVToARGB(200,this.Hue[1],this.Hue[1]>255?0:100,this.Hue[1]>255?(this.Hue[1]>>8):255)
    ; первая строка (этой строки может не быть)
    MenuSPY:=StampPos.Y+StampPos.H
    if (Ars>1)
      Pos:=this.DrawString("1",0,MenuSPY,,,"Center vCenter",this.GHide[1]?tmpc:this.Font_Colour,tmpc)
    else
      Pos:={X:0,Y:0,W:0,H:0,Chars:0,Lines:0}
    SelPos.Push(Pos)
    loop Ars
    {
      tmp1:=a_index+1
      if (tmp1<=Ars)
      {
        tmpc:=this.AHSVToARGB(200,this.Hue[tmp1],this.Hue[tmp1]>255?0:100,this.Hue[tmp1]>255?(this.Hue[tmp1]>>8):255)
        Pos:=this.DrawString(tmp1,SelPos[a_index].X+SelPos[a_index].W+5,MenuSPY,,,"Center vCenter",this.GHide[tmp1]?tmpc:this.Font_Colour,tmpc)
      }
      else
        Pos:=this.DrawString("Выбран " this.Sel,SelPos[a_index].X+SelPos[a_index].W+5,MenuSPY,,,"Center vCenter",this.Font_Colour,this.LightBlueBrush)
      SelPos.Push(Pos)
    }
    ; вторая строка (первая)
    MenuSPY:=StampPos.Y+StampPos.H+SelPos[1].H
    if !this.ScaleXByTime
      this.ZoomPos:=this.DrawString("Zoom X = " this.ZoomX " точек",0,MenuSPY,,,"",this.Font_Colour,this.LightBlueBrush)
    else
    {
      tmp1:=this.MsToTime(this.ZoomT)
      tmp1[1]-=1000000
      tmp2:=FormatTime(tmp1[1],"dd HH:mm:ss")
      tmp2.="." tmp1[2]
      this.ZoomPos:=this.DrawString("Zoom X = " tmp2,0,MenuSPY,,,"",this.Font_Colour,this.LightBlueBrush)
    }
    this.SmoothPos:=this.DrawString(this.Smooth[this.Sel]?"~~~~":"_-_-",this.ZoomPos.W+this.Pos.W,MenuSPY,,,"",this.Font_Colour,this.LightBlueBrush)
    this.TransPos:=this.DrawString("<" this.Transp[this.Sel] ">",this.SmoothPos.X+this.SmoothPos.W+this.Pos.W,MenuSPY,,,"",this.Font_Colour,this.LightBlueBrush)
    this.LineWPos:=this.DrawString("[" this.LineW[this.Sel] "]",this.TransPos.X+this.TransPos.W+this.Pos.W,MenuSPY,,,"",this.Font_Colour,this.LightBlueBrush)
    this.FontSizePos:=this.DrawString("Шрифт " this.FontSize,this.LineWPos.X+this.LineWPos.W+this.Pos.W,MenuSPY,,,"",this.Font_Colour,this.LightBlueBrush)
    ; третья строка (вторая)
    MenuSPY+=this.ZoomPos.H
    this.AutoPos:=this.DrawString("Авто",0,MenuSPY,,,"",this.AutoScale[this.Sel]?this.Font_Colour:this.GrayPen,this.LightBlueBrush)
    this.ScalePos:=this.DrawString("Шкала " Format("{:0." this.FloatFormat "f}",this.YScale[this.Sel]),this.AutoPos.X+this.AutoPos.W,MenuSPY,,,"",this.Font_Colour,this.LightBlueBrush)
    if ArAr
      this.OScalePos:=this.DrawString(" общая",this.ScalePos.X+this.ScalePos.W,MenuSPY,,,"",this.OScale?this.Font_Colour:this.GrayPen,this.LightBlueBrush)
    BWPos:=this.DrawString("(<<)",this.ScalePos.X+this.ScalePos.W+this.OScalePos.W+15,MenuSPY,,,"Center vCenter",this.Font_Colour,this.LightBlueBrush)
    PWPos:=this.DrawString("(||)",BWPos.X+BWPos.W+2,MenuSPY,,,"Center vCenter",this.Font_Colour,this.LightBlueBrush)
    FWPos:=this.DrawString("(>>)",PWPos.X+PWPos.W+2,MenuSPY,,,"Center vCenter",this.Font_Colour,this.LightBlueBrush)

    ; Размер меню
    tmp1:=(this.AutoPos.H>this.ScalePos.H)?this.AutoPos.H:this.ScalePos.H
    this.MenuOffset:=this.ZoomPos.H+tmp1+SelPos[1].H

    ; хитбоксы
    SelHBA:=[]
    SelHB:=0
    SelSplitHB:=false
    loop Ars
    {
      SelHBA.Push(this.HitBox(mX,mY,SelPos[a_index]))
      if SelHBA[a_index]
      {
        SelHB:=a_index
        break
      }
    }
    if ArAr
      SelSplitHB:=this.HitBox(mX,mY,SelPos[Ars+1])
    LineWHB:=this.HitBox(mX,mY,this.LineWPos)
    TransHB:=this.HitBox(mX,mY,this.TransPos)
    FontSizeHB:=this.HitBox(mX,mY,this.FontSizePos)
    ZoomHB:=this.HitBox(mX,mY,this.ZoomPos)
    SmoothHB:=this.HitBox(mX,mY,this.SmoothPos)
    AutoHB:=this.HitBox(mX,mY,this.AutoPos)
    ScaleHB:=this.HitBox(mX,mY,this.ScalePos)
    OScaleHB:=this.HitBox(mX,mY,this.OScalePos)
    BWHB:=this.HitBox(mX,mY,BWPos)
    PWHB:=this.HitBox(mX,mY,PWPos)
    FWHB:=this.HitBox(mX,mY,FWPos)
    HB:=AutoHB or ScaleHB or ZoomHB or SmoothHB or LineWHB or TransHB or FontSizeHB
	 or SelHB or OScaleHB or SelSplitHB or BWHB or PWHB or FWHB
    if (MouseIn and HB and !this.OffsetSetting and !this.Setting and A_Cursor="Arrow")
    {
      this.Setting:=true
      if AutoHB
      {
        TipText:="Автоматический масштаб  вкл./выкл."
        this.WhichSetting:=1
      }
      else if ScaleHB
      {
        TipText:="Масштаб шкалы`nЗажмите левую кн. мыши`n и потяните вверх/вниз`n или правой кнопкой мыши"
        this.WhichSetting:=2
      }
      else if ZoomHB
      {
        TipText:="Масштаб по горизонтали`nЗажмите левую кн. мыши`n и потяните вверх/вниз`n или правой кнопкой мыши"
        this.WhichSetting:=3
      }
      else if LineWHB
      {
        TipText:="Толщина линии графика`nЗажмите левую кн. мыши`n и потяните вверх/вниз`n или правой кнопкой мыши"
        this.WhichSetting:=4
      }
      else if SelHB
      {
        TipText:="Нажмите левой кн. мыши чтобы`n выбрать график, настройки`n которого будут изменяться`nПравой кн. мыши - выключить график`n`n" this.ArrName[SelHB]
        this.WhichSetting:=5
      }
      else if FontSizeHB
      {
        TipText:="Размер шрифта`nЗажмите левую кн. мыши`n и потяните вверх/вниз`n или правой кнопкой мыши"
        this.WhichSetting:=6
      }
      else if SmoothHB
      {
        TipText:="Сглаживать ступеньки графика  вкл./выкл."
        this.WhichSetting:=7
      }
      else if OScaleHB
      {
        TipText:="Общий масштаб шкалы  вкл./выкл."
        this.WhichSetting:=8
      }
      else if SelSplitHB
      {
        TipText:="Нажмите чтобы отделить `nвыбранный график в отдельное окно`n`n" this.ArrName[this.Sel]
        this.WhichSetting:=9
      }
      else if BWHB
      {
        TipText:="На 1 экран назад"
        this.WhichSetting:=10
      }
      else if PWHB
      {
        TipText:="Пауза прокрутки"
        this.WhichSetting:=11
      }
      else if FWHB
      {
        TipText:="На 1 экран вперед"
        this.WhichSetting:=12
      }
      else if TransHB
      {
        TipText:="Видимость линии графика`nЗажмите левую кн. мыши`n и потяните вверх/вниз`n или правой кнопкой мыши"
        this.WhichSetting:=13
      }
      this.TipPos:=this.DrawString(TipText,(this.GPosW-this.TipPos.W)//2,this.GPosH-this.TipPos.H,,,,this.Font_Colour,this.LightGreenBrush)
    }
    ; Изменение настроек
    if (getkeystate("LButton","P") and this.Setting)
    {
      if (this.WhichSetting=1)
      {
        this.AutoScale[this.Sel]:=!this.AutoScale[this.Sel]
        if this.OScale
          loop Ars
            this.AutoScale[a_index]:=this.AutoScale[this.Sel]
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=2)
      {
        this.YScale[this.Sel]+=(round(abs(this.YScale[this.Sel])/20)+1)*DeltaY
        this.YScale[this.Sel]:=round(this.YScale[this.Sel])
        this.AutoScale[this.Sel]:=false
      }
      else if (this.WhichSetting=3)
      {
        this.ZoomX+=(round(abs(this.ZoomX)/20)+1)*DeltaY
        this.ZoomX:=(this.ZoomX>100000)?100000:(this.ZoomX<10)?10:round(this.ZoomX)
        this.ZoomT+=(round(abs(this.ZoomT)/20)+1)*DeltaY
        this.ZoomT:=(this.ZoomT>Graph.MaxTime)?Graph.MaxTime:(this.ZoomT<1000)?1000:round(this.ZoomT)
      }
      else if (this.WhichSetting=4)
      {
        this.LineW[this.Sel]+=DeltaY/10
        if (this.LineW[this.Sel]<1)
          this.LineW[this.Sel]:=1
        if (this.LineW[this.Sel]>5)
          this.LineW[this.Sel]:=5
        this.LineW[this.Sel]:=round(this.LineW[this.Sel],1)
      }
      else if (this.WhichSetting=5)
      {
        loop Ars
        {
          if (SelHBA[a_index])
          {
            this.Sel:=a_index
            this.UpdateMenu()
            break
          }
        }
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=6)
      {
        this.FontSize+=DeltaY
        if (this.FontSize<8)
          this.FontSize:=8
        if (this.FontSize>70)
          this.FontSize:=70
        if (this.FontSize!=this.prevSize)
          this.SetFont(,this.FontSize)
      }
      else if (this.WhichSetting=7)
      {
        this.Smooth[this.Sel]:=!this.Smooth[this.Sel]
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=8)
      {
        this.OScale:=!this.OScale
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=9)
      {
        this.GuiGraph.GetPos(&x,&y)
        this.Child.Push(Graph())
        this.Child[this.Child.Length].CreateTime:=this.CreateTime
        this.Child[this.Child.Length].Show(x+20,y+20)
        this.Parent++
        this.Child[this.Child.Length].IsChild:=this.Parent
        this.Child[this.Child.Length].DataArray:=this.Data[this.Sel]
        this.Child[this.Child.Length].ArrName[1]:=this.ArrName[this.Sel]
        this.Child[this.Child.Length].Parent:=this
        ObjRelease(ObjPtr(this))
        this.WhichSetting:=0
        this.GHide[this.Sel]:=true
        loop Ars
        {
          if !this.GHide[a_index]
            this.Sel:=a_index
        }
      }
      else if (this.WhichSetting=10)
      {
        if !this.ScaleXByTime
          this.X_Offset+=this.ZoomX
        else
          this.XTOffset+=this.ZoomT
        this.ShiftStrt:=true
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=11)
      {
        this.Pause:=!this.Pause
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=12)
      {
        if !this.ScaleXByTime
          this.X_Offset-=this.ZoomX
        else
          this.XTOffset-=this.ZoomT
        this.ShiftStrt:=true
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=13)
      {
        this.Transp[this.Sel]+=DeltaY
        if (this.Transp[this.Sel]<1)
          this.Transp[this.Sel]:=1
        if (this.Transp[this.Sel]>255)
          this.Transp[this.Sel]:=255
        this.Pen[this.Sel]:=this.Transp[this.Sel]<<24|this.Pen[this.Sel]&0xffffff
      }
    }
    if (getkeystate("RButton","P") and this.Setting)
    {
      if (this.WhichSetting=5)
      {
        loop Ars
        {
          if (SelHBA[a_index])
          {
            this.GHide[a_index]:=!this.GHide[a_index]
            if this.GHide[a_index]
            {
              loop Ars
              {
                if !this.GHide[a_index]
                  this.Sel:=a_index
              }
            }
            break
          }
        }
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=2)
      {
        this.WhichVal:="YScale"
        this.GuiValText1.Value:="Масштаб шкалы"
        this.GuiValVal.Value:=this.YScale[this.Sel]
        this.GuiValText2.Value:=""
        this.MenuVal()
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=3)
      {
        this.GuiValText1.Value:="Масштаб по горизонтали"
        if this.ScaleXByTime
        {
          this.WhichVal:="ZoomT"
          this.GuiValVal.Value:=this.ZoomT/60000
          this.GuiValText2.Value:="минут"
        }
        else
        {
          this.WhichVal:="ZoomX"
          this.GuiValVal.Value:=this.ZoomX
          this.GuiValText2.Value:="точек"
        }
        this.MenuVal()
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=4)
      {
        this.WhichVal:="LineW"
        this.GuiValText1.Value:="Толщина линии графика"
        this.GuiValVal.Value:=this.LineW[this.Sel]
        this.GuiValText2.Value:=""
        this.MenuVal()
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=6)
      {
        this.WhichVal:="FontSize"
        this.GuiValText1.Value:="Размер шрифта"
        this.GuiValVal.Value:=this.FontSize
        this.GuiValText2.Value:=""
        this.MenuVal()
        this.WhichSetting:=0
      }
      else if (this.WhichSetting=13)
      {
        this.WhichVal:="Transp"
        this.GuiValText1.Value:="Видимость линии графика"
        this.GuiValVal.Value:=this.Transp[this.Sel]
        this.GuiValText2.Value:="0=прозрачен 255=видим"
        this.MenuVal()
        this.WhichSetting:=0
      }
    }
    if (!getkeystate("LButton","P") and !getkeystate("RButton","P"))
    {
      this.Setting:=false
      this.WhichSetting:=0
    }

; Информация от маркеров
    Markers:
    max:=min:="`n"
    if this.ShowMarkers
    {
      loop 2
      {
        if XMarkerCoord[a_index]
          this.DrawLine(this.RedPen,XMarkerCoord[a_index],0,XMarkerCoord[a_index],this.GPosH)
      }
      if !XMarkerCoord[1]
        MarkerText[1]:="Нажмите левой кн. мыши на график"
      if !XMarkerCoord[2]
        MarkerText[2]:="Нажмите правой кн. мыши на график"
      if (XMarkerCoord[1] and XMarkerCoord[2])
      {
        StDeviation:=sqrt((Deviation/ValNum))
        StDeviation:="Среднекв. отклонение = " Format("{:0." this.FloatFormat "f}",StDeviation)
        MarkerMean:="Среднее = " Format("{:0." this.FloatFormat "f}",MarkerMean)
        min:="Min = " Format("{:0." this.FloatFormat "f}",MarkerMin)
        max:="Max = " Format("{:0." this.FloatFormat "f}",MarkerMax)
      }
      String:=MarkerText[1] "`n" MarkerText[2] "`n" MarkerDText "`n" MarkerMean "`n" StDeviation "`n" max "`n" min
      Pos:=this.DrawString(String,0,this.AutoPos.Y+this.AutoPos.H,this.GPosW,,"",this.Font_Colour,this.LightGreenBrush)
    }

; Подсказка о текущем увеличении
    if ((!this.ScaleXByTime and this.OldZoomX!=this.ZoomX) or this.OldZoomT!=this.ZoomT)
    {
      this.ShowZoomTip:=1000
      this.OldZoomX:=this.ZoomX
      this.OldZoomT:=this.ZoomT
    }
    if (this.ShowZoomTip>0)
    {
      this.ShowZoomTip-=CycleTime
      if !this.ScaleXByTime
        this.iZoomPos:=this.DrawString("Zoom`n" this.ZoomX "`nточек",(this.GPosW-this.iZoomPos.W)//2,0,,,,this.Font_Colour,this.LightGreenBrush)
      else
      {
        tmp1:=this.MsToTime(this.ZoomT)
        tmp1[1]+=-1000000
        tmp2:=FormatTime(tmp1[1],"dd HH:mm:ss")
        tmp2.="." tmp1[2]
        this.iZoomPos:=this.DrawString("Zoom`n" tmp2,(this.GPosW-this.iZoomPos.W)//2,0,,,,this.Font_Colour,this.LightGreenBrush)
      }
    }
; Подсказка о времени отрисовки
    DllCall("QueryPerformanceCounter","Int64*",&EndTime:=0)
    ElapsedTime:=round((EndTime-StartTime)/frequency*1000,1)
    this.ETPos:=this.DrawString(ElapsedTime "ms",this.GPosW-this.ETPos.W,this.ZoomPos.Y+this.ETPos.H,,,,this.Font_Colour,this.Font_Back)
    if (this.HitBox(mX,mY,this.ETPos) and MouseIn)
      this.TipPos:=this.DrawString("Время отрисовки кадра",(this.GPosW-this.TipPos.W)//2,this.GPosH-this.TipPos.H,,,,this.Font_Colour,this.LightGreenBrush)

    if !this.ScaleXByTime
    {
      if (ElapsedTime>1000)
      {
        if !this.TimeOut
        {
          this.TOZoomX:=this.ZoomX
          this.TimeOut:=true
        }
        else
          this.ZoomX-=round(abs(this.ZoomX)/10)
      }
      else if (this.TimeOut and this.ZoomX<this.TOZoomX or ElapsedTime<100)
      {
        this.TimeOut:=false
      }
    }
    else
    {
      if (ElapsedTime>1000)
      {
        if !this.TimeOut
        {
          this.TOZoomT:=this.ZoomT
          this.TimeOut:=true
        }
        else
          this.ZoomT-=round(abs(this.ZoomT)/10)
      }
      else if (this.TimeOut and this.ZoomT<this.TOZoomT or ElapsedTime<100)
      {
        this.TimeOut:=false
      }
    }

; Обновить изображение
    if !this.GuiHiden
      this.Update()

    this.OldmX:=mX
    this.OldmY:=mY
    this.Drawing:=false
    ObjRelease(ObjPtr(this))
  }
;||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/||||
;|||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||
;||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////||
;|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|
;/////////////////////////////////////////////////////////////////////////////////////////////////

  TimeSub(Time1,Time2)
  {
    T1:=Time1[1]
    T2:=Time2[1]
    ms:=Time1[2]-Time2[2]
    if (ms<0)
    {
      ms+=1000
      T2:=DateAdd(T2,1,"Seconds")
    }
    Dif:=DateDiff(T1,T2,"Seconds")
    return Dif*1000+ms
  }
  TimeSubMs(Time,ValueMs)
  {
    if (ValueMs=0)
      return Time
    m:=0
    if (ValueMs>0)
    {
      ms:=Time[2]-mod(ValueMs,1000)
      if (ms<0)
      {
        ms+=1000
        m:=1
      }
      t:=DateAdd(Time[1],-(Floor(ValueMs/1000)+m),"Seconds")
    }
    else
    {
      ms:=mod(-ValueMs,1000)+Time[2]
      if (ms>999)
      {
        ms-=1000
        m:=1
      }
      t:=DateAdd(Time[1],Floor(-ValueMs/1000)+m,"Seconds")
    }
    return [t,format("{:03}",round(ms))]
  }
  TimeAddMs(Time,ValueMs)
  {
    T:=DateAdd(Time[1],ValueMs//1000,"Seconds")
    ms:=Time[2]+mod(ValueMs,1000)
    if (ms>999)
    {
      ms-=1000
      T:=DateAdd(T,1,"Seconds")
    }
    return [T,format("{:03}",ms)]
  }
  TimeToMs(Time)
  {
    HH:=FormatTime(Time,"HH")
    MM:=FormatTime(Time,"mm")
    SS:=FormatTime(Time,"ss")
    return HH*3600000+MM*60000+SS*1000
  }
  MsToTime(Ms)
  {
    T:=DateAdd(1601,Ms//1000,"Seconds")
    Ms:=format("{:03}",mod(Ms,1000))
    return [T,Ms]
  }

  TTOk(*)
  {
    ToTime:=this.hTT.Value
    tmp1:=DateDiff(A_Now,ToTime,"Seconds")
    this.XTOffset:=tmp1*1000-round((this.GPosW/2)/this.PerioT)
    this.Pause:=true
    this.ShiftStrt:=true
    this.GuiGoToT.Hide()
  }
  TTCancel(*)
  {
    this.GuiGoToT.Hide()
  }
  TXOk(*)
  {
    this.X_Offset:=this.ArrSize[this.Sel]-this.hTX.Value
    this.Pause:=true
    this.GuiGoToX.Hide()
  }
  TXCancel(*)
  {
    this.GuiGoToX.Hide()
  }
  NameOk(*)
  {
    this.ArrName[this.Sel]:=this.GuiNameVal.Value
    this.GuiName.Hide()
  }
  NameCancel(*)
  {
    this.GuiName.Hide()
  }
  ValAcc(*)
  {
    if (this.WhichVal="")
      return
    Whichval:=this.Whichval
    if (Whichval="YScale")
    {
      this.%Whichval%[this.Sel]:=this.GuiValVal.Value
      this.AutoScale[this.Sel]:=false
      if this.OScale
        loop this.AutoScale.Length
          this.AutoScale[a_index]:=this.AutoScale[this.Sel]
    }
    else if (Whichval="ZoomX")
    {
      this.%Whichval%:=round(this.GuiValVal.Value)
    }
    else if (Whichval="ZoomT")
    {
      this.%Whichval%:=round(this.GuiValVal.Value*60000)
    }
    else if (Whichval="FontSize")
    {
      this.%Whichval%:=this.GuiValVal.Value
      if (this.FontSize!=this.prevSize)
        this.SetFont(,this.FontSize)
    }
    else if (Whichval="Transp")
    {
      this.%Whichval%[this.Sel]:=round(this.GuiValVal.Value)&0xff
      this.Pen[this.Sel]:=this.Transp[this.Sel]<<24|this.Pen[this.Sel]&0xffffff
    }
    else if (Whichval!="")
    {
      if (isObject(this.%Whichval%))
        this.%Whichval%[this.Sel]:=this.GuiValVal.Value
      else
        this.%Whichval%:=this.GuiValVal.Value
    }
  }
  ValOk(*)
  {
    this.ValAcc()
    this.WhichVal:=""
    this.GuiVal.Hide()
  }
  ValCancel(*)
  {
    this.WhichVal:=""
    this.GuiVal.Hide()
  }
  HelpClose(*)
  {
    this.GuiHelp.Hide()
  }

  MenuVal(*)
  {
    this.GuiVal.Show()
  }
  MenuFlFt(*)
  {
    this.WhichVal:="FloatFormat"
    this.GuiValText1.Value:="Количество знаков после запятой"
    this.GuiValVal.Value:=this.FloatFormat
    this.GuiValText2.Value:=""
    this.MenuVal()
  }
  MenuName(*)
  {
    this.GuiNameVal.Value:=this.ArrName[this.Sel]
    this.GuiName.Show()
  }
  MenuGColor(*)
  {
    tmp1:=ChooseColor(this.Pen[this.Sel],this.GuiGraph.Hwnd)
    if (tmp1!="")
    {
      tmp2:=this.RGBToHSV(tmp1)
      if ((tmp2>>8)&0xff=0)
      {
        tmp3:=(tmp2&0xff)
        if (tmp3<127)
          tmp3:=127
        this.Hue[this.Sel]:=tmp3<<8
      }
      else
        this.Hue[this.Sel]:=tmp2>>16
      this.Pen[this.Sel]:=0xff000000 | tmp1
    }
  }
  MenuHelp(*)
  {
    this.GuiHelp.Show()
  }
  MenuGoTo(*)
  {
    if this.ScaleXByTime
    {
      this.GuiGoToT.Show()
    }
    else
    {
      this.GuiGoToX.Show()
    }
  }
  MenuClose(*)
  {
    this.Hide()
  }
  MenuPause(*)
  {
    this.Pause:=!this.Pause
  }
  MenuForward(*)
  {
    this.Pause:=false
    this.XTOffset:=0
    this.X_Offset:=0
    this.ShiftStrt:=true
  }
  MenuBackward(*)
  {
    this.Pause:=true
    this.X_Offset:=this.ArrSize[this.Sel]-this.ZoomX//2
    if this.ScaleXByTime
    {
      if this.TimeStampExist[this.Sel]
        tmp1:=DateDiff(A_Now,this.pData[1][2],"Seconds")
      else
        tmp1:=DateDiff(A_Now,this.StampT[this.Sel][1][1],"Seconds")
      this.XTOffset:=tmp1*1000-this.ZoomT//2
    }
    this.ShiftStrt:=true
  }
  MenuMarkers(*)
  {
    Add:=this.Pos.H*8
    if this.ShowMarkers:=!this.ShowMarkers
    {
      Size:=100+Add
      this.GuiGraph.Opt("+MinSize300x" Size)
      this.GuiGraph.GetPos(,,,&BPosH)
      this.GuiGraph.Move(,,,BPosH+Add)
      this.MarkerOffset:=Add
    }
    else
    {
      this.GuiGraph.Opt("+MinSize300x100")
      this.GuiGraph.GetPos(,,,&BPosH)
      this.GuiGraph.Move(,,,BPosH-Add)
      this.MarkerOffset:=0
    }
  }
  MenuAutoScale(*)
  {
    if (this.Lock_hi_lim[this.Sel] or this.Lock_lo_lim[this.Sel])
      this.AutoScale[this.Sel]:=false
    if this.AutoScale[this.Sel]:=!this.AutoScale[this.Sel]
    {
      this.Lock_hi_lim[this.Sel]:=false
      this.Lock_lo_lim[this.Sel]:=false
      this.GraphMenuSettings.check(this.mi.MenuAutoScale)
    }
    else
      this.GraphMenuSettings.uncheck(this.mi.MenuAutoScale)
  }
  MenuAlwaysOnTop(*)
  {
    if this.AlwaysOnTop:=!this.AlwaysOnTop
    {
      this.GuiGraph.Opt("+alwaysontop")
      this.GraphMenuSettings.check(this.mi.MenuAlwaysOnTop)
    }
    else
    {
      this.GuiGraph.Opt("-alwaysontop")
      this.GraphMenuSettings.uncheck(this.mi.MenuAlwaysOnTop)
    }
  }
  MenuSmooth(*)
  {
    if this.Smooth[this.Sel]:=!this.Smooth[this.Sel]
      this.GraphMenuSettings.check(this.mi.MenuSmooth)
    else
      this.GraphMenuSettings.uncheck(this.mi.MenuSmooth)
  }
  MenuScaleXByTime(*)
  {
    this.ScaleXByTime:=true
    this.GraphMenuSettings.check(this.mi.MenuScaleXByTime)
    this.GraphMenuSettings.uncheck(this.mi.MenuScaleXByDots)
  }
  MenuScaleXByDots(*)
  {
    this.ScaleXByTime:=false
    this.GraphMenuSettings.uncheck(this.mi.MenuScaleXByTime)
    this.GraphMenuSettings.check(this.mi.MenuScaleXByDots)
  }
  MenuResetFont(*)
  {
    this.FontSize:=12
    this.SetFont(,this.FontSize)
  }
  MenuSave(*)
  {
    SaveFileName:=FileSelect("S 16",,"Сохранить данные графика","(*.garr)")
    if (SaveFileName!="")
    {
      if !InStr(SaveFileName,".garr")
        SaveFileName.=".garr"
      o:=this.Data.Clone()
      o.Push(this.CreateTime)
      o.Push(this.ArrName)
      tooltip "Сохраняется..."
      ObjDump(SaveFileName,o)
      tooltip
      msgbox "Сохранено."
    }
  }
  MenuLoad(*)
  {
    SaveFileName:=FileSelect(3,,"Загрузить данные графика для просмотра","(*.garr)")
    if (SaveFileName!="")
      this.LoadFile(SaveFileName)
  }
  DropFiles(GuiObj,GuiCtrlObj,FileArray,X,Y)
  {
    if instr(FileArray[1],".garr")
      this.LoadFile(FileArray[1])
  }
  LoadFile(File)
  {
    o:=ObjLoad(File)
    this.SaveArrName:=this.ArrName.Clone()
    this.ArrName:=[]
    for v in o[o.Length]
    {
      if !isobject(v) and !isNumber(V)
      {
        this.ArrName:=o.Pop()
        this.CreateTime:=o.Pop()
        break
      }
    }
    this.NewData:=o
    this.MenuBackward()
    this.Pause:=true
    this.Loaded:=1
  }
  UpdateMenu()
  {
    if this.AutoScale[this.Sel]
      this.GraphMenuSettings.check(this.mi.MenuAutoScale)
    else
      this.GraphMenuSettings.uncheck(this.mi.MenuAutoScale)
    if this.Smooth[this.Sel]
      this.GraphMenuSettings.check(this.mi.MenuSmooth)
    else
      this.GraphMenuSettings.uncheck(this.mi.MenuSmooth)
  }

  AHSVToARGB(a:=255,h:=0,s:=0,v:=0)
  {
    H:=h/255
    S:=s/255
    V:=v/255
    i:=Floor(H*6)
    f:=H*6-i
    p:=V*(1-S)
    q:=V*(1-f*S)
    t:=V*(1-(1-f)*S)
    sw:=mod(i,6)
    if (sw=0)
      r:=V, g:=t, b:=p
    if (sw=1)
      r:=q, g:=V, b:=p
    if (sw=2)
      r:=p, g:=V, b:=t
    if (sw=3)
      r:=p, g:=q, b:=V
    if (sw=4)
      r:=t, g:=p, b:=V
    if (sw=5)
      r:=V, g:=p, b:=q
    r:=(Integer(r*255))&0xff
    g:=(Integer(g*255))&0xff
    b:=(Integer(b*255))&0xff
    return (a<<24 | r<<16 | g<<8 | b)
  }
  RGBToHSV(RGB)
  {
    r:=(RGB>>16 & 0xff)/255
    g:=(RGB>>8 & 0xff)/255
    b:=(RGB & 0xff)/255
    min:=(r<g)?((r<b)?r:b):((g<b)?g:b)
    val:=(r>g)?((r>b)?r:b):((g>b)?g:b)
    delta:=val-min
    if (delta=0) ;pure blackness
      return (RGB & 0xff)
    if (r=val) ;between yellow and magenta
      hue:=(g-b)/delta
    else if (g=val) ;between cyan and yellow
      hue:=2+(b-r)/delta
    else ;between magenta and cyan
      hue:=4+(r-g)/delta
    if (hue<=0)
      hue+=6
    hue:=round(hue/6*255)
    sat:=round((Delta/val)*255)
    val:=round(val*255)
    return (hue<<16 | sat<<8 | val)
  }
  Show(x:="",y:="",w:="",h:="")
  {
    this.GuiHiden:=false
    this.GuiGraph.Title:=this.Name
    this.GuiGraph.Show((x=""?"":"x" x " ") . (y=""?"":"y" y " ") . (w=""?"":"w" w " ") . (h=""?"":"h" h " "))
    if !this.SetParentOnce
    {
      this.SetParentOnce:=true
      DllCall("SetParent","Ptr",this.GuiGDIP.hwnd,"Ptr",this.GuiGraph.hwnd)
      WinMove(0,0,,,"ahk_id " this.GuiGDIP.hwnd)
      this.GuiGraph.Show()
    }
    settimer this.UpdateTimer,this.UpdatePeriod
  }

  Hide(*)
  {
    this.GuiHiden:=true
    settimer this.UpdateTimer,0
    this.Update(0)
    this.GuiGraph.Hide()
    if this.IsChild
      this.Parent.DeleteChild(this.IsChild)
  }
  DeleteChild(Num)
  {
    loop this.Child.Length
    {
      if (this.Child[a_index].IsChild=Num)
      {
        ObjAddRef(ObjPtr(this))
        this.Child[a_index]:=""
        this.Child.RemoveAt(a_index)
        break
      }
    }
  }

  Create()
  {
    this.hDC:=DllCall("CreateCompatibleDC","UPtr",0)
    bi:=Buffer(40,0)
    NumPut("UInt",40,bi,0)
    NumPut("UInt",A_ScreenWidth,bi,4)
    NumPut("UInt",A_ScreenHeight,bi,8)
    NumPut("UShort",1,bi,12)
    NumPut("UShort",32,bi,14)
    NumPut("UInt",0,bi,16)
    this.hBitmap:=DllCall("CreateDIBSection"
                     ,"Ptr",this.hDC
                     ,"Ptr",bi.Ptr
                     ,"uint",0
                     ,"UPtr*",0
                     ,"Ptr",0
                     ,"uint",0,"Ptr")
    this.obm:=DllCall("SelectObject","Ptr",this.hDC,"Ptr",this.hBitmap)
    DllCall(Graph.hGdipCreateFromHDC,"Ptr",this.hDC,"UPtr*",&pGraphics:=0)
    this.pGraphics:=pGraphics
    DllCall(Graph.hGdipSetSmoothingMode,"UPtr",this.pGraphics,"Int",4)
  }

  Update(Alpha:=255)
  {
    WinGetClientPos(,,&GPosW,&GPosH,this.GuiGraph.hwnd)
    this.GPosW:=GPosW
    this.GPosH:=GPosH-this.StampOffset-this.MarkerOffset-this.MenuOffset
    this.ZeroPosU:=this.GPosH-this.ZeroPos
    if (this.ZeroPosU<0 and this.GPosH_!=GPosH)
    {
      this.ZeroPos:=GPosH//2
      this.ZeroPosU:=this.GPosH-this.ZeroPos
    }
    this.GPosH_:=GPosH
    pt:=Buffer(8,0)
    DllCall("UpdateLayeredWindow"
            ,"Ptr",this.GuiGDIP.Hwnd
            ,"Ptr",0
            ,"Ptr",pt.Ptr
            ,"UInt*",GPosW|GPosH<<32
            ,"Ptr",this.hDC
            ,"int64*",0
            ,"uint",0
            ,"UInt*",Alpha<<16|1<<24
            ,"uint",2)
  }

  CreateSolidBrush(ARGB:=0xff000000)
  {
    DllCall(Graph.hGdipCreateSolidFill,"UInt",ARGB,"UPtr*",&pBrush:=0)
    return pBrush
  }
  DeleteBrush(pBrush)
  {
    DllCall(Graph.hGdipDeleteBrush,"UPtr",pBrush)
    return 0
  }
  CreatePen(ARGB:=0xff000000,w:=1)
  {
    DllCall(Graph.hGdipCreatePen1,"UInt",ARGB,"float",w,"int",2,"UPtr*",&pPen:=0)
    return pPen
  }
  DeletePen(pPen)
  {
    DllCall(Graph.hGdipDeletePen,"UPtr",pPen)
    return 0
  }

  FillRectangle(ARGB,x,y,w,h)
  {
    if (!this.pBrush or this.prevBrushColor!=ARGB)
    {
      if this.pBrush
        this.pBrush:=this.DeleteBrush(this.pBrush)
      this.pBrush:=this.CreateSolidBrush(ARGB)
    }
    this.prevBrushColor:=ARGB
    DllCall(Graph.hGdipFillRectangle
            ,"Ptr",this.pGraphics
            ,"Ptr",this.pBrush
            ,"float",x
            ,"float",y
            ,"float",w
            ,"float",h)
    return {X:x,Y:y,W:w,H:h}
  }
  DrawLine(ARGB,x1,y1,x2,y2,LineWidth:=1)
  {
    if (!this.pPen or this.prevPenColor!=ARGB or this.OldLineWidth!=LineWidth)
    {
      if this.pPen
        this.pPen:=this.DeletePen(this.pPen)
      this.pPen:=this.CreatePen(ARGB,LineWidth)
    }
    this.OldLineWidth:=LineWidth
    this.prevPenColor:=ARGB
    DllCall(Graph.hGdipDrawLine
            ,"Ptr",this.pGraphics
            ,"Ptr",this.pPen
            ,"float",x1
            ,"float",y1
            ,"float",x2
            ,"float",y2)
    return {X1:x1,Y1:y1,X2:x2,Y2:y2}
  }
  DrawLines(ARGB,Points,LineWidth:=1)
  {
    tmp1:=Points.Length
    if !tmp1
      return -1
    PointF:=Buffer(8*tmp1)
    Loop tmp1
    {
      NumPut("float",Points[a_index][1],PointF,8*(A_Index-1))
      NumPut("float",Points[a_index][2],PointF,(8*(A_Index-1))+4)
    }

    if (!this.pPen or this.prevPenColor!=ARGB or this.OldLineWidth!=LineWidth)
    {
      if this.pPen
        this.pPen:=this.DeletePen(this.pPen)
      this.pPen:=this.CreatePen(ARGB,LineWidth)
    }
    this.OldLineWidth:=LineWidth
    this.prevPenColor:=ARGB
    return DllCall(Graph.hGdipDrawLines
                   ,"Ptr",this.pGraphics
                   ,"Ptr",this.pPen
                   ,"Ptr",PointF.Ptr
                   ,"int",tmp1)
  }
  DrawEllipse(ARGB,x,y,w,h,LineWidth:=1)
  {
    if (!this.pPen or this.prevPenColor!=ARGB or this.OldLineWidth!=LineWidth)
    {
      if this.pPen
        this.pPen:=this.DeletePen(this.pPen)
      this.pPen:=this.CreatePen(ARGB,LineWidth)
    }
    this.OldLineWidth:=LineWidth
    this.prevPenColor:=ARGB
    DllCall(Graph.hGdipDrawEllipse
              ,"Ptr",this.pGraphics
              ,"Ptr",this.pPen
              ,"float",x
              ,"float",y
              ,"float",w
              ,"float",h)
    return {X:x,Y:y,W:w,H:h}
  }
  DrawRectangle(ARGB,x,y,w,h,LineWidth:=1)
  {
    if (!this.pPen or this.prevPenColor!=ARGB or this.OldLineWidth!=LineWidth)
    {
      if this.pPen
        this.pPen:=this.DeletePen(this.pPen)
      this.pPen:=this.CreatePen(ARGB,LineWidth)
    }
    this.OldLineWidth:=LineWidth
    this.prevPenColor:=ARGB
    DllCall(Graph.hGdipDrawRectangle
                ,"Ptr",this.pGraphics
                ,"Ptr",this.pPen
                ,"float",x
                ,"float",y
                ,"float",w
                ,"float",h)
    return {X:x,Y:y,W:w,H:h}
  }

  SetFont(Font:="",Size:="",fStyle:="",NoWrap:="",RenderingHint:="",Vertical:="")
  {
    Style:=0,Styles:="Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
    Loop Parse Styles,"|"
    {
      if RegExMatch(fStyle, "\b" A_loopField)
        Style|=(A_LoopField!="StrikeOut")?(A_Index-1):8
    }

    if (!this.hFormat
	 or (this.prevNoWrap!=NoWrap and NoWrap!="")
	 or (this.prevVertical!=Vertical and Vertical!=""))
    {
      if this.hFormat
      {
        DllCall(Graph.hGdipDeleteStringFormat,"UPtr",this.hFormat)
        this.hFormat:=""
      }
      Flags:=0x4000
      Flags:=NoWrap?(Flags|0x1000):Flags
      Flags:=Vertical?(Flags|0x2):Flags
      DllCall(Graph.hGdipCreateStringFormat,"int",Flags,"int",0,"UPtr*",&hFormat:=0)
      this.hFormat:=hFormat
      this.prevNoWrap:=NoWrap
      this.prevVertical:=Vertical
    }

    if (!this.hFamily or (this.prevFont!=Font and Font!=""))
    {
      if this.hFamily
      {
        if this.hFont
        {
          DllCall(Graph.hGdipDeleteFont,"UPtr",this.hFont)
          this.hFont:=""
        }
        DllCall(Graph.hGdipDeleteFontFamily,"UPtr",this.hFamily)
        this.hFamily:=""
      }
      Font:=Font="" ? "Arial" : Font
;      if !A_IsUnicode
;      {
;        nSize:=DllCall("MultiByteToWideChar","uint",0,"uint",0,"Ptr",&Font,"int",-1,"uint",0,"int",0)
;        VarSetCapacity(wFont,nSize*2)
;        DllCall("MultiByteToWideChar","uint",0,"uint",0,"Ptr",&Font,"int",-1,"Ptr",&wFont,"int",nSize)
;      }
;      DllCall(Graph.hGdipCreateFontFamilyFromName
;              ,"Ptr",A_IsUnicode ? &Font : &wFont
;              ,"uint",0
;              ,"UPtr*",hFamily)
      DllCall(Graph.hGdipCreateFontFamilyFromName
              ,"Ptr",StrPtr(String(Font))
              ,"uint",0
              ,"UPtr*",&hFamily:=0)
      this.hFamily:=hFamily
      this.prevFont:=Font
    }

    if (!this.hFont or (this.prevSize!=Size and Size!="") or (this.prevfStyle!=fStyle and fStyle!=""))
    {
      if this.hFont
      {
        DllCall(Graph.hGdipDeleteFont,"UPtr",this.hFont)
        this.hFont:=""
      }
      DllCall(Graph.hGdipCreateFont
		,"UPtr",this.hFamily
		,"float",(Size ? Size : 12)
		,"int",fStyle="" ? 0 : Style
		,"int",0
		,"UPtr*",&hFont:=0)
      this.hFont:=hFont
      this.prevSize:=Size
      this.prevfStyle:=fStyle
    }

    RenderingHint:=(RenderingHint="") ? ((this.prevRenderingHint="") ? 0 : this.prevRenderingHint) : RenderingHint
    this.prevRenderingHint:=RenderingHint
    ; SystemDefault = 0
    ; SingleBitPerPixelGridFit = 1
    ; SingleBitPerPixel = 2
    ; AntiAliasGridFit = 3
    ; AntiAlias = 4
    DllCall(Graph.hGdipSetTextRenderingHint,"UPtr",this.pGraphics,"int",RenderingHint)
  }

  DrawString(Text,xpos:=0,ypos:=0,Width:=0,Height:=0,Options:="Center",Colour:=0xff000000,BackgroundColour:=0)
  {
    if !(this.hFamily and this.hFont and this.hFormat)
      this.SetFont()

    Align:=0
    Loop Parse "Near|Left|Centre|Center|Far|Right","|"
    {
      if RegExMatch(Options, "\b" A_loopField)
        Align|=Floor(A_Index/2.1)      ; 0|0|1|1|2|2
    }
    DllCall(Graph.hGdipSetStringFormatAlign,"UPtr",this.hFormat,"int",Align)

    if (!this.FontpBrush or Colour!=this.prevColour)
    {
      if this.FontpBrush
        this.FontpBrush:=this.DeleteBrush(this.FontpBrush)
      this.FontpBrush:=this.CreateSolidBrush(Colour)
      this.prevColour:=Colour
    }

    RC:=this.CreateRectF(xpos,ypos,Width,Height)
    ReturnRC:=this.MeasureString(Text,RC)
    if (!Width or !Height)
    {
      Width:=Width ? Width : ReturnRC.W
      Height:=Height ? Height : ReturnRC.H
      RC:=this.CreateRectF(xpos,ypos,Width,Height)
      ReturnRC:=this.MeasureString(Text,RC)
    }
    if BackgroundColour
      this.FillRectangle(BackgroundColour,xpos,ypos,Width,Height)
    RegExMatch(Options,"i)Top|Up|Bottom|Down|vCentre|vCenter",&vPos)
    if vPos
    {
      vPos:=SubStr(Options,vPos.Pos,vPos.Len)
      if (vPos="vCentre" or vPos="vCenter")
        ypos+=(Height-ReturnRC.H)//2
      else if (vPos="Top" or vPos="Up")
        ypos:=ReturnRC.Y
      else if (vPos="Bottom" or vPos="Down")
        ypos+=Height-ReturnRC.H

      RC:=this.CreateRectF(xpos,ypos,Width,ReturnRC.H)
      ReturnRC:=this.MeasureString(Text,RC)
    }

;    sString:=Text
;    if (!A_IsUnicode)
;    {
;      nSize:=DllCall("MultiByteToWideChar","uint",0,"uint",0,"Ptr",&sString,"int",-1,"Ptr",0,"int",0)
;      VarSetCapacity(wString,nSize*2)
;      DllCall("MultiByteToWideChar","uint",0,"uint",0,"Ptr",&sString,"int",-1,"Ptr",&wString,"int",nSize)
;    }
;    DllCall(Graph.hGdipDrawString
;            ,"Ptr",this.pGraphics
;            ,"Ptr",A_IsUnicode ? &sString : &wString
;            ,"int",-1
;            ,"Ptr",this.hFont
;            ,"Ptr",&RC
;            ,"Ptr",this.hFormat
;            ,"Ptr",this.FontpBrush)
    DllCall(Graph.hGdipDrawString
            ,"Ptr",this.pGraphics
            ,"Ptr",StrPtr(String(Text))
            ,"int",-1
            ,"Ptr",this.hFont
            ,"Ptr",RC.Ptr
            ,"Ptr",this.hFormat
            ,"Ptr",this.FontpBrush)
    return ReturnRC
  }
  CreateRectF(x,y,w,h)
  {
    RectF:=Buffer(16)
    NumPut("float",x,RectF,0)
    NumPut("float",y,RectF,4)
    NumPut("float",w,RectF,8)
    NumPut("float",h,RectF,12)
    return RectF
  }

  MeasureString(sString,RectF)
  {
    RC:=Buffer(16)
;    if !A_IsUnicode
;    {
;      nSize:=DllCall("MultiByteToWideChar","uint",0,"uint",0,"Ptr",&sString,"int",-1,"uint",0,"int",0)
;      VarSetCapacity(wString,nSize*2)
;      DllCall("MultiByteToWideChar","uint",0,"uint",0,"Ptr",&sString,"int",-1,"Ptr",&wString,"int",nSize)
;    }
;    if !DllCall(Graph.hGdipMeasureString
;            ,"Ptr",this.pGraphics
;            ,"Ptr",A_IsUnicode ? &sString : &wString
;            ,"int",-1
;            ,"Ptr",this.hFont
;            ,"Ptr",&RectF
;            ,"Ptr",this.hFormat
;            ,"Ptr",&RC
;            ,"uint*",Chars
;            ,"uint*",Lines)
    if !DllCall(Graph.hGdipMeasureString
            ,"Ptr",this.pGraphics
            ,"Ptr",StrPtr(String(sString))
            ,"int",-1
            ,"Ptr",this.hFont
            ,"Ptr",RectF.Ptr
            ,"Ptr",this.hFormat
            ,"Ptr",RC.Ptr
            ,"uint*",&Chars:=0
            ,"uint*",&Lines:=0)
    return	{X:Ceil(NumGet(RC,0,"float"))
		,Y:Ceil(NumGet(RC,4,"float"))
		,W:Ceil(NumGet(RC,8,"float"))
		,H:Ceil(NumGet(RC,12,"float"))
		,Chars:Chars
		,Lines:Lines}
  }
}

#HotIf WinActive("ahk_class AutoHotkeyGUI",Graph.WinText)
WheelUp::
{
  MouseGetPos(,,&mW)
  Graph.WheelUp:=true
  Graph.HotKeymW:=mW
}
WheelDown::
{
  MouseGetPos(,,&mW)
  Graph.WheelDown:=true
  Graph.HotKeymW:=mW
}
#HotIf



/*  Windows Color Picker Plus
    https-//autohotkey.com/board/topic/91229-windows-color-picker-plus/
    Edited by me lemasato-
        BGR2RGB()-
        Use Format() to convert the value to hex instead of SetFormat

    Function- ChooseColor([pRGB, hOwner, DlgX, DlgY, Palette])
        Displays a standard Windows dialog for choosing colors.
    Parameters-
        pRGB - The initial color to display in the dialog in RGB format.
               The default setting is Black.
        hOwner - The Window ID of the dialog's owner, if it has one. Defaults to
                0, i.e. no owner. If specified DlgX and DlgY are ignored.
        DlgX, DlgY - The X and Y coordinates of the upper left corner of the
                     dialog. Both default to 0.
        Palette - An array of up to 16 RGB color values. These become the
                  initial custom colors in the dialog.
    Remarks-
        The custom colors in the dialog are remembered between calls.

        If the user selects OK, the Palette array (if it exists) will be loaded
        with the custom colors from the dialog.
    Returns-
        If the user selects OK, the selected color is returned in RGB format
        and ErrorLevel is set to 0. Otherwise, the original pRGB value is
        returned and ErrorLevel is set to 1.
*/

ChooseColor(pRGB:=0,hOwner:=0,DlgX:=0,DlgY:=0)
{
  static CustColors:=Buffer(64,0)    ; Custom colors are remembered between calls
  static SizeOfCustColors:=CustColors.size
  static ChooseColor:=Buffer(9*A_PtrSize,0)
  static StructSize:=ChooseColor.size
  static Palette:=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

  CustData:=(DlgX<<16)|DlgY    ; Store X in high word, Y in the low word

;___Load user's custom colors
  loop 16
    NumPut("UInt",BGR2RGB(Palette[a_index]),CustColors,(A_Index-1)*4)

;___Set up a ChooseColor structure as described in the MSDN
  NumPut("UInt",StructSize,ChooseColor,0)
  NumPut("UPtr",hOwner,ChooseColor,A_PtrSize)
  NumPut("UInt",BGR2RGB(pRGB),ChooseColor,3*A_PtrSize)
  NumPut("UPtr",CustColors.Ptr,ChooseColor,4*A_PtrSize)
  NumPut("UInt",0x113,ChooseColor,5*A_PtrSize)
  NumPut("UInt",CustData,ChooseColor,6*A_PtrSize)
  NumPut("UPtr",CallbackCreate(ColorWindowProc),ChooseColor,7*A_PtrSize)

;___Call the function
  ErrorLevel:=!DllCall("comdlg32\ChooseColor","UPtr",ChooseColor.Ptr,"UInt")

;___Save the changes made to the custom colors
  if !ErrorLevel
    Loop 16
      Palette[A_Index]:=BGR2RGB(NumGet(CustColors,(A_Index-1)*4,"UInt"))
  if !ErrorLevel ; modified by Alectric
    return BGR2RGB(NumGet(ChooseColor,3*A_PtrSize,"UINT"))
}

/*!
    Function- ColorWindowProc(hwnd, msg, wParam, lParam)
        Callback function used to modify the Color dialog before it is displayed
    Parameters-
        hwnd - Handle to the Color dialog window.
        msg - The message sent to the window.
        wParam - The handle to the control that has the keyboard focus.
        lParam - A pointer to the ChooseColor structure associated with the
                 Color dialog.
    Remarks-
        This is intended to be a private function, called only by ChooseColor.
        In response to a WM_INITDIALOG message, this function can be used to
        modify the Color dialog before it is displayed. Currently it just moves
        the window to a new X, Y location.
    Returns-
        If the hook procedure returns zero, the default dialog box procedure
        also processes the message. Otherwise, the default dialog box procedure
        ignores the message.
*/
ColorWindowProc(hwnd,msg,wParam,lParam)
{
  static WM_INITDIALOG:=0x0110

  if (msg!=WM_INITDIALOG)
    return 0

  hOwner:=NumGet(lParam,A_PtrSize,"UPtr")
  if hOwner
    return 0

  DetectSetting:=A_DetectHiddenWindows
  DetectHiddenWindows true
  CustData:=NumGet(lParam,6*A_PtrSize,"UInt")
  DlgX:=CustData>>16
  DlgY:=CustData&0xFFFF
  WinMove DlgX,DlgY,,,"ahk_id " hwnd

  DetectHiddenWindows DetectSetting
  return 0
}

/*!
    Function- BGR2RGB(Color)
        Converts a BGR color value to a RGB one or vice versa.
    Parameters-
        Color - The BGR or RGB value to convert
    Returns-
        The converted value.
*/
BGR2RGB(Color)
{
  ret:=  (Color & 0xFF000000)
       |((Color & 0x00FF0000)>>16)
       | (Color & 0x0000FF00)
       |((Color & 0x000000FF)<<16)
  return ret
}

ObjLoad(addr,&offset:=0) ; load dumped object to variable; from memory (addr is int) or from file (addr is string)
{
  ; #requires AutoHotkey v2.0.19
  If !IsNumber(addr)
  {
    If !FileExist(addr)
      return
    sz:=FileGetSize(addr)
    If !sz
      return
    Data:=FileRead(addr,"RAW")
    addr:=Data.Ptr
    sz:=NumGet(addr,"Int64")
    addr+=8
    obj:=0
  }
  ;    Sizes          1       1       2        2     4      4       8        8        8    x     x     x      x         x
  ;    Type.Enum: Char.1 UChar.2 Short.3 UShort.4 Int.5 UInt.6 Int64.7 UInt64.8 Double.9 Str.10 [].11 {}.12 map().13 buffer.14
  static types:=["Char","UChar","Short","UShort","Int","UInt","Int64","UInt64","Double"]
  static syzes:=[     2,      2,      3,       3,    5,     5,      9,       9,       9]
  typ:=NumGet(addr,offset,"Char")
  if (typ<10)
  {
    obj:=NumGet(addr,offset+1,types[typ])
    offset+=syzes[typ]
  }
  else if (typ=10)
  {
    size:=NumGet(addr,offset+1,"Int64")
    obj:=StrGet(addr+offset+9,size)
    offset+=9+size
  }
  else if (typ=11)
  {
    obj:=[]
    size:=NumGet(addr,offset+1,"Int64")
    obj.Length:=size
    offset+=9
    loop size
      obj[a_index]:=ObjLoad(addr,&offset)
  }
  else if (typ=12)
  {
    obj:={}
    Props:=NumGet(addr,offset+1,"Int64")
    offset+=9
    loop Props
    {
      k:=ObjLoad(addr,&offset)
      obj.%k%:=ObjLoad(addr,&offset)
    }
  }
  else if (typ=13)
  {
    obj:=Map()
    Props:=NumGet(addr,offset+1,"Int64")
    Items:=NumGet(addr,offset+9,"Int64")
    offset+=17
    loop Props
    {
      k:=ObjLoad(addr,&offset)
      obj.%k%:=ObjLoad(addr,&offset)
    }
    loop Items
    {
      i:=ObjLoad(addr,&offset)
      obj[i]:=ObjLoad(addr,&offset)
    }
  }
  else if (typ=14)
  {
    size:=NumGet(addr,offset+1,"Int64")
    obj:=Buffer(size)
    offset+=9
    loop size
      NumPut("UChar",NumGet(addr,offset+a_index-1,"UChar"),obj,a_index-1)
    offset+=size
  }
  return obj
}

ObjDump(Path,obj) ; dump object to file
{
  critical
  ; #requires AutoHotkey v2.0.19
  if !sz:=RawObjectSize(obj)
    return
  If FileExist(Path)
    FileDelete Path
  sz+=8
  Data:=Buffer(sz,0)
  ptr:=Data.Ptr
  NumPut("Int64",sz-8,ptr) ;size of all data
  RawObject(obj,ptr+8) ; use this for dumping to memory instead of ObjDump
  f:=FileOpen(Path,"rw-rwd","CP0")
  Loop (sz//65536)
    f.RawWrite(ptr,65536),ptr+=65536
  f.RawWrite(ptr,Mod(sz,65536))
  f.Close()
  return sz
}
RawObject(obj,ptr,offset:=0) ; dump object to memory
{
  ; #requires AutoHotkey v2.0.19
  ;    Sizes          1       1       2        2     4      4       8        8        8    x     x     x      x         x
  ;    Type.Enum: Char.1 UChar.2 Short.3 UShort.4 Int.5 UInt.6 Int64.7 UInt64.8 Double.9 Str.10 [].11 {}.12 map().13 buffer.14
  if isObject(Obj)
  {
    if (Obj.base=[].base)
    {
      NumPut("Char",11,ptr,offset) ; type
      NumPut("Int64",Obj.Length,ptr,offset+1) ; Length of array
      offset+=9
      loop Obj.Length
        offset:=RawObject(Obj[a_index],ptr,offset)
    }
    else if (Obj.base={}.base)
    {
      NumPut("Char",12,ptr,offset) ; type
      NumPut("Int64",ObjOwnPropCount(Obj),ptr,offset+1) ; num of Props
      offset+=9
      for k,v in Obj.OwnProps()
      {
        offset:=RawObject(k,ptr,offset) ; name of prop (str)
        offset:=RawObject(v,ptr,offset) ; value of prop (any)
      }
    }
    else if (Obj.base=Map().base)
    {
      NumPut("Char",13,ptr,offset) ; type
      NumPut("Int64",ObjOwnPropCount(Obj),ptr,offset+1) ; num of Props
      NumPut("Int64",Obj.Count,ptr,offset+9) ; num of items
      offset+=17
      for k,v in Obj.OwnProps()
      {
        offset:=RawObject(k,ptr,offset) ; name of prop (str)
        offset:=RawObject(v,ptr,offset) ; value of prop (any)
      }
      for k,v in Obj
      {
        offset:=RawObject(k,ptr,offset) ; index of item (any)
        offset:=RawObject(v,ptr,offset) ; value of item (any)
      }
    }
    else if (Obj.base=Buffer().base)
    {
      NumPut("Char",14,ptr,offset) ; type
      NumPut("Int64",Obj.size,ptr,offset+1) ; num of bytes
      offset+=9
      loop Obj.size
        NumPut("UChar",NumGet(Obj,a_index-1,"UChar"),ptr,offset+a_index-1) ; Buffer content (RAW)
      offset+=Obj.size
    }
  }
  else if IsNumber(obj) ; num
  {
    NumPut("Char",IsFloat(Obj)?9:Obj>4294967295?8       :Obj>65535?6     :Obj>255?4       :Obj>-1?2      :Obj>-129?1     :Obj>-32769?3      :Obj>-2147483649?5    :7,ptr,offset+0)
    NumPut(IsFloat(Obj)?"Double":Obj>4294967295?"UInt64":Obj>65535?"UInt":Obj>255?"UShort":Obj>-1?"UChar":Obj>-129?"Char":Obj>-32769?"Short":Obj>-2147483649?"Int":"Int64",Obj,ptr,offset+1)
    offset+=       IsFloat(Obj)||Obj>4294967295?9       :Obj>65535?5     :Obj>255?3                      :Obj>-129?2     :Obj>-32769?3      :Obj>-2147483649?5    :9
  }
  else ; string
  {
    NumPut("Char",10,ptr,offset)  ; type
    NumPut("Int64",sz:=StrPut(Obj,ptr+offset+9),ptr,offset+1) ; (str size) string text
    offset+=sz+9
  }
  return offset
}

RawObjectSize(Obj,sz:=0) ; get object's size in dumped condition
{
  ; #requires AutoHotkey v2.0.19
  if isObject(Obj)
  {
    if (Obj.base=[].base)
    {
      sz+=9 ; 1 (type) + 8 (num of items)
      loop Obj.Length
        sz:=RawObjectSize(Obj[a_index],sz)
    }
    else if (Obj.base={}.base)
    {
      sz+=9 ; 1 (type) + 8 (num of prop)
      for k,v in Obj.OwnProps()
      {
        sz:=RawObjectSize(k,sz) ; name of prop (str)
        sz:=RawObjectSize(v,sz) ; value of prop (any)
      }
    }
    else if (Obj.base=Map().base)
    {
      sz+=17 ; 1 (type) + 8 (size of Props) + 8 (size of array)
      for k,v in Obj.OwnProps()
      {
        sz:=RawObjectSize(k,sz) ; name of prop (str)
        sz:=RawObjectSize(v,sz) ; value of prop (any)
      }
      for k,v in Obj
      {
        sz:=RawObjectSize(k,sz) ; index
        sz:=RawObjectSize(v,sz) ; value
      }
    }
    else if (Obj.base=Buffer().base) ; sz = 1 (type) + 8 (size) + (buffer size)
      sz+=9+Obj.size
    else
    {
      MsgBox "Unsupported object type: `"" Type(obj) "`".","ObjDump",0x30
      return 0
    }
  }
  else if IsNumber(obj) ; sz = 1 (type) + (size of data type)
    sz+=IsFloat(Obj)||Obj>4294967295?9:Obj>65535?5:Obj>255?3:Obj>-129?2:Obj>-32769?3:Obj>-2147483649?5:9
  else ; sz = 1 (type) + 8 (size) + (str size)
    sz+=9+StrPut(Obj)
  return sz
}
Win 10 x64
AHK
                       Справка AHK v1 тебе в помощь.