It is based on this video by IronMortality:
Rotate an acute angle in MS Paint - YouTube
https://www.youtube.com/watch?v=wUKMIi0mInc
The script has been tested on MS Paint (Windows XP and Windows 7 versions).
Code: Select all
;==================================================
;MS Paint: rotate by scaling/shearing (stretching/skewing)
;tested on MS Paint (Windows XP version)
;tested on MS Paint (Windows 7 version)
;2 approaches are available, use q or w to trigger
;==================================================
;NOTES: DEFINITION
;Transformation matrix - Wikipedia
;https://en.wikipedia.org/wiki/Transformation_matrix#Examples_in_2D_computer_graphics
;Rotation matrix - Wikipedia
;https://en.wikipedia.org/wiki/Rotation_matrix
;anticlockwise rotation:
;R = [cos(x), -sin(x)]
; [sin(x), cos(x)]
;clockwise rotation:
;R = [ cos(x), sin(x)]
; [-sin(x), cos(x)]
;==================================================
;NOTES: APPROACH 1
;approach 1 based on:
;[demonstrated in video]
;Rotate an acute angle in MS Paint - YouTube
;https://www.youtube.com/watch?v=wUKMIi0mInc
;IronMortality's Blog: MS Paint Rotate Notes
;http://ironmortal.blogspot.co.uk/2015/05/ms-paint-rotate-notes.html
;Step 1: Horizontal skew by 60 degrees
;Step 2: Vertical stretch by 400%
;Step 3: Vertical skew by -60 degrees
;Step 4: Horizontal and vertical stretch by 50%
;based on calculations by IronMortality:
;(note: matrix for clockwise rotation)
;R = [1, tan(x)][1/cos^2(x), 0][ 1 , 0][cos(x), 0 ]
; [0, 1 ][ 0 , 1][-tan(x), 1][ 0 , cos(x)]
;R = [ cos(x), sin(x)]
; [-sin(x), cos(x)]
;simplify {{1,tan(x)},{0,1}} . {{1/cos^2(x),0},{0,1}} . {{1,0},{-tan(x),1}} . {{cos(x),0},{0,cos(x)}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=simplify+%7B%7B1,tan(x)%7D,%7B0,1%7D%7D+.+%7B%7B1%2Fcos%5E2(x),0%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7B-tan(x),1%7D%7D+.+%7B%7Bcos(x),0%7D,%7B0,cos(x)%7D%7D
;does {{1,tan(x)},{0,1}} . {{1/cos^2(x),0},{0,1}} . {{1,0},{-tan(x),1}} . {{cos(x),0},{0,cos(x)}} = {{cos(x),sin(x)},{-sin(x),cos(x)}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=does+%7B%7B1,tan(x)%7D,%7B0,1%7D%7D+.+%7B%7B1%2Fcos%5E2(x),0%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7B-tan(x),1%7D%7D+.+%7B%7Bcos(x),0%7D,%7B0,cos(x)%7D%7D+%3D+%7B%7Bcos(x),sin(x)%7D,%7B-sin(x),cos(x)%7D%7D
;==================================================
;NOTES: APPROACH 2
;approach 2 based on:
;[mentioned in video description]
;Rotate an acute angle in MS Paint - YouTube
;https://www.youtube.com/watch?v=wUKMIi0mInc
;Rotation matrix - Wikipedia
;https://en.wikipedia.org/wiki/Rotation_matrix#Decomposition_into_shears
;Rotation by Shearing
;http://www.ocf.berkeley.edu/~fricke/projects/israel/paeth/rotation_by_shearing.html
;(note: original text used theta, Chr(952), not 'x')
;Step 1: Horizontal skew by x/2
;Step 2: Vertical skew by -arctan[sin[x]]
;Step 3: Horizontal skew by x/2
;based on Wikipedia 'Rotation matrix' page:
;(note: matrix for anticlockwise rotation)
;decomposition into shears:
;R = [1, -tan(x/2)][ 1 , 0][1, -tan(x/2)]
; [0, 1 ][sin(x), 1][0, 1 ]
;R = [cos(x), -sin(x)]
; [sin(x), cos(x)]
;simplify {{1,-tan(x/2)},{0,1}} . {{1,0},{sin(x),1}} . {{1,-tan(x/2)},{0,1}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=simplify+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7Bsin(x),1%7D%7D+.+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D
;does {{1,-tan(x/2)},{0,1}} . {{1,0},{sin(x),1}} . {{1,-tan(x/2)},{0,1}} = {{cos(x),-sin(x)},{sin(x),cos(x)}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=does+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7Bsin(x),1%7D%7D+.+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D+%3D+%7B%7Bcos(x),-sin(x)%7D,%7Bsin(x),cos(x)%7D%7D
;==================================================
;SCRIPT
#SingleInstance force
#IfWinActive ahk_class MSPaintApp
q:: ;rotate by shear/scale/shear/scale
w:: ;rotate by 3 shears
WinGet, hWnd, ID, A
WinGetClass, vWinClass, % "ahk_id " hWnd
if !(vWinClass = "MSPaintApp")
return
vApproach := InStr(A_ThisHotkey, "q") ? 1 : 2
vPi := 3.141592653589793
InputBox, vInput,, rotate by what angle?,,,,,,,, 23
if ErrorLevel
return
;angle: put a for anticlockwise, p for pi, r for radians
;(no need for d=degrees or c=clockwise)
vAngle := StrReplace(vInput, "a")
if InStr(vInput, "a")
vAngle *= -1
if InStr(vInput, "p")
vAngle *= vPi
if InStr(vInput, "p") || InStr(vInput, "r")
vAngle := Round(vAngle * (180/vPi))
WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
FileGetVersion, vPVersion, % vPPath
vIsWinXP := (SubStr(vPVersion, 1, 3) = "5.1")
;click Select tool before begin:
;note: if you do 'Edit, Select All', or equivalent,
;before you begin, you may get problems,
;clicking the Select tool avoids this, e.g. problems:
;the selected image expands,
;but the canvas does not expand,
;unless you copy and paste the image
if vIsWinXP
SendMessage, 0x111, 620,,, % "ahk_id " hWnd ;WM_COMMAND := 0x111 ;Select
;==============================
;step 1: rotate by between -45 and 45 degrees
vRotate1 := Mod(vAngle, 90)
if (vRotate1 > 45)
vRotate1 -= 90
;D/R/C/A degrees/radians/clockwise/anticlockwise
vAngleDC := vRotate1
vAngleDA := vAngleDC * -1
vAngleRC := vAngleDC * (vPi/180)
vAngleRA := vAngleRC * -1
if !(vRotate1 = 0)
{
if (vApproach = 1)
{
vMatrix1 := vAngleDC
vMatrix2 := Round((1/(Cos(vAngleRC)**2)) * 100)
vMatrix3 := -vAngleDC
vMatrix4 := Round(Cos(vAngleRC) * 100)
vList := "3,2,4,1"
}
else if (vApproach = 2)
{
vMatrix1 := -Round(-vAngleDC/2)
vMatrix2 := -Round((ATan(Sin(vAngleRC)) * (180/vPi)))
vMatrix3 := -Round(-vAngleDC/2)
vList := "3,4,3"
}
WinActivate, % "ahk_id " hWnd
Loop, Parse, vList, % ","
{
if !WinActive("ahk_id " hWnd)
return
if vIsWinXP
{
PostMessage, 0x111, 37681, 0,, % "ahk_id " hWnd ;Stretch/Skew...
WinWaitActive, Stretch and Skew
hDlg := WinExist()
ControlSetText, % "Edit" A_LoopField, % vMatrix%A_Index%, % "ahk_id " hDlg
if (vApproach = 2) && (A_Index = 4)
ControlSetText, Edit2, % vMatrix4, % "ahk_id " hDlg
ControlSend, ahk_parent, {Enter}, % "ahk_id " hDlg
}
else ;e.g. Windows 7
{
;ControlSend, ahk_parent, ^w, % "ahk_id " hWnd
SendInput, ^w
WinWaitActive, Resize and Skew
hDlg := WinExist()
ControlSetText, % "Edit" A_LoopField, % vMatrix%A_Index%, % "ahk_id " hDlg
if (vApproach = 2) && (A_Index = 4)
ControlSetText, Edit2, % vMatrix4, % "ahk_id " hDlg
ControlSend, ahk_parent, {Enter}, % "ahk_id " hDlg
}
WinWaitActive, % "ahk_id " hWnd
}
}
;==============================
;step 2: rotate by 0/90/180/270 degrees
vRotate2 := Mod(vAngle - vRotate1, 360)
;note: this stage is only supported on MS Paint (Windows XP version)
if RegExMatch(vRotate2, "^(90|180|270)$")
&& vIsWinXP
{
PostMessage, 0x111, 37680, 0,, % "ahk_id " hWnd ;Flip/Rotate...
WinWaitActive, Flip and Rotate
hDlg := WinExist()
Control, Check,, Button4, % "ahk_id " hDlg
if (vRotate2 = 90)
Control, Check,, Button5, % "ahk_id " hDlg
else if (vRotate2 = 180)
Control, Check,, Button6, % "ahk_id " hDlg
else if (vRotate2 = 270)
Control, Check,, Button7, % "ahk_id " hDlg
ControlSend, ahk_parent, {Enter}, % "ahk_id " hDlg
}
return
#IfWinActive
;==================================================
Some common transformations in linear algebra, using a 2x2 matrix, on 2D graphics, are: reflection, rotation, scaling (stretching), shearing (skewing), but *not* translation. It is mathematically possible to exactly rotate by using shears only, or by combining shears and scalings. The rotations in MS Paint are not perfect because integers are rounded, and because square pixels are not a pure mathematical entity, but the principle is correct.
If you haven't already, I'd suggest getting a copy of MS Paint (Windows XP version).
C:\Windows\System32\mspaint.exe
(Other Windows XP candidates: notepad.exe, sndrec32.exe, wordpad.exe.)
(AFAIK, you only need the exe files.)
Links:
[an explanation of matrix multiplication]
matrix functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=64480
[I'd recommend using the Gdip library for doing image rotations]
GDI+ standard library 1.45 by tic - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=6517