Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

PinRotate (berechnet Koorninaten für eine Drehung um einen definierten Punkt)


  • Please log in to reply
8 replies to this topic
Dr_Holle
  • Members
  • 105 posts
  • Last active: Mar 26 2014 10:10 AM
  • Joined: 18 Dec 2012

PinRotate ist ein Script, mit dem man die Koordinaten für eine Drehung berechnen kann.

Entstanden ist das Script als ich "Overlays" drehen wollte.

Gdip bietet bereits solch eine Möglichkeit, allerdings habe ich es damit nicht geschafft Teile eine Bildes "außer mittig" zu drehen.

 

PinRotate macht nichts weiter als eine X- und eine Y- Position in Abhängigkeit eines definierten Drehpunktes zu drehen.

 

So, erst einmal der Code, ein Beispiel folgt weiter unten.

PinRotate.ahk

PinRotate(AddDeg,PosX,PosY,CenterPos*) ; Berechnet eine Drehposition um einen fixierten Punkt herum
{
	{ ; Header
	; #######################################################################################################################
	; # Name      : PinRotate.ahk
	; # Autor     : Dr_Holle
	; # Function  : Calculator for new coordinates when rotate around a fixed position
	; # 
	; # 
	; # Verwendung: PinRotate(zu_drehende_grad , Start-X-Position , Start-Y-Position [ , Drehpunkt-X-Position , Drehpunkt-Y-Position ] )
	; # 			Das Ergebnis wird als assoziatives Array zurück geliefert, mit den Schlüsseln "X" und "Y". Beispiel:
	; #	
	; # 			NewPos := PinRotate(45,150,150,100,100)
	; # 			MsgBox, % "Die neue X-Position ist " NewPos.X "`nDie neue Y-Position ist " NewPos.Y
	; #			MsgBox, % "X-Position = " PinRotate(-45,100,150,100,100)
	; # 	
	; # 			Die Angabe von CenterPosX und CenterPosY ist optional.
	; # 			Wird auf die Eingabe der beiden Parameter verzichtet, dann wird der letzte bekannt Wert verwendet.
	; # 	
	; #  			Die Ausgabe erfolgt im gleichen Format wie die Eingabe. Sind PosX und PosY Integer,
	; # 			dann erfolgt die Ausgabe ebenfalls als Ineger. Sollte mal eine höhere Genauigkeit benötigt werden
	; # 			sollten die Werte als Fließkommazahl eingegeben werden 
	; #			(z.B. PinRotate(75 , 100.000 , 80.2 , 150 , 150) ergibt X=204.481 , Y=83.638
	; # 			Die Ausgabe wird dann auf die gleiche Stellenanzahl gerundet, wie die Eingabe (Beispiel oben = 3 Stellen).
	; # 			Beide Werte werden auf die Anzahl hinter den Komma gerundet, wie der Wert mit dem meisten Nachkomma-Stellen.	
	; # 
	; # 
	; # 	
	; # Beispiele : PinRotate(-45,100,120) --> 
	; #			Berechnet die neue Position von x100,y120 bei einer Drehung von -45°
	; #			Wurde vorher noch kein Wert für CenterPosX und CenterPosY angegeben 
        ; #                     wird die Bildmitte als Drehpunkt verwendet,
	; #		        ansonsten wird der letzte bekannte Wert benutzt.
	; # 		PinRotate(20,60,90,350,500) --> 
	; #			Berechnet die neue Position von x60,y90 bei einer Drehung von 20°. 
	; #			Als Drehpunkt wird die Koordinate x350,y500 benutzt.
	; # 		PinRotate(20,80,90,"",650) --> 
	; #			Berechnet die neue Position von x80,y90 bei einer Drehung von 20°. 
	; #			Als Drehpunkt wird die Koordinate x350,y650 benutzt, da der letzte bekannte X-Wert 350 war.
	; # 		PinRotate(-95,145,36,20) ist das gleiche wie PinRotate(20,80,90,20,"") --> 
	; #			Berechnet die neue Position von x145,y36 bei einer Drehung von -95°. 
	; #			Als Drehpunkt wird die Koordinate x20,y650 benutzt, da der letzte bekannte Y-Wert 650 war.
	; # 		PinRotate(15,200,150,"",0) --> 
	; #			Berechnet die neue Position von x200,y150 bei einer Drehung von 15°. 
	; #			Als Drehpunkt wird die Koordinate x20,y0 benutzt, da der letzte bekannte X-Wert 20 war.	
	; #######################################################################################################################
	}
	static CenterPosX , CenterPosY
	AddDeg := (AddDeg >= 0) ? Mod(AddDeg, 360) : 360-Mod(-AddDeg, -360)
	if CenterPos[1] || (CenterPos[1] = 0)
		CenterPosX := CenterPos[1]
	if CenterPos[2] || (CenterPos[2] = 0)
		CenterPosY := CenterPos[2]
	if !CenterPosX && (CenterPosX != 0)
		CenterPosX := round(A_ScreenWidth / 2)
	if !CenterPosY && (CenterPosY != 0)
		CenterPosY := round(A_ScreenHeight / 2)

	; calculate source degree
	if (PosX > CenterPosX) ; 270,x° - 89,x°
	{
		if (CenterPosY > PosY) ; 270,x° - 359,x°
			Degree := 270 , ak := CenterPosY - PosY , gk := PosX - CenterPosX
		else ; 0° - 89,x°
			ak := PosX - CenterPosX , gk := PosY - CenterPosY
	}
	else ; 90° - 270°
	{
		if (PosY > CenterPosY) ; 90 - 179,x°
			Degree := 90 , ak := PosY - CenterPosY , gk := CenterPosX - PosX
		else ; 180-270°
			Degree := 180 , ak := CenterPosX - PosX , gk := CenterPosY - PosY
	}
	
	hy := Sqrt(ak*ak + gk*gk)
	Degree += ASin(gk/hy) * 180 / 3.141592653589793
	
	; calculate destination positions
	Degree := mod(Degree + AddDeg, 360)
	if (Degree < 90)
		NewX := CenterPosX + cos(Degree * (3.141592653589793 / 180)) * hy , NewY := CenterPosY + sin(Degree * (3.141592653589793 / 180)) * hy
	else if (Degree < 180)
		NewX := CenterPosX - sin((Degree - 90) * (3.141592653589793 / 180)) * hy , NewY := CenterPosY + cos((Degree - 90) * (3.141592653589793 / 180)) * hy
	else if (Degree < 270)
		NewX := CenterPosX - cos((Degree - 180) * (3.141592653589793 / 180)) * hy , NewY := CenterPosY - sin((Degree - 180) * (3.141592653589793 / 180)) * hy
	else
		NewX := CenterPosX + sin((Degree - 270) * (3.141592653589793 / 180)) * hy , NewY := CenterPosY - cos((Degree - 270) * (3.141592653589793 / 180)) * hy
	
	; if posX oder posY is float, return as float (with same digits)
	if InStr(posX,".") || InStr(posY,".")
	{
		digitsX := StrLen(PosX) - InStr(PosX,".",0,0) , digitsY := StrLen(PosY) - InStr(PosY,".",0,0)
		digits := digitsX>digitsY ? digitsX : digitsY 
		return Object("x", Round(NewX,3), "y", Round(NewY, 3))
	}
	else
		return Object("x", Round(NewX), "y", Round(NewY))
}

 

So, nun ein Beispiel, welches aus einer X/Y Koordinate, einer Grad-zahl und dem Drehpunkt (eine weitere X/Y Koordinate) eine Linie "im Kreis" zeichnet. Da PinRotate immer nur einzelne X/Y-Positionen errechnet sind für eine Linie 2 Koordinaten nötig. Da im Beispiel jedoch der Drehpunkt als 2. Koordinate verwendet wird reicht hier eine Koordinate.

Beispiel:

PinRotate-Test.ahk

#Include gdip.ahk
#Include PinRotate.ahk
OnExit, GuiClose

Width := Height := 400
pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmap(Width, Height)
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
gui, add, Picture, x0 y0 w400 h400 0xE hwndPRtest
Gui, Show, w400 h400, PinRotate-Test

G := Gdip_GraphicsFromImage(pBitmap)
Gdip_SetSmoothingMode(G, 2)
DrawBlack := Gdip_CreatePen(0xFF000000,1)
DrawRed := Gdip_CreatePen(0xFFFF0000,1)
FillGrey := Gdip_BrushCreateSolid(0xFFA0A0A0)
Gdip_FillRectangle(G, FillGrey, 0, 0, Width, Height) ; Hintergrund

PinX := 150 , PinY := 150 ; Mittel-Position (der fixierte Punkt, um den sich alles dreht ...also der "Egoist" ;-) )
PosX := 380 , PosY := 150
NewPos := PinRotate(0,PosX,PosY,PinX,PinY)
Gdip_DrawLine(G, DrawRed, NewPos.X, NewPos.Y, PinX, PinY) ; Grundlinie (0°) in rot zeichnen
; ein ebenfalls gültiger Aufruf:  xPosition := Gdip_DrawLine(G, DrawRed, NewPos.X, NewPos.Y, PinX, PinY).X
Loop, 359 ; die anderen 359 Linien (eine pro Grad) in schwarz zeichnen
{
	NewPos := PinRotate(a_index,PosX,PosY) ; a_index sind die Grad ...Jede Linie wird von der "Grundline" berechnet. Die Pin-Position ist nicht mehr nötig, die PinRotate sich diese gemerkt hat.
	Gdip_DrawLine(G, DrawBlack, NewPos.X, NewPos.Y, PinX, PinY)
}
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
SetImage(PRtest, hBitmap)
return

GuiClose:
  Gdip_DeleteBrush(DrawBlack)
  Gdip_DeleteBrush(DrawRed)
  Gdip_DeleteBrush(FillGrey)
  Gdip_DeleteGraphics(G)
  Gdip_DisposeImage(pBitmap)
  DeleteObject(hBitmap)
  Gdip_Shutdown(pToken)
  ExitApp
return

So sieht das dann aus:

pinrotateqxbui.jpg

 

 

Wenn man nun also eine Linie drehen will braucht man 2 Koordinaten. Man ruft also PinRotate für jede Koordinate extra auf. Das hat den Vorteil dass man unabhängig davon ist wieviele Koordinaten man benötigt (Linie = zwei Koordinaten , Kreis = eine Koordinate , usw.).

Da man in den Meisten Fällen "um einen zentralen Punkt" drehen will merkt sich PinRotate den letzten Drehpunkt (die Pin-Koordinaten). Sofern also bereits einmal eine die Pin-Koordinate angegeben wurde muss man für weitere Berechnungen keine Pin-Koordinaten mehr angeben, es sei denn man benötigt einen "anderen" Drehpunkt

 

 

Edit:

Hier ist mal ein weiteres Beispiel, an dem man sehen kann wie man das Script bei Linien einsetzt. ich hätte natürlich auch einfach ein Rechteck zeichnen können *, das wäre deutlich kompakter geworden als 4 Linien, aber ich denke als Beispiel ist das so besser geeignet.

* Edit: Nun muss ich mich selber korrigieren confused.png  . Ein Rechteck geht nicht ohne weiteres, da man ein Rechteck nicht schräg zeichnen kann, also funktioniert das nur mit den einzelnen Linien wink.png

#Include PinRotate.ahk
OnExit, GuiClose

Width := Height := 400
pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmap(Width, Height)
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
gui, add, Picture, x0 y0 w400 h400 0xE hwndPRtest
Gui, Show, w400 h400, PinRotate-Test

G := Gdip_GraphicsFromImage(pBitmap)
Gdip_SetSmoothingMode(G, 2)
DrawBlack := Gdip_CreatePen(0xFF000000,1)
DrawRed := Gdip_CreatePen(0xFFFF0000,1)
DrawBlue := Gdip_CreatePen(0xFF0000FF,1)
FillGrey := Gdip_BrushCreateSolid(0xFFA0A0A0)
Gdip_FillRectangle(G, FillGrey, 0, 0, Width, Height) ; Hintergrund

;Original-Grafik
Gdip_DrawLine(G, DrawRed, 50, 80, 250, 80) ; Oben
Gdip_DrawLine(G, DrawRed, 50, 220, 250, 220) ; Unten
Gdip_DrawLine(G, DrawRed, 50, 80, 50, 220) ; Links
Gdip_DrawLine(G, DrawRed, 250, 80, 250, 220) ; Rechts

; Gedreht mit PinRotate
Loop, 6
{
	Farbe := DrawBlack , Drehung := -15 * a_index , PinX := 150 , PinY := 150
	If (a_index > 3)
		Farbe := DrawBlue , Drehung := 25 * a_index , PinX := 50 , PinY := 220
	Oben1 := PinRotate(Drehung,50,80,PinX,PinY) , Oben2 := PinRotate(Drehung,250,80,PinX,PinY)
	Gdip_DrawLine(G, Farbe, Oben1.X, Oben1.Y, Oben2.X, Oben2.Y) ; Oben
	Unten1 := PinRotate(Drehung,50,220) , Unten2 := PinRotate(Drehung,250,220)
	Gdip_DrawLine(G, Farbe, Unten1.X, Unten1.Y, Unten2.X, Unten2.Y) ; Unten
	Links1 := PinRotate(Drehung,50,80) , Links2 := PinRotate(Drehung,50,220)
	Gdip_DrawLine(G, Farbe, Links1.X, Links1.Y, Links2.X, Links2.Y) ; Links
	Rechts1 := PinRotate(Drehung,250,80) , Rechts2 := PinRotate(Drehung,250,220)
	Gdip_DrawLine(G, Farbe, Rechts1.X, Rechts1.Y, Rechts2.X, Rechts2.Y) ; Rechts
}

hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
SetImage(PRtest, hBitmap)
return

GuiClose:
  Gdip_DeleteBrush(DrawBlack)
  Gdip_DeleteBrush(DrawRed)
  Gdip_DeleteBrush(FillGrey)
  Gdip_DeleteGraphics(G)
  Gdip_DisposeImage(pBitmap)
  DeleteObject(hBitmap)
  Gdip_Shutdown(pToken)
  ExitApp
return

 

...und das kommt dabei raus:

pinrotate4mnz4m.jpg

 

Das rote Rechteck wird in den ersten 3 Schleifendurchläufen um jeweils -15° um die Koordinate X150,Y150 gedreht und in schwarz dargestellt.

In den nächsten 3 Schleifendurchläufe wird das rote Rechteck jeweils um +25° um die Koordinate X50,Y220 (das entspricht die untere linke Ecke des roten Rechtecks) gedreht und in blau dargestellt.

Da a_index nun schon den Wert 4 hat wird also erst bei 100° begonnen.

 

Viel Spaß damit cool.png



strobo
  • Members
  • 359 posts
  • Last active: Mar 10 2015 08:13 PM
  • Joined: 19 Jun 2012

Hallo,

und Dank fürs Teilen.

Es kommt imho ja nur drauf an was hinten rauskommt, aber Drehungen kann man wesentlich kompakter coden. Ich hab mal ein Beispiel mit ein paar deiner Zusatzfunktionen gemacht (alledings mit ByRef statt mit return,object):

pinrotate(45,x:=1,y:=0,0,0,DontRound:=0)
msgbox,% x "`n" y
pinrotate(90,x:=0,y:=1)
msgbox,% x "`n" y
PinRotate(phi_in_deg,ByRef x,ByRef y,x0="",y0="",RoundResult=1)
    {
    static x00:=round(A_ScreenWidth / 2) , y00:=round(A_ScreenHeight / 2)
    if (x0!="")
        x00:=x0
    if (y0!="")
        y00:=y0
    dx:=x-x00
    dy:=y-y00
    phi:=phi_in_deg*atan(1)/45
    x:=x00+cos(phi)*dx-sin(phi)*dy
    y:=y00+sin(phi)*dx+cos(phi)*dy
    if (RoundResult)
        x:=round(x) , y:=round(y)
    }
 

Regards,
Babba

Dr_Holle
  • Members
  • 105 posts
  • Last active: Mar 26 2014 10:10 AM
  • Joined: 18 Dec 2012

Vielen Dank für die Antwort.

Ich habe das gerade mal getestet, aber das Ergebnis hat "leichte" Differenzen wink.png

 

Hier ist der Test-Code:

OnExit, GuiClose

Width := Height := 400
pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmap(Width, Height)
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
gui, add, Picture, x0 y0 w400 h400 0xE hwndPRtest
Gui, Show, w400 h400, PinRotate-Test

G := Gdip_GraphicsFromImage(pBitmap)
Gdip_SetSmoothingMode(G, 2)
DrawBlack := Gdip_CreatePen(0xFF000000,1)
DrawRed := Gdip_CreatePen(0xFFFF0000,1)
FillGrey := Gdip_BrushCreateSolid(0xFFA0A0A0)
Gdip_FillRectangle(G, FillGrey, 0, 0, Width, Height) ; Hintergrund

PinX := 150 , PinY := 150 ; Mittel-Position (der fixierte Punkt, um den sich alles dreht ...also der "Egoist" ;-) )
X := 380 , Y := 150
NewPos := PinRotate(0,X,Y,PinX,PinY)
Gdip_DrawLine(G, DrawRed, X, Y, PinX, PinY) ; Grundlinie (0°) in rot zeichnen
Loop, 359 ; die anderen 359 Linien (eine pro Grad) in schwarz zeichnen
{
	NewPos := PinRotate(a_index,X,Y) ; a_index sind die Grad ...Jede Linie wird von der "Grundline" berechnet. Die Pin-Position ist nicht mehr nötig, die PinRotate sich diese gemerkt hat.
	Gdip_DrawLine(G, DrawBlack, X, Y, PinX, PinY)
}
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
SetImage(PRtest, hBitmap)
return

GuiClose:
  Gdip_DeleteBrush(DrawBlack)
  Gdip_DeleteBrush(DrawRed)
  Gdip_DeleteBrush(FillGrey)
  Gdip_DeleteGraphics(G)
  Gdip_DisposeImage(pBitmap)
  DeleteObject(hBitmap)
  Gdip_Shutdown(pToken)
  ExitApp
return


PinRotate(phi_in_deg,ByRef x,ByRef y,x0="",y0="",RoundResult=1)
    {
    static x00:=round(A_ScreenWidth / 2) , y00:=round(A_ScreenHeight / 2)
    if (x0!="")
        x00:=x0
    if (y0!="")
        y00:=y0
    dx:=x-x00
    dy:=y-y00
    phi:=phi_in_deg*atan(1)/45
    x:=x00+cos(phi)*dx-sin(phi)*dy
    y:=y00+sin(phi)*dx+cos(phi)*dy
    if (RoundResult)
        x:=round(x) , y:=round(y)
    }

...und hier das Ergebnis:

pinrotate2gza3h.jpg



strobo
  • Members
  • 359 posts
  • Last active: Mar 10 2015 08:13 PM
  • Joined: 19 Jun 2012

Hi,

das liegt an ByRef-artigkeit von X,Y.

wenn man

NewPos := PinRotate(a_index,X,Y) ; a_index sind die Grad ...Jede Linie wird von der "Grundline" berechnet. Die Pin-Position ist nicht mehr nötig, die PinRotate sich diese gemerkt hat.

durch

PinRotate(1,X,Y)

ersetzt, siehts schon wieder besser aus. Ich hab das Bsp. mit ByRef gemacht um mich nicht mit Formatierungen rumzuschlagen, ByRef kann je nach Aufgabe und Herangehensweise aber auch nerven.;)


Regards,
Babba

Dr_Holle
  • Members
  • 105 posts
  • Last active: Mar 26 2014 10:10 AM
  • Joined: 18 Dec 2012

Ne, daran liegt das nicht.

Teste das doch mal selber, das letzte Beispielscript ist ja direkt lauffähig (musst nur noch gdip includen, wenn es nicht in deiner Standard-Lib ist).



strobo
  • Members
  • 359 posts
  • Last active: Mar 10 2015 08:13 PM
  • Joined: 19 Jun 2012

Doch ist getestet, hier nochmal als ganzes

Spoiler

Regards,
Babba

Dr_Holle
  • Members
  • 105 posts
  • Last active: Mar 26 2014 10:10 AM
  • Joined: 18 Dec 2012

OK, das sieht schon anders auswink.png .

Ich hatte nicht gewusst dass ich in der Schleife auch "1" statt a_index schreiben sollte.

Nun wird mir jedoch klar dass deine Funktion anders vorgeht als meine. Bei meiner Funktion wird immer von der angegebenen Position um x° gedreht und deine berechnet im x° von der "letzten Position".

 

Ich werde den ersten Beitrag mal um ein weiteres Beispiel ergänzen, damit man besser erkennt wofür die Funktion eigentlich gedacht war.

 

Irgendwie berechnet deine Funktion etwas "ungenauer", das muss wohl an den Tangens-Umrechnungen liegen (nehme ich an).

Hier ist mal das Resultat von deinem letzten Script:

pinrotate3irxh6.jpg

Achte mal auf die Ränder, die sind leicht "wellig" und der Abstand zwischen den Linien ist "Blockweise" unterschiedlich groß. Dass die Abstände nie gleich sein können ist ja klar, weil die Pixel ja nicht im Kreis angeordnet sind, aber warum so "Blockweise" ? Die Formel selber scheint zu stimmen, da wird von AHK intern wohl zu stark gerundet, oder so was in der Art.

 

Aber kompakter ist deine Funktion auf jeden Fall. Wen man dein Code möglichst klein halten will ist das eine gute Alternative. Kommt aber auch immer auf den Einsatzzweck an. Siehe neues Beispiel im ersten Beitrag, das dürfte mit deinem aktuellen Code nicht möglich sein (was jedoch lediglich eine Anpassungs-Sache wäre).

 



strobo
  • Members
  • 359 posts
  • Last active: Mar 10 2015 08:13 PM
  • Joined: 19 Jun 2012

Die Abweichung kommt von den Rundungsfehlern, die sich X,Y in der loop,359 einhandeln.

Wenn man

 

Loop, 359 ; die anderen 359 Linien (eine pro Grad) in schwarz zeichnen

{

    NewPos := PinRotate(a_index,X,Y) ; a_index sind die Grad ...Jede Linie wird von der "Grundline" berechnet. Die Pin-Position ist nicht mehr nötig, die PinRotate sich diese gemerkt hat.

    Gdip_DrawLine(G, DrawBlack, X, Y, PinX, PinY)

}

durch

 

fixX:=X

fixY:=Y

Loop, 359

{

    PinRotate(a_index,X:=fixX,Y:=fixY)

    Gdip_DrawLine(G, DrawBlack, X, Y, PinX, PinY)

}

ersetzt, ist das abgestellt. Hier wär "return,Coord_Object" aber handlicher als "ByRef", darum geht es aber nicht bei dieser PinRotate Funktion.happy.png

Edit:

Am bisher gleichmässigstens wird das Bild, wenn man im Spoiler von #6 einfach NUR die round-Funktion deaktiviert, d.h. dort NUR

PinRotate(1,X,Y)

durch

PinRotate(1,X,Y,,,0)

ersetzt, denn offenbar beherrscht Gdip_DrawLine "subpixel precision" (dafür gibts bestimmt bessere Bezeichnungen;)!

(Ergänzung: X,Y sacken sich auch hier in der loop Rundungsfehler auf, die sind aber jetzt (jeweils) grob im Bereich der AHK-Rechengenauigkeit, wohingegen sie vorher (jeweils) grob im Bereich +-0.5 waren.)


Regards,
Babba

geob
  • Members
  • 2 posts
  • Last active: Oct 18 2014 10:10 AM
  • Joined: 30 Oct 2012

Hier noch ein Beispiel.

 

; Tested with AutoHotkey Version v1.1.16.05
;
#NoEnv
#SingleInstance force
 
; #include GDIP.ahk ; remove comment, if GDIP is not in the standard library
 
WM_PAINT = 0x0F
WM_WINDOWPOSCHANGED = 0x47
OnMessage(WM_WINDOWPOSCHANGED, "MSGH")
OnMessage(WM_PAINT, "MSGH")
; OnMessage(0x201, "WM_LBUTTONDOWN")
 
OnExit, GuiClose
 
If !pToken := Gdip_Startup()
{
  MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
  ExitApp
}
 
;============================
;;-- Größe der Zeichenfläche
;============================
Width  := 400
Height := 400
 
hTB := 20  ; Höhe für Text und Button-Zeile
 
; Gui, 1:-Caption
; Gui, 1:+Resize +0x300000  ; WS_VSCROLL | WS_HSCROLL
Gui, 1:Color, D0D0D0
Gui, 1:Add, Picture,  % "x"0    " y"0            " w"Width " h"Height-hTB " hwndPRpict1 vPRpicture1",
Gui, 1:Add, Button,   % "x"0    " y"Height-hTB   " w"70    " h"hTB " g_RotateLine   vPRbutton1"  , Rotate Line
Gui, 1:Add, Button,   % "xp+"70 " yp"            " w"80    " h"hTB " g_RotateFrame  vPRbutton2"  , Rotate Frame
Gui, 1:Add, Checkbox, % "xp+"85 " yp"            " w"65    " h"hTB " g_DrawStepwise vPRcheckbox1", Stepwise
Gui, 1:Add, Button,   % "xp+"65 " yp"            " w"40    " h"hTB " g_ResetCanvas  vPRbutton3"  , Reset
Gui, 1:Add, Text,     % "xp+"40 " y"Height-hTB+3 " w"40    " h"hTB " -Wrap Right    vPRtext1"    , Angle°:
Gui, 1:Add, Edit,     % "xp+"45 " y"Height-hTB   " w"25    " h"hTB " -Wrap Center   vPRedit1"    ,
Gui, 1:Add, Text,     % "xp+"30 " y"Height-hTB+3 " w"30    " h"hTB " -Wrap Right    vPRtext2"    , Steps:
Gui, 1:Add, Edit,     % "xp+"35 " y"Height-hTB   " w"25    " h"hTB " -Wrap Center   vPRedit2"    ,
Gui, 1:Show, % "w"Width " h"Height, PinRotate-Test
; WinSet, Transparent, 200, PinRotate-Test
ControlGetPos, pictX, pictY, pictW, pictH,, ahk_id %PRpict1%
 
;=======================================================================
; Mittel-Position (der fixierte Punkt, um den sich alles dreht ...)
;=======================================================================
PinX := pictW//2 , PinY := pictH//2
MidX := PinX , MidY := PinY
 
;======================================
;;-- Prepare 'canvas' to work upon
;======================================
hdc_PRpict1 := GetDC(PRpict1)
hbm := CreateDIBSection(pictW, pictH)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)
 
gosub initColors
gosub drawBackground
gosub drawCoord
gosub initLine
gosub initFrame
; gosub _ResetCanvas
gosub updateCanvas
return
 
;=== Line ==============================================================
initLine:
  DISTANCE  := pictW//2-40  ; Länge der Linie
  DRAW_ANGLE := 0    ; Startwinkel der ersten Linie (0° = Ost)
  PEN_COLOR := c_White
  STEP := 0
  x1L := MidX, y1L := MidY
  ; Linienlänge und Endpunkt berechnen
  polar(x1L,y1L, dtr(DRAW_ANGLE), DISTANCE, x2L,y2L)
  ; Linie um Winkel 'DRAW_ANGLE' rotieren
  gosub rotateLinePoint
  gosub drawLine
return
 
rotateLinePoint:
  Loop, 2
  {
    i := A_Index
    rotatePoint(x%i%L,y%i%L, PinX,PinY, DRAW_ANGLE, ROT_X%i%L,ROT_Y%i%L)
  }
return
 
drawLine:
  Gdip_SetSmoothingMode(G, 4)
  DrawPen := Gdip_CreatePen(PEN_COLOR, 1)
  DllCall("gdiplus\GdipSetPenDashStyle", "Uint", DrawPen, "Int", 0)
  Gdip_DrawLine(G, DrawPen, ROT_X1L, ROT_Y1L, ROT_X2L, ROT_Y2L)
  Gdip_DeletePen(DrawPen)
return
 
;=== Frame =============================================================
initFrame:
  ; Anzahl Rahmenpunkte
  points := 4
 
  ; Rahmen-Kantenlängen
  d1 := 50     ; rechts u. links
  d2 := 70     ; oben u. unten
 
  ; Rahmen-Winkel in Grad
  a1 := 10        ; Winkel für Linie 'x1,y1' - 'x2,y2'
  a2 := a1 + 90   ; Winkel für Linie 'x2,y2' - 'x3,y3'
  a3 := a2 + 90   ; Winkel für Linie 'x3,y3' - 'x4,y4'
 
  PinD := 50 ; Entfernung 'PinX,PinY' - Rahmenpunkt unten rechts
  PinA := 5   ; Winkel 'PinX,PinY' - Rahmenpunkt unten rechts (0°=Ost, 90°=Nord)
 
  ; Rahmenpunkte berechnen
  polar(MidX,MidY, dtr(PinA), PinD, x1F,y1F)   ; 1. Pt. unten rechts
  polar(x1F,y1F, dtr(a1), d1, x2F,y2F)         ; 2. Pt. oben rechts
  polar(x2F,y2F, dtr(a2), d2, x3F,y3F)         ; 3. Pt. oben links
  polar(x3F,y3F, dtr(a3), d1, x4F,y4F)         ; 4. Pt. unten links
 
  ROTATION_LINE_LENGTH_F := PinD-10
  DRAW_ANGLE := 0
  PEN_COLOR := c_White
  STEP := 0
 
  gosub rotateFramePoints
  gosub drawFrame
return
 
rotateFramePoints:
  Loop, % points
  {
    i := A_Index
    rotatePoint(x%i%F,y%i%F, PinX,PinY, DRAW_ANGLE, ROT_X%i%F,ROT_Y%i%F)
  }
return
 
drawFrame:
  Gdip_SetSmoothingMode(G, 4)
  DrawPen := Gdip_CreatePen(PEN_COLOR, 1)
  Loop, % points
  {
    i := A_Index
    ii := (i = points) ? 1 : i + 1
    Gdip_DrawLine(G, DrawPen, ROT_X%i%F,ROT_Y%i%F, ROT_X%ii%F,ROT_Y%ii%F)
  }
  Gdip_DeletePen(DrawPen)
return
 
drawFrameExtent:
  xmin := ROT_X1F, ymin := ROT_Y1F
  xmax := ROT_X1F, ymax := ROT_Y1F
  Loop, % points
  {
    i := A_Index
    xmin := (ROT_X%i%F < xmin) ? ROT_X%i%F : xmin
    ymin := (ROT_Y%i%F < ymin) ? ROT_Y%i%F : ymin
    xmax := (ROT_X%i%F > xmax) ? ROT_X%i%F : xmax
    ymax := (ROT_Y%i%F > ymax) ? ROT_Y%i%F : ymax
  }
  DrawPen := Gdip_CreatePen(c_Extent, 1)
  ; DllCall("gdiplus\GdipSetPenDashStyle", "Uint", DrawPen, "Int", 0)
  ; DllCall("gdiplus\GdipPenSetAlignment", "Uint", DrawPen, "Int", 0)
  Gdip_DrawLine(G, DrawPen, xmin,ymin, xmax,ymin)  ; oben
  Gdip_DrawLine(G, DrawPen, xmax,ymin, xmax,ymax)  ; links
  Gdip_DrawLine(G, DrawPen, xmax,ymax, xmin,ymax)  ; unten
  Gdip_DrawLine(G, DrawPen, xmin,ymax, xmin,ymin)  ; rechts
  Gdip_DeletePen(DrawPen)
return
 
;=======================================================================
; Koordinatensystem zeichnen
drawCoord:
  Gdip_SetSmoothingMode(G, 4)
  DrawPen := Gdip_CreatePen(c_Coord, 3)
  DllCall("gdiplus\GdipSetPenDashStyle", "Uint", DrawPen, "Int", 2)
  DllCall("gdiplus\GdipSetPenEndCap", "Uint", DrawPen, "Int",0x14) ; Arrow Cap
 
  angleList := "0|90|180|270"  ; Ost|Nord|West|Süd
  subtractionX := 10
  subtractionY := 10
  pictW2 := pictW - subtractionX
  pictH2 := pictH - subtractionY
  x0   := pictW2 , y0   := PinY
  x90  := PinX   , y90  := subtractionY
  x180 := subtractionX, y180 := PinY
  x270 := PinX   , y270 := pictH2
  Loop, parse, angleList,|
    Gdip_DrawLine(G, DrawPen, PinX,PinY, x%A_LoopField%, y%A_LoopField%)
  Gdip_DeletePen(DrawPen)
 
  ;=================================================
  ;;-- Raster zeichnen
  ;=================================================
  gridX := 25
  gridY := gridX
  ;;-- Raster-Nullpunkt
  gridX0 := PinX, gridY0 := PinY
 
  ;====================================
  ; Quadrant 1 (oben rechts) 0-90°
  ;====================================
  x := gridX0, gx := 0
  while (x < pictW2)
  {
    x++, gx++
    if (mod(gx, gridx) = 0)
    {
      y := gridY0, gy := 0
      while (y > subtractionY)
      {
        y--, gy++
        if (mod(gy, gridY) = 0)
          drawPoint(G, x, y, c_Coord, 1)
      }
    }
  }
  ;====================================
  ; Quadrant 2 (oben links) 90-180°
  ;====================================
  x := gridX0, gx := 0
  while (x > subtractionX)
  {
    x--, gx++
    if (mod(gx, gridX) = 0)
    {
      y := gridY0, gy := 0
      while (y > subtractionY)
      {
        y--, gy++
        if (mod(gy, gridY) = 0)
          drawPoint(G, x, y, c_Coord, 1)
      }
    }
  }
  ;====================================
  ; Quadrant 3 (unten links) 180-270°
  ;====================================
  x := gridX0, gx := 0
  while (x > subtractionX)
  {
    x--, gx++
    if (mod(gx, gridX) = 0)
    {
      y := gridY0, gy := 0
      while (y < pictH2)
      {
        y++, gy++
        if (mod(gy, gridY) = 0)
          drawPoint(G, x, y, c_Coord, 1)
      }
    }
  }
  ;====================================
  ; Quadrant 4 (unten rechts) 270-360°
  ;====================================
  x := gridX0, gx := 0
  while (x < pictW2)
  {
    x++, gx++
    if (mod(x, gridX) = 0)
    {
      y := gridY0, gy := 0
      while (y < pictH2)
      {
        y++, gy++
        if (mod(gy, gridY) = 0)
          drawPoint(G, x, y, c_Coord, 1)
      }
    }
  }
return
 
drawText:
  Gdip_SetSmoothingMode(G, 1)
 
  ; Textfeld löschen
  Gdip_SetCompositingMode(G, 1) ; set to overdraw
  pBrush := Gdip_BrushCreateSolid(c_Background)
  Gdip_FillRectangle(G, pBrush, xMSG,yMSG, wMSG, hMSG)
  Gdip_DeleteBrush(pBrush)
 
  ; Text schreiben
  if MSG
  {
    Gdip_SetCompositingMode(G, 0) ; switch off overdraw
    drawText(G, MSG, 14, "Left", PEN_COLOR, "Arial", xMSG,yMSG, wMSG, hMSG)
  }
return
 
; Hintergrund zeichnen
drawBackground:
  pBrush := Gdip_BrushCreateSolid(c_Background)
  Gdip_FillRectangle(G, pBrush, 0, 0, pictW, pictH)
  Gdip_DeleteBrush(pBrush)
return
 
initColors:
  A := "0xFF" ; transparity byte
  c_0    := A . "FFFFFF"  ; White
  c_90   := A . "5959ff"  ; Blue
  c_270  := A . "48ff48"  ; Green
  c_180  := A . "FFFF00"  ; Yellow
  c_360  := A . "ff5b5b"  ; Red
  c_Fuchsia    := A . "FF00FF"  ; Lila
  c_Black      := A . "000000"  ; Black
  c_White      := A . "FFFFFF"  ; White
  c_Background := A . "505050"  ; Gray
  c_Extent     := A . "AFFFFF"
  c_Coord      := A . "000000"
 
  SetFormat, IntegerFast, H
  c_Extent &= 0xFFFFFF   ; get rid of transparency byte
  c_Coord  &= 0xFFFFFF
  c_Extent += 0x55000000 ; set new transparency byte
  c_Coord  += 0xCC000000
  SetFormat, IntegerFast, D
return
 
;#######################################################################
_ROTATE_STEPWISE_:
  WORKING := True
 
  ANGLE := DRAW_ANGLE + ROTATION_ANGLE
 
  ; Farbe für 0°, 90°, 180°, 270°, 360°
  If ANGLE in 0,90,180,270,360
    PEN_COLOR := c_%ANGLE%
  else
  {
    If (STEP = ROTATION_STEPS) or (STEP = ROTATION_STEPS)
      PEN_COLOR := c_Fuchsia  ; Farbe für letzten Schritt
    else   ; Farbe für laufenden Schritt
      PEN_COLOR := c_White
  }
 
  if (DRAW = "Line")
  {
    if (STEP = 0)  ; clear Canvas
    {
      gosub drawBackground
      gosub drawCoord
      gosub initLine
    }
    DRAW_ANGLE += ROTATION_ANGLE
    gosub rotateLinePoint
    gosub drawLine
 
    MSG := "Step " ++STEP "/" ROTATION_STEPS
  }
  else if (DRAW = "Frame")
  {
    if (STEP = 0)  ; clear Canvas
    {
      gosub drawBackground
      gosub drawCoord
      gosub initFrame
    }
    DRAW_ANGLE += ROTATION_ANGLE
    gosub rotateFramePoints
    gosub drawFrame
 
    ; Drehwinkel-Linie zeichnen
    PEN_COLOR2 := PEN_COLOR
    PEN_COLOR -= 0xCC000000
    ROT_X1L := PinX , ROT_Y1L := PinY
    polar(PinX,PinY, dtr(DRAW_ANGLE), ROTATION_LINE_LENGTH_F, ROT_X2L, ROT_Y2L)
    gosub drawLine
 
    drawPoint(G, ROT_X1F, ROT_Y1F, PEN_COLOR, 4.5*2)
    PEN_COLOR := PEN_COLOR2
 
    MSG := "Step " ++STEP "/" ROTATION_STEPS
  }
  ; Step-Text, Textposition für MSG
  xMSG := 5 , yMSG := 5 , wMSG := 100 , hMSG := 20
  gosub drawText
 
  ; Winkel-Text
  ANGLE := round(rtd(angleFix(dtr(ANGLE))),0)
  MSG := "Angle " . ANGLE . "°"
  ; Textposition für MSG
  xMSG := 5 , yMSG := 30 , wMSG := 100 , hMSG :=20
  gosub drawText
 
  gosub updateCanvas
  WORKING := False
return
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_ROTATE_:
  STOP_WORKING := False
  WORKING := True
  Loop, % ROTATION_STEPS
  {
    if STOP_WORKING
      break
 
    ; clear Canvas
    if (STEP = 0) and (STEP = 0) and (A_Index = 1)
    {
      gosub drawBackground
      gosub drawCoord
      if (DRAW = "Line")
        gosub initLine
      if (DRAW = "Frame")
        gosub initFrame
    }
 
    ANGLE := DRAW_ANGLE + ROTATION_ANGLE
 
    ; Farbe für 0°, 90°, 180°, 270°, 360°
    If ANGLE in 0,90,180,270,360
      PEN_COLOR := c_%ANGLE%
    else
    {
      If (A_Index = ROTATION_STEPS)
        PEN_COLOR := c_Fuchsia  ; Farbe für letzten Schritt
      else   ; Farbe für den laufenden Schritt
        PEN_COLOR := c_White
    }
 
    if (DRAW = "Line")
    {
      DRAW_ANGLE += ROTATION_ANGLE
      gosub rotateLinePoint
      gosub drawLine
    }
    else if (DRAW = "Frame")
    {
      DRAW_ANGLE += ROTATION_ANGLE
      gosub rotateFramePoints
      gosub drawFrame
 
      ; Drehwinkel-Linie zeichnen
      PEN_COLOR2 := PEN_COLOR
      PEN_COLOR -= 0xCC000000
      ROT_X1L := PinX , ROT_Y1L := PinY
      polar(PinX,PinY, dtr(DRAW_ANGLE), ROTATION_LINE_LENGTH_F, ROT_X2L, ROT_Y2L)
      gosub drawLine
 
      drawPoint(G, ROT_X1F, ROT_Y1F, PEN_COLOR, 4.5*2)
      PEN_COLOR := PEN_COLOR2
 
      if (A_Index = ROTATION_STEPS)
        gosub drawFrameExtent
    }
 
    ; Winkel-Text setzen
    ANGLE := round(rtd(angleFix(dtr(ANGLE))),0)
    MSG := "Angle " . ANGLE . "°"
    ; Textposition
    xMSG := 5 , yMSG := 30 , wMSG := 100 , hMSG :=20
    gosub drawText
 
    ; Step-Text
    MSG := "Step " A_Index "/" ROTATION_STEPS
    ; Textposition für MSG
    xMSG := 5 , yMSG := 5 , wMSG := 100 , hMSG := 20
    gosub drawText
 
    gosub updateCanvas
  }
return
;#######################################################################
 
_RotateLine:
  if WORKING or (DRAW = "Frame")
    return
  DRAW := "Line"
  ROTATION_ANGLE := 1  ; Drehwinkel pro Schritt
  ROTATION_STEPS := 360     ; Anzahl Drehungen
 
  GuiControlGet, PRedit1Val,, PRedit1
  if PRedit1Val
    ROTATION_ANGLE := PRedit1Val
  else
    GuiControl,, PRedit1, %ROTATION_ANGLE%
 
  GuiControlGet, PRedit2Val,, PRedit2
  if PRedit2Val
    ROTATION_STEPS := PRedit2Val
  else
    GuiControl,, PRedit2, %ROTATION_STEPS%
 
  gosub _DrawStepwise
  If DRAW_STEPWISE
  {
    if (STEP < ROTATION_STEPS)
      gosub _ROTATE_STEPWISE_
  }
  else
  {
    ROTATION_STEPS -= STEP
    gosub _ROTATE_
  }
return
 
_RotateFrame:
  if WORKING or (DRAW = "Line")
    return
  DRAW := "Frame"
  ROTATION_ANGLE := 30  ; Drehwinkel pro Schritt
  ROTATION_STEPS := 12        ; Anzahl Drehungen
 
  GuiControlGet, PRedit1Val,, PRedit1
  if PRedit1Val
    ROTATION_ANGLE := PRedit1Val
  else
    GuiControl,, PRedit1, %ROTATION_ANGLE%
 
  GuiControlGet, PRedit2Val,, PRedit2
  if PRedit2Val
    ROTATION_STEPS := PRedit2Val
  else
    GuiControl,, PRedit2, %ROTATION_STEPS%
 
  gosub _DrawStepwise
  If DRAW_STEPWISE
  {
    if (STEP < ROTATION_STEPS)
      gosub _ROTATE_STEPWISE_
  }
  else
  {
    ROTATION_STEPS -= STEP
    gosub _ROTATE_
  }
return
 
_DrawStepwise:
  gui,submit,nohide
  DRAW_STEPWISE := PRcheckbox1
return
 
_ResetCanvas:
  ANGLE := False
  DRAW := False
  STEP := 0
  STOP_WORKING := True
  WORKING := False
 
  gosub drawBackground
  gosub drawCoord
  gosub initLine
  gosub initFrame
 
  MSG := "'Ctrl+LButton' to set different Rotation-Point"
  ; Textposition für MSG
  xMSG := 5 , yMSG := 5 , wMSG := Width-10 , hMSG := 20
  PEN_COLOR := c_White
  gosub drawText
 
  gosub updateCanvas
  GuiControl,, PRedit1,
  GuiControl,, PRedit2,
return
 
updateCanvas:
  Critical
  BitBlt(hdc_PRpict1, 0, 0, Width, Height, hdc, 0, 0, 0x00CC0020) ; SRCCOPY
  Critical, off
Return
 
GuiClose:
  ; Select the object back into the hdc
  SelectObject(hdc, obm)
  ; Now the bitmap may be deleted
  DeleteObject(hbm)
  ; Also the device context related to the bitmap may be deleted
  DeleteDC(hdc)
  ; The graphics may now be deleted
  Gdip_DeleteGraphics(G)
 
  ; ...and gdi+ may now be shutdown
  Gdip_Shutdown(pToken)
  ExitApp
return
 
; Function midpt
; Calculate middle point 'Mx,My' of line 'x1,y1 - x2,y2'
; midpt(x1,y1, x2,y2, Byref Mx,Byref My)
; {
  ; Mx := (x1 + x2) / 2
  ; My := (y1 + y2) / 2
; }
 
; Function angle
; Calculate angle of line 'x1,y1 - x2,y2' in radians
; angle(x1,y1, x2,y2)
; {
  ; a := ACos((x2-x1)/((x2-x1)**2+(y2-y1)**2)**0.5)
  ; If (y2-y1) < 0
    ; a := 6.28319 - a
  ; Return a
; }
 
;{ ========= Functions for Rotation =====================
; Function rotatePoint
; Rotate point about z-axis.
; Calculate target point 'Tx,Ty' of point 'x,y' from rotation
; point 'rotX,rotY' through an angle 'rotAng' (rotAng in degrees).
rotatePoint(x,y, rotX,rotY, rotAng, Byref Tx,Byref Ty)
{
  a := angleDeg(rotX,rotY, x,y)
  d := distance(rotX,rotY, x,y)
  polar(rotX, rotY, dtr(a + rotAng), d, Tx, Ty)
}
; Function distance
; Calculate distance between 2 points
distance(x1,y1, x2,y2)
{
  return ((x2-x1)**2+(y2-y1)**2)**0.5
}
; Function angleDeg
; Calculate angle of line 'x1,y1 - x2,y2' in degrees
angleDeg(x1,y1 , x2,y2)
{
  ; Richting holen (Anwendung des Skalarproduktes zur Winkelberechnung)
  a := ACos( (x2-x1) / ((x2-x1)** 2 + (y2-y1)** 2)** 0.5 ) * 57.29578
  If (y2-y1) > 0  ; Den Unteren Halbkreis berechnen
    a := 360 - a
  Return a
}
; Function polar
; Calculate target point 'Tx,Ty' from base point 'x,y'
; in distance 'd' through an angle 'a' (a in radians).
polar(x,y, a, d, Byref Tx,Byref Ty)
{
  Tx := x + d * Cos(a)
  Ty := y - d * Sin(a)
}
; Convert a degree value to radians
dtr(deg)
{
  Return deg * (3.141592653589793 / 180)
}
; Convert a radian value to degrees
rtd(rad)
{
  Return rad * (180 / 3.141592653589793)
}
 
; Force the angle (radian) 0 <= ang < 2pi
; Reduces to [0°, 360°]
; Takes an angle in radians and reduces it to less than 2pi if ang>=2pi
angleFix(ang)
{
  Static pi2 := 6.28319  ; 6.28319 = pi * 2
  if (ang < 0)
    ang += pi2
  else if (ang > pi2)
    ang -= pi2
  Return ang
}
;} ======================================================
 
drawPoint(G, X, Y, C, R:=0.5, LW:=1, Fill:=0)
{
  ; C  = Color
  ; X  = X coordinate
  ; Y  = Y coordinate
  ; X2 = Second X coordinate
  ; Y2 = Second Y coordinate
  ; R  = Radius
  ; LW  = Linewidth
 
  X2 := X
  Y2 := Y
  Xpos := X + R / 2
  Ypos := Y + R / 2
  Width  := X2 - X - R
  Height := Y2 - Y - R
  if Fill
  {
    pBrush := Gdip_BrushCreateSolid©
    Gdip_FillEllipse(G, pBrush, Xpos, Ypos, Width, Height)
    Gdip_DeleteBrush(pBrush)
  }
  else
  {
    pPen := Gdip_CreatePen(C, LW)
    Gdip_DrawEllipse(G, pPen, Xpos, Ypos, Width, Height)
    Gdip_DeletePen(pPen)
  }
}
 
drawText(G, str, size, options, colr, font, x, y, w, h, measure=0)
{
  ; Align options: Near,Left,Centre,Center,Far,Right,
  ;                Top,Up,Bottom,Down,vCentre,vCenter
  ; Style options: Regular,Bold,italic,Bolditalic,underline,strikeout
  colr := "c" . RegExReplace(colr, "^0x", "")
  options2 = X%x% Y%y% S%size% %options% %colr%
  Return Gdip_TextToGraphics(G, str, options2, font, w, h, measure)
}
 
MSGH(wParam, lParam)
{
  Gosub, updateCanvas
}
 
;=======================================================================
#IfWinActive, PinRotate-Test
 
  ; Uncomment for quick reload while programing
  ; #r::
    ; Reload
  ; return
 
  ^LButton::
    ;;-- Drehpunkt versetzen
    GuiControlGet, PRedit1Val,, PRedit1
    GuiControlGet, PRedit2Val,, PRedit2
    CoordMode, Mouse, Client
    MouseGetPos, mx, my
    PinX := mx, PinY := my
    gosub _ResetCanvas
    GuiControl,, PRedit1, %PRedit1Val%
    GuiControl,, PRedit2, %PRedit2Val%
  return
 
  #s::
    Path := A_ScriptDir . "\"
    if (DRAW = "Line")
      Name := "PinRotate-Test-Line"
    else if (DRAW = "Frame")
      Name := "PinRotate-Test-Frame"
    else
      Name := "PinRotate-Test"
    Ext  := ".png"
    outFile := Path . Name . Ext
    pBitmapHBM := Gdip_CreateBitmapFromHBITMAP(hbm)
    Gdip_SaveBitmapToFile(pBitmapHBM, outFile)
    ; The bitmap can be deleted
    Gdip_DisposeImage(pBitmapHBM)
    MsgBox, % outFile . " gespeichert!"
  return
#IfWinActive
 
; WM_LBUTTONDOWN()
; {
  ; PostMessage, 0xA1, 2,,, A ;LeftClick to drag around
; }
 
Esc::
  ExitApp