-
Notifications
You must be signed in to change notification settings - Fork 71
/
class_Compass.ahk
208 lines (198 loc) · 9.97 KB
/
class_Compass.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/* Class_Compass
* By kon - ahkscript.org/boards/viewtopic.php?t=7225
* Translation to stand-alone Gdip thanks to jNizM - ahkscript.org/boards/viewtopic.php?&t=7225&p=45589#p45570
* Updated May 18, 2015
* Measure angles and scale distances with your mouse. Press the hotkeys and drag your mouse between two points.
* Note:
* *If you change the Measure/SetScale hotkeys, also change the keys GetKeyState monitors in the while-loop.
* **If you have multiple monitors, adjust the static variables w and h to match your resolution.
* Methods:
* Measure Measure angles and scale distances with your mouse.
* SetScale Sets the scale factor to use for subsequent measurements.
* ToggleSnap Toggle snap to horizontal/vertical when within +/-3 degrees.
* Methods (Internal):
* SetScaleGui Displays a GUI which prompts the user for the distance measured.
* pxDist Returns the distance between two points in pixels.
* DecFtToArch Converts decimal feet to architectural.
* gcd Euclidean GCD.
*/
/* EXAMPLE
*
* ^!LButton::Compass.Measure(1) ; Ctrl+Alt+LButton
* ^#LButton::Compass.Measure(2) ; Ctrl+Win+LButton
*
* ;Measure a known distance. Sets the scale factor for measuring subsequent distances.
* ^!RButton::Compass.SetScale(1, 1) ; Ctrl+Alt+RButton
*
* ; Clear the scale factor. Distance will not be calculated for subsequent measurements.
* ^#RButton::Compass.SetScale(1, 2) ; Ctrl+Win+RButton
*
* ; Toggle snap to horizontal/vertical when within +/-3 degrees.
* ^F12::Compass.ToggleSnap() ; Ctrl+F12
*
*
*/
class Compass {
static Color1 := 0x55000000, Color2 := 0x990066ff, Lineweight1 := 3
, ScaleFactor := "", Units := "ft", Snap := false
, w := A_ScreenWidth ;**The area in which this script is displayed, which should be your full resolution.
, h := A_ScreenHeight
/* Measure
* Measure angles and scale distances with your mouse.
* Parameters:
* Orientation 1 Up=90, Down=-90, Left=180, Right=0
* 2 Up=0, Down=180, Left=270, Right=90
* Returns: The distance, in pixels, between the two points. (Used internally by the SetScale method.)
*/
Measure(Orientation:=1) {
Conv := 180 / (4 * ATan(1)) * (Orientation = 1 ? -1 : 1)
if !(DllCall("kernel32.dll\GetModuleHandle", "Str", "gdiplus", "Ptr"))
hGDIPLUS := DllCall("kernel32.dll\LoadLibrary", "Str", "gdiplus.dll", "Ptr")
VarSetCapacity(SI, 24, 0), NumPut(1, SI, 0, "UInt")
DllCall("gdiplus.dll\GdiplusStartup", "UPtrP", pToken, "Ptr", &SI, "Ptr", 0)
if !(pToken) {
MsgBox, GDIPlus could not be started.`nCheck the availability of GDIPlus on your system.
return
}
CoordMode, Mouse, Screen
MouseGetPos, StartX, StartY
Gui, Compass: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
Gui, Compass: Show, NA
hWnd := WinExist()
, VarSetCapacity(BI, 40, 0), NumPut(this.w, BI, 4, "UInt"), NumPut(this.h, BI, 8, "UInt"), NumPut(40, BI, 0, "UInt")
, NumPut(1, BI, 12, "UShort"), NumPut(0, BI, 16, "UInt"), NumPut(32, BI, 14, "UShort")
, hbm := DllCall("gdi32.dll\CreateDIBSection", "Ptr", hdc := DllCall("user32.dll\GetDC", "Ptr", 0, "Ptr"), "UPtr", &BI, "UInt", 0, "UPtr*", 0, "Ptr", 0, "UInt", 0, "Ptr")
, DllCall("user32.dll\ReleaseDC", "Ptr", hWnd, "Ptr", hdc)
, hdc := DllCall("gdi32.dll\CreateCompatibleDC", "Ptr", 0, "Ptr")
, obm := DllCall("gdi32.dll\SelectObject", "Ptr", hdc, "Ptr", hbm)
, DllCall("gdiplus.dll\GdipCreateFromHDC", "Ptr", hdc, "PtrP", G)
, DllCall("gdiplus.dll\GdipSetSmoothingMode", "Ptr", G, "Int", 4)
, DllCall("gdiplus.dll\GdipCreatePen1", "UInt", this.Color1, "Float", this.Lineweight1, "Int", 2, "PtrP", pPen)
, DllCall("gdiplus.dll\GdipCreateSolidFill", "Int", this.Color2, "PtrP", pBrush)
, MouseKey := SubStr(A_ThisHotkey, 3), PrevX := "", PrevY := ""
, hMSVCRT := DllCall("kernel32.dll\LoadLibrary", "Str", "msvcrt.dll", "Ptr")
while (GetKeyState("Ctrl", "P") && GetKeyState(MouseKey, "P")) { ; *Loop until Ctrl or L/RButton is released.
MouseGetPos, CurrentX, CurrentY
if (PrevX != CurrentX) || (PrevY != CurrentY) {
PrevX := CurrentX, PrevY := CurrentY
if (this.Snap) {
if ((ADiff := Abs(CurrentY - StartY) / Abs(CurrentX - StartX)) < 0.052407 && ADiff)
CurrentY := StartY
else if (ADiff > 19.081136)
CurrentX := StartX
}
DllCall("gdiplus.dll\GdipDrawLine", "Ptr", G, "Ptr", pPen, "Float", StartX, "Float", StartY, "Float", CurrentX, "Float", CurrentY)
, DllCall("gdiplus.dll\GdipFillEllipse", "Ptr", G, "Ptr", pBrush, "Float", StartX - 3, "Float", StartY - 3, "Float", 7, "Float", 7)
, DllCall("gdiplus.dll\GdipFillEllipse", "Ptr", G, "Ptr", pBrush, "Float", CurrentX - 3, "Float", CurrentY - 3, "Float", 7, "Float", 7)
, VarSetCapacity(PT, 8), NumPut(0, PT, 0, "UInt"), NumPut(0, PT, 4, "UInt")
, DllCall("user32.dll\UpdateLayeredWindow", "Ptr", hWnd, "Ptr", 0, "UPtr", &PT, "Int64*", this.w | this.h << 32, "Ptr", hdc, "Int64*", 0, "UInt", 0, "UInt*", 0x1FF0000, "UInt", 2)
, DllCall("gdiplus.dll\GdipGraphicsClear", "Ptr", G, "UInt", 0x00FFFFFF)
, Angle := DllCall("msvcrt.dll\atan2", "Double", CurrentY - StartY, "Double", CurrentX - StartX, "CDECL Double") * Conv
if (Orientation = 2)
Angle += Angle < -90 ? 450 : 90
if (this.ScaleFactor) {
Dist := Format("{1:0.3f}", this.pxDist(CurrentX, StartX, CurrentY, StartY) * this.ScaleFactor)
if (this.Units = "ft")
Dist .= " ft (" . this.DecFtToArch(Dist) . ")"
else
Dist .= " " . this.Units
}
ToolTip, % Format("{1:0.3f}", Angle) . "°" . (Dist ? "`n" Dist : "")
}
}
DllCall("kernel32.dll\FreeLibrary", "Ptr", hMSVCRT)
, DllCall("gdiplus.dll\GdipDeletePen", "Ptr", pPen)
, DllCall("gdiplus.dll\GdipDeleteBrush", "Ptr", pBrush)
, DllCall("gdi32.dll\SelectObject", "Ptr", hdc, "Ptr", obm)
, DllCall("gdi32.dll\DeleteObject", "Ptr", hbm)
, DllCall("gdi32.dll\DeleteDC", "Ptr", hdc)
, DllCall("gdiplus.dll\GdipDeleteGraphics", "Ptr", G)
, DllCall("gdiplus.dll\GdiplusShutdown", "UPtr", pToken)
, DllCall("kernel32.dll\FreeLibrary", "Ptr", hGDIPLUS)
ToolTip
Gui, Compass: Destroy
return this.pxDist(CurrentX, StartX, CurrentY, StartY)
}
/* SetScale
* Enter the known distance measured to set the scale factor to be used with all subsequent calls to Measure.
* Parameters:
* Orientation 1 Up=90, Down=-90, Left=180, Right=0
* 2 Up=0, Down=180, Left=270, Right=90
* Action 1 The user will be prompted to enter the real world dimension corresponding to the
* current measurement. Distance will be calculated for any subsequent measurements.
* 2 Clear the scale factor. Distance will not be calculated for subsequent measurements.
*/
SetScale(Orientation:=1, Action:=1) {
if (Action = 2)
this.ScaleFactor := ""
pxDist := this.Measure(Orientation)
if (Action = 1)
this.SetScaleGui(pxDist)
return
}
/* ToggleSnap
* Toggle snap to horizontal/vertical when within +/-3 degrees.
*/
ToggleSnap() {
this.Snap := !this.Snap
TrayTip, Compass, % "Snap: " (this.Snap ? "On" : "Off")
}
SetScaleGui(pxLen) {
static Text1, Text2, Text3, Edit1, Edit2, Button1
Gui, SetScale: -Caption +Owner +LastFound
WinID := WinExist()
Gui, SetScale: Add, Text, vText1 x10 y10, Length
Gui, SetScale: Add, Text, vText2 x110 y10, Units
Gui, SetScale: Add, Edit, vEdit1 x10 y+10 w90
GuiControlGet, Edit1, SetScale:pos, Edit1
Gui, SetScale: Add, Edit, vEdit2 x+10 w90 y%Edit1Y%, % this.Units
Gui, SetScale: Add, Text, vText3 x10 y+10, Enter the length of this measurement.
Gui, SetScale: Add, Button, vButton1 x10 y+10 w190 gSetScaleOK Default, Ok
Gui, SetScale: Show,, Set Scale
WinSet AlwaysOnTop
GuiControl, Focus, Edit1
WinWaitNotActive, ahk_id %WinID%
SetScaleGuiEscape:
Gui, SetScale: Destroy
return
SetScaleOK:
Gui, SetScale: Submit
if Edit1 is number
this.ScaleFactor := Edit1 / pxLen
if (Edit2)
this.Units := Edit2
return
}
pxDist(x1, x2, y1, y2) {
DiffX := x1 - x2, DiffY := y1 - y2
return Sqrt(DiffX * DiffX + DiffY * DiffY)
}
DecFtToArch(DecFt, Precision:=16) {
Ft := Floor(DecFt), In := 12 * Mod(DecFt, 1)
, UpperLimit := 1 - (HalfPrecision := 0.5 / Precision)
, Fraction := Mod(In, 1), In := Floor(In)
if (Fraction >= UpperLimit) {
In++, Fraction := ""
if (In = 12)
In := 0, Ft++
}
else if (Fraction < HalfPrecision)
Fraction := ""
else {
Step := 1 / Precision
loop % Precision - 1 {
if (Fraction >= UpperLimit - (A_Index * Step)) {
gcd := this.gcd(Precision, n := Precision - A_Index)
, Fraction := " " n // gcd "/" Precision // gcd
break
}
}
}
return LTrim((Ft ? Ft "'-" : "") In Fraction """", " 0")
}
gcd(a, b) {
while (b)
b := Mod(a | 0x0, a := b)
return a
}
}