Compass - Measure angles and scale distances with your mouse

Post your working scripts, libraries and tools
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Compass - Measure angles and scale distances with your mouse

24 Apr 2015, 11:25

Compass
Measure angles and scale distances with your mouse.
Demo Gif

Code: Select all

^!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


/*  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.
 */
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
    }
}
Last edited by kon on 18 May 2015, 15:57, edited 14 times in total.
User avatar
joedf
Posts: 7283
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: [Function] Compass - Measure angles with your mouse

25 Apr 2015, 09:46

Interesting concept! :0
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 4.00 GHz, 2x8GB G.Skill RipJaws V - DDR4 3280 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
toralf
Posts: 636
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: [Function] Compass - Measure angles with your mouse

25 Apr 2015, 10:39

I'm curious, kon.
Why did you script this?
Besides measuring the angle.
What do you do with the angle?
ciao
toralf
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: [Function] Compass - Measure angles with your mouse

25 Apr 2015, 11:13

It was originally an "Ask for help" request. [Link] I thought it was interesting.
Also, I sometimes need to calculate angles from incomplete information. ie plans in .pdf format. I will use this as a quick double-check in those situations. It's already come in handy :)
toralf
Posts: 636
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: [Function] Compass - Measure angles with your mouse

25 Apr 2015, 11:51

Thanks for satisfying my curiosity.
ciao
toralf
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: Compass - Measure angles and scale distances with your m

28 Apr 2015, 12:46

Update: Added the ability to scale distances.
User avatar
joedf
Posts: 7283
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: Compass - Measure angles and scale distances with your m

28 Apr 2015, 18:25

Oh oh Oohh!!!! Add an awesome Gif!! :D
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 4.00 GHz, 2x8GB G.Skill RipJaws V - DDR4 3280 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
KevShaw
Posts: 11
Joined: 15 May 2015, 08:17

Re: Compass - Measure angles and scale distances with your mouse

15 May 2015, 08:24

Great Idea. Wish I'd have thought of it!! This is something I can use for sure. Getting an error on line 046, call to nonexistent function.
User avatar
jNizM
Posts: 2508
Joined: 30 Sep 2013, 01:33
GitHub: jNizM
Contact:

Re: Compass - Measure angles and scale distances with your mouse

15 May 2015, 09:49

@KevShaw:
A: You need to include Gdip.ahk (Lib)
or
B: You use the version without extra lib Compass.ahk
[AHK] 1.1.30.03 x64 Unicode | [WIN] 10 Pro (Version 1903) x64 | [GitHub] Profile
Donations are appreciated if I could help you
KevShaw
Posts: 11
Joined: 15 May 2015, 08:17

Re: Compass - Measure angles and scale distances with your mouse

15 May 2015, 11:40

Alright, where could a copy of the Gdip.ahk (Lib) found?
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: Compass - Measure angles and scale distances with your mouse

15 May 2015, 13:38

KevShaw, Gdip is available here. Currently I recommend getting the "Gdip All" version, rename it to gdip.ahk, and place it in your Lib folder.

Good idea about the Gif joedf. You may have seen that I added one shortly after you suggested it.

Nice job with the alternate version jNizM. (Edit: DllCall("gdiplus.dll\GdipSetSmoothingMode", "Ptr", pGraphics, "Int", 4) should be -> DllCall("gdiplus.dll\GdipSetSmoothingMode", "Ptr", G, "Int", 4) though.)
vasili111
Posts: 747
Joined: 21 Jan 2014, 02:04
Location: Georgia

Re: Compass - Measure angles and scale distances with your mouse

16 May 2015, 01:20

Thanks for sharing! I think it will be useful for making mouse gestures.
DRAKON-AutoHotkey: Visual programming for AutoHotkey.
User avatar
jNizM
Posts: 2508
Joined: 30 Sep 2013, 01:33
GitHub: jNizM
Contact:

Re: Compass - Measure angles and scale distances with your mouse

18 May 2015, 07:45

Thx for the hint. Updated. I changed 1-2 small things since last time.
[AHK] 1.1.30.03 x64 Unicode | [WIN] 10 Pro (Version 1903) x64 | [GitHub] Profile
Donations are appreciated if I could help you
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: Compass - Measure angles and scale distances with your mouse

18 May 2015, 12:38

Update:
  • Incorporated translation to stand-alone Gdip thanks to jNizM - http://ahkscript.org/boards/viewtopic.p ... 860#p45570
  • Added new method, ToggleSnap, and adjusted existing method Measure. When Snap is on, the measurement will snap to horizontal/vertical when within +/-3 degrees. New default hotkey, Ctrl+F12, will toggle snap on/off.
(Quick Edit: fixed a divide-by-zero bug introduced by the new snap feature.)
FanaticGuru
Posts: 1359
Joined: 30 Sep 2013, 22:25

Re: Compass - Measure angles and scale distances with your mouse

19 May 2015, 17:54

Nice script. Useful for my job.

I added hotkeys to paste the last results from a measurement. Since you were already using F12 for toggle I went with Win+F12 paste decimal and Win+Alt+F12 paste architectural.

Not that it matters much but here is an example of how I do decimal feet to architectural measurements.

Code: Select all

DecFtToArch(Decimal,P:=16)
{
	Feet := Floor(Decimal), Decimal -= Feet, Inches := Floor(Decimal * 12), Decimal := Decimal * 12 - Inches
	RegExMatch(Decimal,"\.(\d+)0*", Match), N := Match1
	D := 10 ** StrLen(N)
	N := Round(N / D * P), D := P
	while X != 1
		X := GCD(N,D), N := N / X, D := D / X
	if (N = D)
		Inches ++, N := 0
	if (Inches = 12)
		Feet++, Inches := 0
	return Feet "'-" Inches (N and D ? " " Floor(N) "/" Floor(D):"")"""" 
}

GCD(A, B) 
{
    while B 
	   B := Mod(A|0x0, A:=B)
    return A
}
Here is a post that offer more options along the same vein:
Decimal2Fraction and Fraction2Decimal
The Highlighted to Fraction example is useful to convert measurements back and forth between decimal and architectural in other programs through the clipboard.

Also there are these functions about reducing terms in fractions:
Reduce Fraction Terms

Great job. If you ever decide to add to the script any more that would be great.

An option to leave the measurements displayed until erased might be nice. Multiply measurements would be nice but even if it was just one it would still be useful.

An option to measure areas.

An option to change the precision.

A GUI to choose options.

You get the idea. :bravo:

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts

AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon

[Function] Timer - Create and Manage Timers
User avatar
joedf
Posts: 7283
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: Compass - Measure angles and scale distances with your mouse

28 May 2015, 09:43

That's an awesome gif! :D
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 4.00 GHz, 2x8GB G.Skill RipJaws V - DDR4 3280 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]

Return to “Scripts and Functions”

Who is online

Users browsing this forum: Google [Bot] and 45 guests