Тема: AHK: Class Рисование графиков из массива данных
Класс позволяет рассматривать собранные данные в графическом виде.
Поддерживает следующие типы массивов:
простой массив с данными; [ 1, 2, 3, ...]
массив данных с меткой времени в формате AHK (YYYYMMDDHH24MISS); [ [1,20230510200000], [2,20230510200001], ...]
массив с меткой времени с миллисекундами; [ [1,20230510200000,123], [2,20230510200001,432], ...]
массив массивов выше перечисленных типов.
Возможности:
автомасштаб;
режим отображения по точкам/по времени;
пауза перемещения;
переход на точку/время;
перемещение графика левой кнопкой мыши;
выделение и увеличение выделенного правой кнопкой мыши;
возврат к предыдущим параметрам после увеличения кликом правой кн. мыши;
отображение значения при наведении мыши на точку;
сглаживание "ступенек" линий;
регулировка толщины линии;
изменения размера шрифта;
маркеры диапазона значений с расчетом времени, среднего значения, среднеквадратичного отклонения;
маркеры и настройки работают только для выбранного графика;
выбор цвета графика.
Совместимость: Windows XP, 7, 10.
Важно!
Для удаления экземпляра класса обязательно необходимо вызвать метод "Delete" до удаления объекта.
Пример:
SetBatchLines,-1
c:=1,d:=0,e:=1
arr1:=[]
arr2:=[1]
arr3:={}
arr3[1]:={}
arr3[2]:={}
arr3[3]:={}
arr3[4]:={}
arr3[5]:={}
arr3[6]:={}
gosub,ddd
settimer,ddd,100
gosub,add2
gosub,add3
gra1:=new Graph()
gra2:=new Graph()
gra3:=new Graph()
gra1.show(700,500)
gra2.show(800,600)
gra3.show(900,700)
gra1.DataArray:=arr1
gra2.DataArray:=arr2
gra3.DataArray:=arr3
return
f1::
gra1.show()
gra2.show()
return
f2::
gra3.show()
return
ddd:
{
a+=0.1
if (b<-1 or b>1)
t:=!t
if t
b+=0.005
else
b-=0.005
if (b>1)
{
random,d,-1000000000,1000000000
random,e,-1000000000,1000000000
random,c,0.5,1.5
}
random,var,50.5,100.5
shift:=0
;var:=11
Val:=(sin(a)*var*b+shift)
arr1.Push([Val,A_Now])
arr3[3].Push([-Val,A_Now,A_MSec])
arr3[4].Push([Val,A_Now,A_MSec])
arr3[5].Push([cos(-a*2)*10*b+10,A_Now,A_MSec])
arr3[6].Push([-(cos(a*2)*10*b+10),A_Now,A_MSec])
}
return
add2:
random,g,0,1
if (g!=prevg)
{
arr2.Push(g)
arr3[1].Push(g)
}
prevg:=g
random,v,0,5000
settimer,add2,% v
return
add3:
random,g,0,1
if (g!=prevg3)
{
arr3[2].Push(g)
}
prevg3:=g
random,v,0,5000
settimer,add3,% v
return
esc::
gra1:=gra1.Delete()
gra2:=gra2.Delete()
gra3:=gra3.Delete()
exitapp
Class:
class Graph
{
static hModule:=DllCall("LoadLibrary","str","gdiplus","Ptr")
static pToken, 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 ; миллисекунд
ChooseColor(pRGB:=0,hOwner:=0,DlgX:=0,DlgY:=0,Palette*)
{
F:="ChooseColor"
if isFunc(F)
{
tmp1:=%F%(pRGB,hOwner,DlgX,DlgY,Palette)
return tmp1
}
}
__Delete()
{
Graph.InstanceCount--
if (Graph.InstanceCount=0)
{
DllCall(Graph.hGdiplusShutdown,"Ptr",Graph.pToken)
Graph.pToken:=""
}
; tooltip,% "Деструктор вызван.`n" Graph.InstanceCount
; sleep,1000
; tooltip
}
Delete()
{
obm:=this.UpdateTimer
settimer,% obm,Delete
this.UpdateTimer:=""
Number:=this.Number
menu,GraphMenu%Number%settings,DeleteAll
menu,GraphMenu%Number%,DeleteAll
DllCall(Graph.hGdipDeleteGraphics,"UPtr",this.pGraphics)
DllCall("SelectObject","Ptr",this.hDC,"Ptr",this.obm)
DllCall("DeleteObject","UPtr",this.hBitmap)
DllCall("ReleaseDC","Ptr",this.hGuiG,"Ptr",this.hDC)
Gui,% this.hGuiHelp . ":Destroy"
Gui,% this.hGuiTT . ":Destroy"
Gui,% this.hGuiG . ":Destroy"
Gui,% this.hGuiB . ":Destroy"
}
__New(Name="",UpdatePeriod=50)
{
; Сохранить дефолтное окно
SaveDefaultGui:=A_DefaultGui
; Запустить библиотеку гдиплюс
if !Graph.pToken
{
VarSetCapacity(si,A_PtrSize=8 ? 24 : 16,0), si:=Chr(1)
DllCall(Graph.hGdiplusStartup,"UPtr*",pToken,"Ptr",&si,"Ptr",0)
Graph.pToken:=pToken
}
Graph.InstanceCount++
this.Number:=Number:=Graph.InstanceCount
if !Name
this.Name:="График " Graph.InstanceCount
this.UpdatePeriod:=UpdatePeriod
; Добавление меню
; Меню настроек
obm:=ObjBindMethod(this,"MenuAlwaysOnTop")
menu,GraphMenu%Number%settings,add,Всегда поверх окон,% obm
obm:=ObjBindMethod(this,"MenuAutoScale")
menu,GraphMenu%Number%settings,add,Автомасштаб,% obm
menu,GraphMenu%Number%settings,check,Автомасштаб
obm:=ObjBindMethod(this,"MenuSmooth")
menu,GraphMenu%Number%settings,add,Сглаживать ступеньки,% obm
menu,GraphMenu%Number%settings,check,Сглаживать ступеньки
menu,GraphMenu%Number%settings,add
obm:=ObjBindMethod(this,"MenuScaleXByTime")
menu,GraphMenu%Number%settings,add,Масштабировать X по времени,% obm,+Radio
menu,GraphMenu%Number%settings,check,Масштабировать X по времени
this.ScaleXByTime:=true
obm:=ObjBindMethod(this,"MenuScaleXByDots")
menu,GraphMenu%Number%settings,add,Масштабировать X по точкам,% obm,+Radio
menu,GraphMenu%Number%settings,add
obm:=ObjBindMethod(this,"MenuResetFont")
menu,GraphMenu%Number%settings,add,Сбросить размер шрифта,% obm
obm:=ObjBindMethod(this,"MenuGColor")
menu,GraphMenu%Number%settings,add,Выбрать цвет графика,% obm
; основное меню
obm:=ObjBindMethod(this,"MenuClose")
menu,GraphMenu%Number%,add,Закрыть,% obm
obm:=ObjBindMethod(this,"MenuPause")
menu,GraphMenu%Number%,add,Пауза,% obm
obm:=ObjBindMethod(this,"MenuForward")
menu,GraphMenu%Number%,add,Вперёд,% obm
obm:=ObjBindMethod(this,"MenuBackward")
menu,GraphMenu%Number%,add,Назад,% obm
obm:=ObjBindMethod(this,"MenuGoTo")
menu,GraphMenu%Number%,add,Перейти,% obm
obm:=ObjBindMethod(this,"MenuMarkers")
menu,GraphMenu%Number%,add,Маркеры,% obm
menu,GraphMenu%Number%,add,Настр.,:GraphMenu%Number%settings
obm:=ObjBindMethod(this,"MenuHelp")
menu,GraphMenu%Number%,add,Справка,% obm
; Создание основного окна
Gui,Graph%Number%:+HwndhGuiB -sysmenu +MinSize400x100 +Resize
this.hGuiB:=hGuiB
Gui,%hGuiB%:Margin,0,0
; Создание окна отрисовки графики
Gui,Graphics%Number%:+HwndhGuiG +ownerGraph%Number% -Caption +ToolWindow +E0x08080020 ; WS_EX_LAYERED := 0x80000
this.hGuiG:=hGuiG
Gui,%hGuiG%:Show, noactivate x0 y0 w1 h1,% "Graphics " Number
; Создание окна перехода по времени
Gui,GraphGoToT%Number%:+HwndhGuiTT +ownerGraph%Number% -sysmenu -Resize
this.hGuiTT:=hGuiTT
; Создание окна перехода к точке
Gui,GraphGoToX%Number%:+HwndhGuiTX +ownerGraph%Number% -sysmenu -Resize
this.hGuiTX:=hGuiTX
; Создание окна справки
Gui,GraphHelp%Number%:+HwndhGuiHelp +ownerGraph%Number% -sysmenu -Resize
this.hGuiHelp:=hGuiHelp
; Добавление элементов основного окна
Gui,%hGuiB%:Menu,GraphMenu%Number%
Gui,%hGuiB%:font,s10
; специальный текст
Gui,%hGuiB%:add,text,w0 h0,% Graph.WinText
; Позиция графики
Gui,Graph%Number%:Add,Edit,+hwndhGPos -HScroll -VScroll Disabled ReadOnly w400 h200
this.hGPos:=hGPos
; Создание основного окна завершено
; Gui,%hGuiB%:show,noactivate,% this.Name
; Gui,%hGuiB%:hide
; Добавление элементов окна перехода по времени
Gui,%hGuiTT%:add,DateTime,+hwndhTT xm+1 w160,HH:mm:ss dd.MM.yyyy
this.hTT:=hTT
Gui,%hGuiTT%:add,button,+hwndhTTOkB xm+5 y+5 w70,Ok
obm:=ObjBindMethod(this,"TTOk")
GuiControl,+g,% hTTOkB,% obm
Gui,%hGuiTT%:add,button,+hwndhTTCancel x+10 w70,Отмена
obm:=ObjBindMethod(this,"TTCancel")
GuiControl,+g,% hTTCancel,% obm
; Добавление элементов окна перехода к точке
Gui,%hGuiTX%:add,Edit,+hwndhTX xm+1 w150,1
this.hTX:=hTX
Gui,%hGuiTX%:add,button,+hwndhTXOkB xm+5 y+5 w70,Ok
obm:=ObjBindMethod(this,"TXOk")
GuiControl,+g,% hTXOkB,% obm
Gui,%hGuiTX%:add,button,+hwndhTXCancel x+10 w70,Отмена
obm:=ObjBindMethod(this,"TXCancel")
GuiControl,+g,% hTXCancel,% obm
; Добавление элементов окна справки
HelpText=
(LTrim
Левая кнопка мыши - перемещение графика.
Правая кнопка мыши - выделение и увеличение выделенного участка.
Клик правой кнопкой - возврат к предыдущему масштабу.
)
Gui,%hGuiHelp%:add,text,+hwndhHelpText,% HelpText
Gui,%hGuiHelp%:add,button,+hwndhHelpClose xm+10 y+5 w70,Закрыть
obm:=ObjBindMethod(this,"HelpClose")
GuiControl,+g,% hHelpClose,% obm
; Вернуть права дефолтному окну
Gui,% SaveDefaultGui . ":Default"
; Создать графику
this.Create()
; Настройки по умолчанию
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:=0xff808080 ; для оси
this.GrayPen2:=0xffe0e0e0 ; для оси
this.RedPen:=0xffff0000 ; для маркера
this.LightGreenBrush:=0xffd5ffd5 ; для фона информации
this.LightBlueBrush:=0xffd5d5ff ; для фона Меню
this.BluePen:=0xff0000ff ; для графика
this.Pen:=[]
this.H:=[]
this.ZoomX:=100
this.ZoomT:=30000
this.XTOffset:=0
this.X_Offset:=0
this.ZeroPos:=0
this.ZeroPosU:=1
this.YScale:=[]
this.GPosH:=1
this.AStartTime:=0
this.StampOffset:=0
this.MarkerOffset:=0
this.StampsNum:=5
this.XMarker:=[]
this.MenuOffset:=0
this.Old_XTOffset:=0
this.Sel:=1
this.Proc:=1
this.prevArrSize:=[]
this.StampT:={}
this.SearchStop:=[]
this.SearchSkip:=[]
this.LineW:=[]
this.Smooth:=[]
this.AutoScale:=[]
this.Savehi_lim:=[]
this.Savelo_lim:=[]
this.SaveAutoScale:=[]
this.prev_ArrSize:=[]
this.Val_UnderGraph_Drawed:=[]
this.UnderGX:=[]
this.UnderGY:=[]
this.UnderGV:=[]
this.Lock_hi_lim:=[]
this.Lock_lo_lim:=[]
this.GHide:=[]
; Запомнить метку для таймера
this.UpdateTimer:=ObjBindMethod(this,"UpdateGraph")
/*
форматы обрабатывемых данных
Data:=[1,2,3,4,5,6,76,56,3,45,6,22,45,234]
- простой массив значений
Data:=[[1,234],[2,345],[3,456],[4,567]]
- массив с меткой времени [[значение,время],[значение,время]...]
Data:=[[1,234,56],[2,345,67],[3,456,78],[4,567,89]]
- массив с меткой времени с миллисекундами [[значение,время,миллисекунды],[значение,время,миллисекунды]...]
Добавить:
Data:=[[[1,234,56],[2,345,67]],[[1,234,56],[2,345,67]],[[1,234,56],[2,345,67]]]
- массив любых выше перечисленных массивов
*/
}
DataArray[]
{
set
{
this.StampT:={}
return this.Data:=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)
}
FloatFormatFix()
{
; Если в вашем коде где либо использован SetFormat,Float, то расскоментируйте следующие строки
/*
if this.SWFormat:=!this.SWFormat
{
this.SaveFormatFloat:=A_FormatFloat
SetFormat,Float,0.15
}
else
{
tmp1:=this.SaveFormatFloat
SetFormat,Float,%tmp1%
}
*/
; Тут какая-то чехорда, нужно подумать как сделать лучше...
}
;/////////////////////////////////////////////////////////////////////////////////////////////////
;|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|
;||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////||
;|||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||
;||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/||||
UpdateGraph()
{
if this.GuiHiden
return
this.FloatFormatFix()
DllCall("QueryPerformanceFrequency", "Int64*", frequency)
DllCall("QueryPerformanceCounter","Int64*",EndTime)
CycleTime:=round((EndTime-this.StartTime)/frequency*1000,1)
DllCall("QueryPerformanceCounter", "Int64*", StartTime)
this.StartTime:=StartTime
; Взять координаты мыши
SaveCoordMode:=A_CoordModeMouse
CoordMode,Mouse,Client
MouseGetPos,mX,mY,mW,mC,2
CoordMode,Mouse,% SaveCoordMode
MouseIn:=(this.hGPos=mC)
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"))
this.XMarker[2]:=mX
; Пауза скролинга для режима отображения по времени
if this.Pause
{
this.XTOffset+=CycleTime
this.OldXTOffset+=CycleTime
}
; Определение массива массивов
ArAr:=isObject(this.Data[1]) and isObject(this.Data[1,1])
or isObject(this.Data[1]) and !isObject(this.Data[1,1]) and (this.Data[1,2]="" or strlen(this.Data[1,2])!=14)
if ArAr
Ars:=this.Data.MaxIndex()
else
Ars:=1
loop,% Ars
{
this.Proc:=a_index
if (this.prevArrSize[this.Proc]="")
{
this.prevArrSize[this.Proc]:=1
this.SearchStop[this.Proc]:=true
this.SearchSkip[this.Proc]:=0
this.LineW[this.Proc]:=1
this.Smooth[this.Proc]:=true
this.AutoScale[this.Proc]:=true
this.GHide[this.Proc]:=false
this.UpdateMenu()
}
if (this.YScale[this.Proc]="")
this.YScale[this.Proc]:=1
if (this.Pen[this.Proc]="")
{
random,Hue,0,255
if this.Hue[this.Proc-1]
{
loop,100
{
random,Hue,0,255
if (abs(this.Hue[this.Proc-1]-Hue)>45)
break
}
}
this.Pen[this.Proc]:=Graph.AHSVToARGB(255,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
; Обработка массивов данных
this.ValTipPos:=""
loop:
loop,% Ars
{
this.Proc:=a_index
if ArAr
this.pData:=this.Data[this.Proc]
else
this.pData:=this.Data
ArrSize:=this.pData.MaxIndex()
this.ArrSize[this.Proc]:=ArrSize
this.TimeStampExist:=strlen(this.pData[ArrSize,2])=14
TimeStampMillis:=this.pData[ArrSize,3]!=""
Period:=this.GPosW/this.ZoomX ; количество пикселей на точку по X
PerioT:=this.GPosW/this.ZoomT ; количество пикселей на миллисекунду по X
this.PerioT:=PerioT
Pixel:=this.GPosH/this.YScale[this.Proc] ; сколько пикселей занимает 1 единица по Y
ZeroPos:=this.ZeroPosU
; Пауза скролинга для режима отображения по точкам
if (this.Pause 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 getkeystate("LButton","P") and mY<this.GPosH 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)
this.ConstrainXOffset()
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.GetmCoordOnce:=false
this.OffsetSetting:=false
; Добавление координаты маркеру
if !this.MouseMoved
this.XMarker[1]:=mX
this.MouseMoved:=false
}
if !getkeystate("LButton","P")
this.SearchStop[this.Proc]:=false
; Выделение участка
R_Button:
if (MouseIn and getkeystate("RButton","P") and mY<this.GPosH 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 ((Graph.WheelUp or Graph.WheelDown) and Graph.HotKeymC=this.hGPos)
{
this.ShiftStrt:=true
CoefHi:=mY/this.GPosH
CoefLo:=(this.GPosH-mY)/this.GPosH
tmp1:=round(this.ZoomX/20)+1
tmt1:=round(this.ZoomT/20)+1
if (tmp2>tmp3)
tmp3:=tmp2
if Graph.WheelUp
{
this.ZoomX-=tmp1
this.ZoomT-=tmt1
}
if Graph.WheelDown
{
this.ZoomX+=tmp1
this.ZoomT+=tmt1
}
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.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)
this.ConstrainXOffset()
}
; Добавление временной метки массиву без неё
if !this.TimeStampExist
{
if !isObject(this.StampT[this.Proc])
{
this.StampT[this.Proc]:=[[A_Now,A_MSec]]
this.prevArrSize[this.Proc]:=1
}
if (ArrSize!=this.prevArrSize[this.Proc])
{
tmp2:=this.StampT[this.Proc,this.prevArrSize[this.Proc]]
tmp3:=Graph.TimeSub([A_Now,A_MSec],tmp2)
tmp1:=ArrSize-this.prevArrSize[this.Proc]
tmp3:=tmp3//tmp1
loop,% tmp1
{
add:=a_index*tmp3
this.StampT[this.Proc].Push(Graph.TimeAddMs(tmp2,add))
}
this.prevArrSize[this.Proc]:=ArrSize
}
}
; отрисовать сетку
if (this.Sel=this.Proc)
{
tmpc:=Graph.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
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:=0
NotScaleXByTime:
this.LockYP[this.Proc]:=0
this.LockYM[this.Proc]:=0
if isobject(this.StampPos)
StampLine:=this.StampPos.W/2
else
StampLine:=0
this.Val_UnderGraph_Drawed[this.Proc]:=false
if !this.ScaleXByTime
{
Old_CoordY:=ZeroPos
Old_CoordX:=this.GPosW+10
GPoints:=[[Old_CoordX,Old_CoordY]]
if !this.GHide[this.Proc]
loop,% this.GPosW
{
iLast:=a_index=this.GPosW
i:=round(ArrSize-(a_index/Period+1))-this.X_Offset
if (i<1 or i>ArrSize) and !iLast
continue
if iLast
{
if (i-1>0)
i--
else
i:=Oldi
}
Aindex++
if (Aindex=1 and i+1<=ArrSize)
{
if !this.TimeStampExist
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
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)
{
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
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
if !isobject(this.StampPos)
tmp1:=0
else
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
{
FormatTime,tmp10,% this.StampT[this.Proc,i,1],HH:mm:ss
tmp10.="`n" this.StampT[this.Proc,i,2] "ms"
}
else
{
FormatTime,tmp10,% this.pData[i,2],dd.MM.yyyy'`n'HH:mm:ss
if (TimeStampMillis and this.pData[i,3]!="")
tmp10.="." 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
XMarker%a_index%i:=i
XMarker%a_index%Coord:=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
{
Strt:=Graph.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:=""
if this.Pause
Strt:=this.SaveStrt
Endt:=Graph.TimeSubMs(Strt,this.ZoomT)
; линия и текст временой шкалы
if (this.Sel=this.Proc)
{
loop,% this.GPosW
{
CoordX:=this.GPosW-(a_index-1)
StampLine++
if !isobject(this.StampPos)
tmp1:=0
else
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:=Graph.TimeSubMs(Strt,tmp2)
FormatTime,tmp10,% 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
}
}
}
; найти первую точку
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
{
iTime:=this.StampT[this.Proc,i,1]
iTimeMs:=this.StampT[this.Proc,i,2]
}
else
{
iTime:=this.pData[i,2]
if TimeStampMillis
iTimeMs:=this.pData[i,3]
else
{
if !Same
{
iTimeMs:=0
loop,1000
{
if (iTime=this.pData[i-a_index,2])
Same++
else
{
SameT:=1000/(Same+1)
break
}
}
}
if (Same>0)
{
iTimeMs:=round(SameT*Same)
Same--
}
}
}
if (iTime>Strt[1] or (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
iLast:=false
if !this.GHide[this.Proc]
loop,% iStart
{
i:=iStart-(a_index-1)
if !this.TimeStampExist
{
iTime:=this.StampT[this.Proc,i,1]
iTimeMs:=this.StampT[this.Proc,i,2]
}
else
{
iTime:=this.pData[i,2]
if TimeStampMillis
iTimeMs:=this.pData[i,3]
else
{
if !Same
{
iTimeMs:=0
loop,1000
{
tmp1:=this.pData[i-a_index,2]
if (iTime=tmp1)
Same++
else
{
SameT:=1000/(Same+1)
break
}
}
}
if (Same>0)
{
iTimeMs:=round(SameT*Same)
Same--
}
}
}
if (iTime<Endt[1])
iLast:=true
mms:=Strt[2]-iTimeMs
if (mms<0)
{
mms+=1000
iTime+=1,Seconds
}
ttt:=Strt[1]
ttt-=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
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)
{
max:=Graph.min
min:=Graph.max
loop,% Oldi-i
{
j:=i+a_index-1
if (j<1 or j>ArrSize)
continue
if !this.TimeStampExist
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
XMarker%a_index%i:=i
if !TimeStampMillis
MarkerTimeMs%a_index%:=format("{:03}",iTimeMs)
XMarker%a_index%Coord:=CoordX
}
}
}
}
}
; рисовать линию графика
if !this.GHide[this.Proc]
this.DrawLines(this.Pen[this.Proc],GPoints,this.LineW[this.Proc])
this.ZoomX:=Aindex
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])
{
tmp1:=this.GPosH//2
if (this.ZeroPosU>tmp1 and this.ZeroPosU<this.GPosH+1)
{
if (hi_lim>0)
{
tmp2:=this.GPosH-this.ZeroPosU
this.YScale[this.Proc]:=abs(hi_lim)+tmp2/Pixel
}
else
this.YScale[this.Proc]:=-lo_lim+this.ZeroPosU/Pixel
}
else if (this.ZeroPosU<=tmp1 and this.ZeroPosU>0)
{
if (lo_lim<0)
this.YScale[this.Proc]:=-lo_lim+this.ZeroPosU/Pixel
else
{
tmp2:=this.GPosH-this.ZeroPosU
this.YScale[this.Proc]:=abs(hi_lim)+tmp2/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]
{
tmpX:=this.UnderGX[this.Proc]+5<this.GPosW-this.ValTipPosW?this.UnderGX[this.Proc]+5:this.GPosW-this.ValTipPosW
tmpY:=this.UnderGY[this.Proc]-this.Pos.H<0?0:(this.UnderGY[this.Proc]-this.Pos.H-5)
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])
tmpc:=Graph.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(this.UnderGV[this.Proc],tmpX,tmpY,,,,this.Font_Colour,tmpc)
this.ValTipPosW:=this.ValTipPos.W
}
if (this.Sel=this.Proc)
{
loop,2
{
if XMarker%a_index%Coord
{
if !this.TimeStampExist
{
MarkerTime%a_index%:=this.StampT[this.Proc,XMarker%a_index%i,1]
FormatTime,tmp10,% MarkerTime%a_index%,HH:mm:ss
MarkerTimeMs%a_index%:=this.StampT[this.Proc,XMarker%a_index%i,2]
MarkerTimeFormated%a_index%:=tmp10 "." MarkerTimeMs%a_index%
MarkerValue%a_index%:=this.pData[XMarker%a_index%i]
MarkerText%a_index%.=tmp10 "." MarkerTimeMs%a_index% " " MarkerValue%a_index% ""
}
else
{
MarkerTime%a_index%:=this.pData[XMarker%a_index%i,2]
FormatTime,tmp10,% MarkerTime%a_index%,dd.MM.yyyy HH:mm:ss
if (TimeStampMillis and this.pData[XMarker%a_index%i,3]!="")
MarkerTimeMs%a_index%:=this.pData[XMarker%a_index%i,3]
tmp10.="." MarkerTimeMs%a_index%
MarkerTimeFormated%a_index%:=tmp10
MarkerValue%a_index%:=this.pData[XMarker%a_index%i,1]
MarkerText%a_index%.=tmp10 " " this.pData[XMarker%a_index%i,1] ""
}
}
}
}
} ; Конец цикла
if (Graph.HotKeymC=this.hGPos)
{
Graph.WheelUp:=false
Graph.WheelDown:=false
}
; Отрисовать меню
OnGraphMenu:
MenuSPY:=StampPos.Y+StampPos.H
SelPos:=[]
tmpc:=Graph.AHSVToARGB(200,this.Hue[1],this.Hue[1]>255?0:100,this.Hue[1]>255?(this.Hue[1]>>8):255)
SelPos[1]:=this.DrawString("1",0,MenuSPY,,,"Center vCenter",this.GHide[1]?tmpc:this.Font_Colour,tmpc)
loop,% Ars-1
{
tmp1:=a_index+1
tmpc:=Graph.AHSVToARGB(200,this.Hue[tmp1],this.Hue[tmp1]>255?0:100,this.Hue[tmp1]>255?(this.Hue[tmp1]>>8):255)
SelPos[tmp1]:=this.DrawString(tmp1,SelPos[a_index].X+SelPos[a_index].W+5,MenuSPY,,,"Center vCenter",this.GHide[tmp1]?tmpc:this.Font_Colour,tmpc)
}
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:=Graph.MsToTime(this.ZoomT)
tmp1[1]-=1000000
FormatTime,tmp2,% 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.ZoomPos.W+this.Pos.W,MenuSPY,,,"",this.Smooth[this.Sel]?this.Font_Colour:this.GrayPen,this.LightBlueBrush)
this.LineWPos:=this.DrawString("Толщина " this.LineW[this.Sel],this.SmoothPos.X+this.SmoothPos.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)
; следующая строка
this.AutoPos:=this.DrawString("Авто",0,this.ZoomPos.Y+this.ZoomPos.H,,,"",this.AutoScale[this.Sel]?this.Font_Colour:this.GrayPen,this.LightBlueBrush)
this.ScalePos:=this.DrawString("Шкала " this.YScale[this.Sel],this.AutoPos.X+this.AutoPos.W,this.AutoPos.Y,,,"",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
; хитбоксы
loop,% Ars
{
SelHB%a_index%:=Graph.HitBox(mX,mY,SelPos[a_index])
if (SelHB%a_index%)
{
SelHB:=true
break
}
}
LineWHB:=Graph.HitBox(mX,mY,this.LineWPos)
FontSizeHB:=Graph.HitBox(mX,mY,this.FontSizePos)
ZoomHB:=Graph.HitBox(mX,mY,this.ZoomPos)
SmoothHB:=Graph.HitBox(mX,mY,this.SmoothPos)
AutoHB:=Graph.HitBox(mX,mY,this.AutoPos)
ScaleHB:=Graph.HitBox(mX,mY,this.ScalePos)
HB:=AutoHB or ScaleHB or ZoomHB or SmoothHB or LineWHB or FontSizeHB or SelHB
if (MouseIn and HB and !this.OffsetSetting and !this.Setting and A_Cursor="Arrow")
{
this.Setting:=true
if AutoHB
{
TipText:="Автоматический масштаб`n`n Нажмите для включения/отключения"
this.WhichSetting:=1
}
else if ScaleHB
{
TipText:="Масштаб шкалы`n`nЗажмите левую `nкн. мыши и потяните`n для изменения"
this.WhichSetting:=2
}
else if ZoomHB
{
TipText:="Зажмите левую `nкн. мыши и потяните`n для изменения"
this.WhichSetting:=3
}
else if LineWHB
{
TipText:="Толщина линии графика`n`nЗажмите левую `nкн. мыши и потяните`n для изменения"
this.WhichSetting:=4
}
else if SelHB
{
TipText:="Нажмите левой кн. мыши чтобы`n выбрать график`n настройки которого`n будут изменяться`n`nПравой кн. мыши`n выключить график"
this.WhichSetting:=5
}
else if FontSizeHB
{
TipText:="Размер шрифта`n`nЗажмите левую `nкн. мыши и потяните`n для изменения"
this.WhichSetting:=6
}
else if SmoothHB
{
TipText:="Сглаживать ступеньки графика`n`n Нажмите для включения/отключения"
this.WhichSetting:=7
}
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]
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 (SelHB%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
}
}
if (getkeystate("RButton","P") and this.Setting)
{
if (this.WhichSetting=5)
{
loop,% Ars
{
if (SelHB%a_index%)
{
this.GHide[a_index]:=!this.GHide[a_index]
break
}
}
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 XMarker%a_index%Coord
this.DrawLine(this.RedPen,XMarker%a_index%Coord,0,XMarker%a_index%Coord,this.GPosH)
}
if !XMarker1Coord
MarkerText1:="Нажмите левой кн. мыши на график"
if !XMarker2Coord
MarkerText2:="Нажмите правой кн. мыши на график"
if (XMarker1Coord and XMarker2Coord)
{
i:=1, j:=2
if (XMarker1Coord<XMarker2Coord)
i:=2, j:=1
tmp1:=Graph.TimeToMs(MarkerTime%i%)+MarkerTimeMs%i%
tmp2:=Graph.TimeToMs(MarkerTime%j%)+MarkerTimeMs%j%
;YYYYMMDDHH24MISS
MarkerTime%i%-=MarkerTime%j%,Seconds
MarkerDeltaT:=1601
MarkerDeltaT+=MarkerTime%i%,Seconds
FormatTime,Y,% MarkerDeltaT,yyyy
FormatTime,M,% MarkerDeltaT,MM
FormatTime,D,% MarkerDeltaT,dd
Y:=format("{:04}",Y-1601)
M:=format("{:02}",M-1)
D:=format("{:02}",D-1)
if this.TimeStampExist
MarkerDText.=D "." M "." Y " "
tmp3:=Graph.MsToTime(tmp1-tmp2)
tmp3:=tmp3[1]
FormatTime,tmp10,% tmp3,HH:mm:ss
MarkerDText.=tmp10
if (MarkerTimeMs%i%!="")
{
StringRight,tmp1,% (tmp1-tmp2),3
MarkerDText.="." tmp1
}
max:=Graph.min
min:=Graph.max
ValNum:=XMarker%i%i-XMarker%j%i+1
loop,% ValNum
{
if !this.TimeStampExist
Val:=this.pData[XMarker%j%i+A_Index-1]
else
Val:=this.pData[XMarker%j%i+A_Index-1,1]
Summ+=Val
if (Val>max)
max:=Val
if (Val<min)
min:=Val
}
Mean:=Summ/ValNum
loop,% ValNum
{
if !this.TimeStampExist
Val:=this.pData[XMarker%j%i+A_Index-1]
else
Val:=this.pData[XMarker%j%i+A_Index-1,1]
Deviation+=(Val-Mean)**2
}
StDeviation:=sqrt((Deviation/ValNum))
StDeviation:="Среднекв. отклонение = " StDeviation
Mean:="Среднее = " Mean
min:="Min = " min
max:="Max = " max
ValNum:="Точек = " ValNum
}
String:=MarkerText1 "`n" MarkerText2 "`n" MarkerDText "`n" StDeviation "`n" Mean "`n" max "`n" min "`n" ValNum
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:=Graph.MsToTime(this.ZoomT)
tmp1[1]+=-1000000
FormatTime,tmp2,% 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)
ElapsedTime:=round((EndTime-StartTime)/frequency*1000,1)
this.ETPos:=this.DrawString(ElapsedTime "ms",this.GPosW-this.ETPos.W,this.ZoomPos.Y,,,,this.Font_Colour,this.Font_Back)
if (Graph.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 (ElapsedTime>1000)
{
if !this.ScaleXByTime
this.ZoomX-=round(abs(this.ZoomX)/5)
else
this.ZoomT-=round(abs(this.ZoomT)/5)
}
; Обновить изображение
if !this.GuiHiden
this.Update()
this.OldmX:=mX
this.OldmY:=mY
this.FloatFormatFix()
}
;||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/|||||||/||||
;|||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||||///|||
;||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////|||/////||
;|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|///////|
;/////////////////////////////////////////////////////////////////////////////////////////////////
ConstrainXOffset()
{
if !this.ScaleXByTime
{
tmp1:=this.ZoomX//2
; if (this.X_Offset>this.ArrSize[this.Proc]-tmp1)
; this.X_Offset:=this.ArrSize[this.Proc]-tmp1
if (this.X_Offset<-tmp1)
this.X_Offset:=-tmp1
}
else
{
tmp1:=this.ZoomT//2
if this.TimeStampExist
tmp2:=Graph.TimeSub([A_Now,A_MSec],[this.pData[1,2],this.pData[1,3]])
else
tmp2:=Graph.TimeSub([A_Now,A_MSec],[this.StampT[this.Proc,1,1],this.StampT[this.Proc,1,2]])
; if (this.XTOffset>tmp2-tmp1)
; this.XTOffset:=tmp2-tmp1
if (this.XTOffset<-tmp1)
this.XTOffset:=-tmp1
}
}
TimeSub(Time1,Time2)
{
T1:=Time1[1]
T2:=Time2[1]
ms:=Time1[2]-Time2[2]
if (ms<0)
{
ms+=1000
T2+=1,Seconds
}
T1-=T2,Seconds
return T1*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:=Time[1]
t+=-(Floor(ValueMs/1000)+m),Seconds
}
else
{
ms:=mod(-ValueMs,1000)+Time[2]
if (ms>999)
{
ms-=1000
m:=1
}
t:=Time[1]
t+=(Floor(-ValueMs/1000)+m),Seconds
}
return [t,format("{:03}",round(ms))]
}
TimeAddMs(Time,ValueMs)
{
T:=Time[1]
T+=ValueMs//1000,Seconds
ms:=Time[2]+mod(ValueMs,1000)
if (ms>999)
{
ms-=1000
T+=1,Seconds
}
return [T,format("{:03}",round(ms))]
}
TimeToMs(Time)
{
FormatTime,HH,% Time,HH
FormatTime,MM,% Time,mm
FormatTime,SS,% Time,ss
return HH*3600000+MM*60000+SS*1000
}
MsToTime(Ms)
{
T:=1601
T+=Ms//1000,Seconds
Ms:=format("{:03}",mod(Ms,1000))
return [T,Ms]
}
TTOk()
{
Gui,% this.hGuiTT . ":submit"
GuiControlGet,ToTime,,% this.hTT
tmp1:=A_Now
tmp1-=ToTime,Seconds
this.XTOffset:=tmp1*1000-round((this.GPosW/2)/this.PerioT)
this.ConstrainXOffset()
this.Pause:=true
this.ShiftStrt:=true
}
TTCancel()
{
Gui,% this.hGuiTT . ":hide"
}
TXOk()
{
Gui,% this.hGuiTX . ":submit"
GuiControlGet,ToX,,% this.hTX
this.X_Offset:=this.ArrSize[this.Sel]-ToX
this.ConstrainXOffset()
this.Pause:=true
}
TXCancel()
{
Gui,% this.hGuiTX . ":hide"
}
HelpClose()
{
Gui,% this.hGuiHelp . ":hide"
}
MenuGColor()
{
tmp1:=Graph.ChooseColor(this.Pen[this.Sel],this.hGuiB)
if (tmp1!="")
{
tmp2:=Graph.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()
{
hGuiHelp:=this.hGuiHelp
Gui,%hGuiHelp%:show,,Справка
}
MenuGoTo()
{
if this.ScaleXByTime
{
hGuiTT:=this.hGuiTT
Gui,%hGuiTT%:show,,Перейти на время
}
else
{
hGuiTX:=this.hGuiTX
Gui,%hGuiTX%: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
{
tmp1:=A_Now
if this.TimeStampExist
tmp1-=this.pData[1,2],Seconds
else
tmp1-=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
Number:=this.Number
Gui,Graph%Number%:+MinSize300x%Size%
WinGetPos,,,,BPosH,% "ahk_id" this.hGuiB
WinMove,% "ahk_id" this.hGuiB,,,,,BPosH+Add
this.MarkerOffset:=Add
}
else
{
Number:=this.Number
Gui,Graph%Number%:+MinSize300x100
WinGetPos,,,,BPosH,% "ahk_id" this.hGuiB
WinMove,% "ahk_id" this.hGuiB,,,,,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
Number:=this.Number
if this.AutoScale[this.Sel]:=!this.AutoScale[this.Sel]
{
this.Lock_hi_lim[this.Sel]:=false
this.Lock_lo_lim[this.Sel]:=false
menu,GraphMenu%Number%settings,check,Автомасштаб
}
else
menu,GraphMenu%Number%settings,uncheck,Автомасштаб
}
MenuAlwaysOnTop()
{
Number:=this.Number
if this.AlwaysOnTop:=!this.AlwaysOnTop
{
Gui,Graph%Number%:+alwaysontop
menu,GraphMenu%Number%settings,check,Всегда поверх окон
}
else
{
Gui,Graph%Number%:-alwaysontop
menu,GraphMenu%Number%settings,uncheck,Всегда поверх окон
}
}
MenuSmooth()
{
Number:=this.Number
if this.Smooth[this.Sel]:=!this.Smooth[this.Sel]
menu,GraphMenu%Number%settings,check,Сглаживать ступеньки
else
menu,GraphMenu%Number%settings,uncheck,Сглаживать ступеньки
}
MenuScaleXByTime()
{
Number:=this.Number
this.ScaleXByTime:=true
menu,GraphMenu%Number%settings,check,Масштабировать X по времени
menu,GraphMenu%Number%settings,uncheck,Масштабировать X по точкам
}
MenuScaleXByDots()
{
Number:=this.Number
this.ScaleXByTime:=false
menu,GraphMenu%Number%settings,uncheck,Масштабировать X по времени
menu,GraphMenu%Number%settings,check,Масштабировать X по точкам
}
MenuResetFont()
{
this.FontSize:=12
this.SetFont(,this.FontSize)
}
UpdateMenu()
{
Number:=this.Number
if this.AutoScale[this.Sel]
menu,GraphMenu%Number%settings,check,Автомасштаб
else
menu,GraphMenu%Number%settings,uncheck,Автомасштаб
if this.Smooth[this.Sel]
menu,GraphMenu%Number%settings,check,Сглаживать ступеньки
else
menu,GraphMenu%Number%settings,uncheck,Сглаживать ступеньки
}
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:=(r*255)&0xff
g:=(g*255)&0xff
b:=(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="")
{
this.GuiHiden:=false
hGuiB:=this.hGuiB
if (x="")
x:=a_screenwidth//2-400
if (y="")
y:=a_screenheight//2-200
Gui,%hGuiB%:show,% "x" x " y" y,% this.Name
obm:=this.UpdateTimer
settimer,% obm,% this.UpdatePeriod
}
Hide()
{
this.GuiHiden:=true
obm:=this.UpdateTimer
settimer,% obm,off
this.Update(0)
Gui,% this.hGuiB . ":hide"
}
Create()
{
this.hDC:=DllCall("CreateCompatibleDC","UPtr",0)
VarSetCapacity(bi,40,0)
NumPut(40,bi,0,"uint")
NumPut(A_ScreenWidth,bi,4,"uint")
NumPut(A_ScreenHeight,bi,8,"uint")
NumPut(1,bi,12,"ushort")
NumPut(32,bi,14,"ushort")
NumPut(0,bi,16,"uInt")
this.hBitmap:=DllCall("CreateDIBSection"
,"Ptr",this.hDC
,"Ptr",&bi
,"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)
this.pGraphics:=pGraphics
DllCall(Graph.hGdipSetSmoothingMode,"UPtr",this.pGraphics,"Int",4)
}
Update(Alpha=255)
{
wingetpos,,,BPosW,BPosH,% "ahk_id" this.hGuiB
if (this.PrevW!=BPosW or this.PrevH!=BPosH)
{
Pos:=Graph.GetClientSize(this.hGuiB)
WinMove,% "ahk_id" this.hGPos,,0,0,Pos.W-1,Pos.H-1
this.PrevW:=BPosW
this.PrevH:=BPosH
}
wingetpos,GPosX,GPosY,GPosW,GPosH,% "ahk_id" this.hGPos
this.GPosW:=GPosW
this.GPosH:=GPosH-this.StampOffset-this.MarkerOffset-this.MenuOffset
this.GPosH_:=GPosH
this.ZeroPosU:=this.GPosH-this.ZeroPos
VarSetCapacity(pt,8)
NumPut(GPosX,pt,0,"UInt")
NumPut(GPosY,pt,4,"UInt")
DllCall("UpdateLayeredWindow"
,"Ptr",this.hGuiG
,"Ptr",0
,"Ptr",&pt
,"int64*",GPosW|GPosH<<32
,"Ptr",this.hDC
,"int64*",0
,"uint",0
,"UInt*",Alpha<<16|1<<24
,"uint",2)
}
GetClientSize(hwnd)
{
VarSetCapacity(rc,16)
DllCall("GetClientRect","uint",hwnd,"uint",&rc)
return {"X":NumGet(rc,0,"int"),"Y":NumGet(rc,4,"int"),"W":NumGet(rc,8,"int"),"H":NumGet(rc,12,"int")}
}
CreateSolidBrush(ARGB=0xff000000)
{
DllCall(Graph.hGdipCreateSolidFill,"UInt",ARGB,"UPtr*",pBrush)
return pBrush
}
DeleteBrush(byref pBrush)
{
DllCall(Graph.hGdipDeleteBrush,"UPtr",pBrush)
pBrush:=""
}
CreatePen(ARGB=0xff000000,w=1)
{
DllCall(Graph.hGdipCreatePen1,"UInt",ARGB,"float",w,"int",2,"UPtr*",pPen)
return pPen
}
DeletePen(byref pPen)
{
DllCall(Graph.hGdipDeletePen,"UPtr",pPen)
pPen:=""
}
FillRectangle(ARGB,x,y,w,h)
{
if (!this.pBrush or this.prevBrushColor!=ARGB)
{
if this.pBrush
Graph.DeleteBrush(this.pBrush)
this.pBrush:=Graph.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
Graph.DeletePen(this.pPen)
this.pPen:=Graph.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.MaxIndex()
if !tmp1
return -1
VarSetCapacity(PointF,8*tmp1)
Loop,% tmp1
{
NumPut(Points[a_index,1],PointF,8*(A_Index-1),"float")
NumPut(Points[a_index,2],PointF,(8*(A_Index-1))+4,"float")
}
if (!this.pPen or this.prevPenColor!=ARGB or this.OldLineWidth!=LineWidth)
{
if this.pPen
Graph.DeletePen(this.pPen)
this.pPen:=Graph.CreatePen(ARGB,LineWidth)
}
this.OldLineWidth:=LineWidth
this.prevPenColor:=ARGB
return DllCall(Graph.hGdipDrawLines
,"Ptr",this.pGraphics
,"Ptr",this.pPen
,"Ptr",&PointF
,"int",tmp1)
}
DrawEllipse(ARGB,x,y,w,h,LineWidth=1)
{
if (!this.pPen or this.prevPenColor!=ARGB or this.OldLineWidth!=LineWidth)
{
if this.pPen
Graph.DeletePen(this.pPen)
this.pPen:=Graph.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
Graph.DeletePen(this.pPen)
this.pPen:=Graph.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="")
{
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!=""))
{
if this.hFormat
{
DllCall(Graph.hGdipDeleteStringFormat,"UPtr",this.hFormat)
this.hFormat:=""
}
DllCall(Graph.hGdipCreateStringFormat,"int",NoWrap ? 0x4000 | 0x1000 : 0x4000,"int",0,"UPtr*",hFormat)
this.hFormat:=hFormat
this.prevNoWrap:=NoWrap
}
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)
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)
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)
{
RegExMatch(Options,"i)Top|Up|Bottom|Down|vCentre|vCenter",vPos)
if !(this.hFamily and this.hFont and this.hFormat)
this.SetFont()
Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right"
Loop, Parse, Alignments, |
{
if RegExMatch(Options, "\b" A_loopField)
Align |= 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
Graph.DeleteBrush(this.FontpBrush)
this.FontpBrush:=Graph.CreateSolidBrush(Colour)
this.prevColour:=Colour
}
Graph.CreateRectF(RC,xpos,ypos,Width,Height)
ReturnRC:=this.MeasureString(Text,RC)
if (!Width or !Height)
{
Width:=Width ? Width : ReturnRC.W
Height:=Height ? Height : ReturnRC.H
Graph.CreateRectF(RC,xpos,ypos,Width,Height)
ReturnRC:=this.MeasureString(Text,RC)
}
if BackgroundColour
this.FillRectangle(BackgroundColour,xpos,ypos,Width,Height)
if vPos
{
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
Graph.CreateRectF(RC,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)
return ReturnRC
}
CreateRectF(ByRef RectF, x, y, w, h)
{
VarSetCapacity(RectF, 16)
NumPut(x, RectF, 0, "float"), NumPut(y, RectF, 4, "float"), NumPut(w, RectF, 8, "float"), NumPut(h, RectF, 12, "float")
}
MeasureString(sString,ByRef RectF)
{
VarSetCapacity(RC,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)
return {"X":NumGet(RC, 0, "float"),"Y":NumGet(RC, 4, "float"),"W":NumGet(RC, 8, "float"),"H":NumGet(RC, 12, "float"),"Chars":Chars,"Lines":Lines}
}
}
#if WinActive("ahk_class AutoHotkeyGUI",Graph.WinText)
WheelUp::
MouseGetPos,,,,mC,2
Graph.WheelUp:=true
Graph.HotKeymC:=mC
return
WheelDown::
MouseGetPos,,,,mC,2
Graph.WheelDown:=true
Graph.HotKeymC:=mC
return
#if
/* Windows Color Picker Plus (by rbrtryn)
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, Palette*)
{
static CustColors ; Custom colors are remembered between calls
static SizeOfCustColors := VarSetCapacity(CustColors, 64, 0)
static StructSize := VarSetCapacity(ChooseColor, 9 * A_PtrSize, 0)
CustData := (DlgX << 16) | DlgY ; Store X in high word, Y in the low word
;___Load user's custom colors
for Index, Value in Palette
NumPut(BGR2RGB(Value), CustColors, (Index - 1) * 4, "UInt")
;___Set up a ChooseColor structure as described in the MSDN
NumPut(StructSize, ChooseColor, 0, "UInt")
NumPut(hOwner, ChooseColor, A_PtrSize, "UPtr")
NumPut(BGR2RGB(pRGB), ChooseColor, 3 * A_PtrSize, "UInt")
NumPut(&CustColors, ChooseColor, 4 * A_PtrSize, "UPtr")
NumPut(0x113, ChooseColor, 5 * A_PtrSize, "UInt")
NumPut(CustData, ChooseColor, 6 * A_PtrSize, "UInt")
NumPut(RegisterCallback("ColorWindowProc"), ChooseColor, 7 * A_PtrSize, "UPtr")
;___Call the function
ErrorLevel := ! DllCall("comdlg32\ChooseColor", "UPtr", &ChooseColor, "UInt")
;___Save the changes made to the custom colors
if not 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+0, A_PtrSize, "UPtr")
if (hOwner)
return 0
DetectSetting := A_DetectHiddenWindows
DetectHiddenWindows On
CustData := NumGet(lParam+0, 6 * A_PtrSize, "UInt")
DlgX := CustData >> 16, DlgY := CustData & 0xFFFF
WinMove ahk_id %hwnd%, , %DlgX%, %DlgY%
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 & 0xFF0000) >> 16)
| (Color & 0x00FF00)
| ((Color & 0x0000FF) << 16)
return ret
}