Last active
December 25, 2015 09:59
-
-
Save Aatoz/6957863 to your computer and use it in GitHub Desktop.
AutoLeap.ahk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; License: public domain | |
; Feel free to modify and improve upon the work herein, but be certain to give credit me (Verdlin) when using my scripts | |
; Credits: | |
; Tidbit: st.ahk lib | |
; tkoi: ILButton.ahk | |
; If you see your work and you are not credit, this is not deliberate. Notify me and I will credit you ASAP | |
/* | |
TODOs: | |
1. Circle gestures will be WAAY more useful if I can post messages while still recording. | |
For example, lets say the gesture is "Left, Circle." As things stand now, actions are trigerred AFTER the | |
gesture has been recorded (once no fingers are detected). But what if the action was the resizing of the window? | |
It would be quite useful if, say, Left, Circle made the window smaller and KEPT on making the window smaller while | |
the Circle gesture was being performed | |
*/ | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; By overriding __Delete, we can ensure that GUIs and global objects are automatically destroyed | |
Leap(hMsgHandlerFunc) | |
{ | |
; Check to see if core LeapMotion software is even installed on this machine | |
RegRead, sKey, HKCR, airspace\shell\open\command | |
if (!InStr(sKey, "Leap Motion")) | |
{ | |
Msgbox, 4096,, Error! Leap Motion software is required to run this script. | |
return false | |
} | |
global g_hMsgHandlerFunc := hMsgHandlerFunc, g_sLeapWorkingDir := A_ScriptDir "\AutoLeap" | |
gosub, DoLeap | |
static base := {__Delete:"Leap_Delete"} | |
return Object("base", base) | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
Leap_Delete(v="") | |
{ | |
global | |
SetTimer, Record, Off | |
g_WB := ; Clearing the document does not kill the IE GUI, and the GUI is hidden, so WinKill it | |
WinKill, Leap Motion JavaScript Sample - Windows Internet Explorer ahk_class IEFrame | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
InitLeap: | |
{ | |
; Note: with the current design, this tolerance factor is actually ignored | |
; I do think it will be needed, though, and I am keeping it here so that | |
; I do not completely forget about it | |
g_iTolerance := 15 | |
g_asGestures := [] | |
g_bUnfold := true | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
InitOSD: | |
{ | |
GUI AutoLeapOSD: New, hwndg_hAutoLeapOSD | |
Gui, +AlwaysOnTop -Caption +Owner +LastFound +ToolWindow +E0x20 | |
WinSet, Transparent, 210 | |
GUI, Color, 202020 | |
GUI, Font, s18 c5C5CF0 wnorm ; c0xF52C5F | |
GUI, Add, Text, x0 y0 hwndg_hAutoLeapOSDText vg_vAutoLeapOSDText Wrap Center | |
g_hFont := Fnt_GetFont(g_hAutoLeapOSDText) | |
g_iMaxWidth := A_ScreenWidth | |
GUI, AutoLeapOSD:Show, x0 y0 NoActivate | |
GUI, AutoLeapOSD:Hide ; Not using WinMove for sizing since it activates the window | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
DoLeap: | |
{ | |
gosub InitLeap | |
;~ Gui Add, ActiveX, w980 h640 vWB, Shell.Explorer ; The final parameter is the name of the ActiveX component. | |
global g_WB := ComObjCreate("InternetExplorer.Application") ;// Create an IE object | |
g_WB.Visible := true ;// Make the IE object visible -- if I can find a way around the annoying block problem, then this can be removed!! | |
g_WB.Navigate(g_sLeapWorkingDir "\AutoLeap.html") ; This is specific to the web browser control. | |
WinActivate, Leap Motion JavaScript Sample - Windows Internet Explorer ahk_class IEFrame | |
Msgbox, 4096,, Click the, "Allow blocked content" button, and then press "OK" on this dialog. | |
g_WB.Visible := false | |
gosub InitOSD | |
; TODO: Is there possibly an OnMessage event that may be hooked on to? | |
; I really would rather just trigger the Leap routines when they are actually needed | |
SetTimer, Record | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
Record: | |
{ | |
try | |
{ | |
g_wb.document.getElementById("pauseOnGesture").checked | |
} | |
catch, sError | |
{ | |
Tooltip %sError% | |
return | |
} | |
; Ensure we always pause on gestures | |
if (!g_wb.document.getElementById("pauseOnGesture").checked) | |
g_wb.document.getElementById("pauseOnGesture").click() | |
g_sPointableData := g_WB.document.getElementById("pointableData").innerText | |
g_sGestureData := g_WB.document.getElementById("gestureData").innerText | |
if (g_sPointableData == A_Blank) | |
return | |
g_bFinishedRecording := g_sPointableData = "No pointables" ; Should we finish when there are no fingers or when there are no palms? | |
if (g_bFinishedRecording) | |
PostRecordedData() | |
if (g_sGestureData == "No gestures") | |
return | |
; Record new data in g_asGestures | |
TranslateLeapData(LoadLeapDataIntoIni()) | |
gosub LeapOSD_Update | |
g_wb.document.getElementById("pause").click() ; Unpause | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
LeapOSD_Update: | |
{ | |
sGestures := st_glue(g_asGestures, ", ") | |
if (sGestures == A_Blank) | |
return | |
GUI, AutoLeapOSD:Default | |
rect := Str_MeasureTextWrap(sGestures, g_iMaxWidth, g_hFont) | |
GUIControl, MoveDraw, g_vAutoLeapOSDText, % "W" rect.right " H" rect.bottom | |
GUIControl,, g_vAutoLeapOSDText, %sGestures% | |
if (g_bUnfold) | |
{ | |
WAnim_FoldOrUnfold(true, rect.right, g_hAutoLeapOSD) | |
GUI, Show, % "W" rect.right " H" rect.bottom " NoActivate" | |
g_bUnfold := false | |
} | |
else GUI, Show, % "W" rect.right " H" rect.bottom " NoActivate" | |
; Post data for gesture recorders... | |
if (g_hMsgHandlerFunc) | |
g_hMsgHandlerFunc.("Record", g_asGestures, "") | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
PostRecordedData() | |
{ | |
global g_asGestures, g_hAutoLeapOSD, g_vAutoLeapOSDText, g_bUnfold, g_iMaxWidth, g_hMsgHandlerFunc | |
if (g_asGestures.MaxIndex()) | |
{ | |
if (g_hMsgHandlerFunc) | |
{ | |
g_hMsgHandlerFunc.("Post", g_asGestures, sOutput) | |
if (sOutput) | |
{ | |
GUI, AutoLeapOSD:Default | |
GUI, Show, w%g_iMaxWidth% NoActivate | |
GUIControl,, g_vAutoLeapOSDText, %sOutput% | |
GUIControl, MoveDraw, g_vAutoLeapOSDText, % "W" g_iMaxWidth | |
DismissAfter1000MS() | |
} | |
else GUI, AutoLeapOSD:Hide | |
} | |
else GUI, AutoLeapOSD:Hide | |
;~ WinGetPos,,,, iH, ahk_id %g_hAutoLeapOSD% | |
;~ WAnim_SlideViewInOutFrom(false, "Top", 0, -iH, g_hAutoLeapOSD, "AutoLeapOSD", 5) | |
;~ msgbox ok %ih% | |
g_bUnfold := true | |
} | |
g_asGestures := [] | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
DismissAfter1000MS() | |
{ | |
SetTimer, DismissAfter1000MS, 1000 | |
return | |
} | |
DismissAfter1000MS: | |
{ | |
if (g_bDismiss) | |
{ | |
g_bDismiss := false | |
GUI, AutoLeapOSD:Hide | |
g_bUnfold := true | |
SetTimer, %A_ThisLabel%, Off | |
} | |
else g_bDismiss := true | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; Loads data from g_sPointableData and g_sGestureData into class_EasyIni object | |
/* sPointableData will be formatted like: | |
Pointable ID: 96 | |
Belongs to hand with ID: 43 | |
Classified as a finger | |
Length: 46.5 mm | |
Direction: (0.18, -0.09, -0.98) | |
Tip position: (-44.2, 243.2, 16.1) mm | |
Tip velocity: (-3.3, -10.3, -18.2) mm/s | |
sGestureData will be formatted like: | |
"Gesture ID: 8 | |
type: swipe | |
state: start | |
hand IDs: | |
pointable IDs: 65 | |
duration: 0 µs | |
start position: (-163.8, 115.5, 16.4) mm | |
current position: (-63.3, 124.8, -41.8) mm | |
direction: (0.86, 0.08, -0.50) | |
speed: 2450.3 mm/s" | |
*/ | |
LoadLeapDataIntoIni() | |
{ | |
global g_sPointableData, g_sGestureData | |
FileDelete, copy.ini | |
vLeapData := class_EasyIni("copy.ini", "") | |
Loop, Parse, g_sGestureData, `n, `r | |
{ | |
; For now, don't handle multi-finger gestures | |
if (A_Index > 1) | |
return | |
; Begin "Gesture ID:..." (etc.) parsing | |
if (SubStr(A_LoopField, 1, 11) = "Gesture ID:") | |
{ | |
sThisSec := "Gesture" A_Index | |
Loop, Parse, g_sGestureData, `, | |
{ | |
/* | |
1=Gesture ID: 8 | |
2= type: swipe | |
3= state: start | |
4= hand IDs: | |
5= pointable IDs: 65 | |
6= duration: 0 µs | |
7= start position: (-163.8 | |
8= 115.5 | |
9= 16.4) mm | |
10= current position: (-63.3 | |
11= 124.8 | |
12= -41.8) mm | |
13= direction: (0.86 | |
14= 0.08 | |
15= -0.50) | |
16= speed: 2450.3 mm/s | |
*/ | |
sTrimmedLoopField := Trim(A_LoopField) | |
if (A_Index == 1 || A_Index == 4 || A_Index == 5 || A_Index == 6 || A_Index ==16) | |
continue | |
else if (A_Index == 2) | |
vLeapData.AddSection(sThisSec, "Type", SubStr(sTrimmedLoopField, 7)) | |
else if (A_Index == 3) | |
vLeapData.AddKey(sThisSec, "State", sTrimmedLoopField) | |
else if (A_Index == 7) | |
vLeapData.AddKey(sThisSec, "StartX", SubStr(sTrimmedLoopField, 18)) | |
else if (A_Index == 8) | |
vLeapData.AddKey(sThisSec, "StartY", sTrimmedLoopField) | |
else if (A_Index == 9) | |
vLeapData.AddKey(sThisSec, "StartD", SubStr(sTrimmedLoopField, 1, StrLen(sTrimmedLoopField) - 4)) | |
else if (A_Index == 10) | |
vLeapData.AddKey(sThisSec, "CurX", SubStr(sTrimmedLoopField, 20)) | |
else if (A_Index == 11) | |
vLeapData.AddKey(sThisSec, "CurY", sTrimmedLoopField) | |
else if (A_Index == 12) | |
vLeapData.AddKey(sThisSec, "CurD", SubStr(sTrimmedLoopField, 1, StrLen(sTrimmedLoopField) - 4)) | |
else if (A_Index == 13) | |
vLeapData.AddKey(sThisSec, "DirX", SubStr(sTrimmedLoopField, 13)) | |
else if (A_Index == 14) | |
vLeapData.AddKey(sThisSec, "DirY", sTrimmedLoopField) | |
else if (A_Index == 15) | |
vLeapData.AddKey(sThisSec, "DirD", SubStr(sTrimmedLoopField, 1, StrLen(sTrimmedLoopField) - 1)) | |
} | |
} | |
} | |
vLeapData.Save() | |
/* | |
iPointable := 0 | |
Loop, Parse, g_sPointableData, `n, `r | |
{ | |
; Direction: (0.18, -0.09, -0.98) | |
if (SubStr(A_LoopField, 1, 10) = "Direction:") | |
{ | |
iPointable++ | |
vLeapData.AddKey("Pointable" iPointable, "Direction", A_LoopField, sError) | |
} | |
; Tip position: (-44.2, 243.2, 16.1) mm | |
if (SubStr(A_LoopField, 1, 13) = "Tip position:") | |
vLeapData["Pointable" iPointable]["Tip"] := A_LoopField | |
} | |
*/ | |
return vLeapData | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; | |
TranslateLeapData(vLeapData) | |
{ | |
global g_iTolerance, g_asGestures | |
; For now, gestures are interpeted as linear movements along a 2D plane | |
; Movements will interpreted as only one of the following: | |
;1. Left | |
;2. Right | |
;3. Up | |
;4. Down | |
;5. Circle - TODO: Circle-Left, Circle-Right | |
;6. KeyTap (A quick Up+Down gesture) | |
; The disadvance to this approach is that diagnal gestures will be recorded awkwardly | |
; Eventually, I really hope to allow more advanced gestures, | |
; but I don't want to bite off more than I can chew | |
; It is also important to note that AddGesture_Safe is used to ensure that there are no | |
; repeat gestures, i.e. Right, Right | |
for sGesture in vLeapData | |
{ | |
if (vLeapData[sGesture].Type = "Circle") | |
{ | |
AddGesture_Safe("Circle", g_asGestures) | |
continue | |
} | |
else if (vLeapData[sGesture].Type = "KeyTap") | |
{ | |
; A Keytap is essentially a Down+Up gesture | |
AddGesture_Safe("KeyTap", g_asGestures) | |
continue | |
} | |
; Compare directions and give preference the the movement that was greater | |
if (abs(vLeapData[sGesture].DirX) > abs(vLeapData[sGesture].DirY)) | |
{ | |
if (vLeapData[sGesture].DirX > 0) | |
AddGesture_Safe("Right", g_asGestures) | |
else AddGesture_Safe("Left", g_asGestures) | |
} | |
else ; Movement along Y axis was greater, so interpret as Up or Down | |
{ | |
if (vLeapData[sGesture].DirY > 0) | |
AddGesture_Safe("Up", g_asGestures) | |
else AddGesture_Safe("Down", g_asGestures) | |
} | |
;~ vLeapData[sGesture].DirD | |
} | |
return g_asGestures | |
} | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;; This function safely prevents recording of double gestures | |
;;;;;;;;;;;;;; i.e. "RIght, Right" or "Left, Left," etc. | |
AddGesture_Safe(sGesture, ByRef rasGestures) | |
{ | |
if (rasGestures[rasGestures.MaxIndex()] != sGesture) | |
rasGestures.Insert(sGesture) | |
return | |
} | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;~ TranslateLeapData(vLeapData) | |
;~ { | |
;~ global g_iTolerance | |
;~ asGestures := [] | |
;~ for sGesture in vLeapData | |
;~ { | |
;~ if ((abs(vLeapData[sGesture].StartX) - abs(vLeapData[sGesture].CurX)) > g_iTolerance) | |
;~ if (vLeapData[sGesture].DirX > 0) | |
;~ asGestures.Insert("Right") | |
;~ else asGestures.Insert("Left") | |
;~ if ((abs(vLeapData[sGesture].StartY) - abs(vLeapData[sGesture].CurY)) > g_iTolerance) | |
;~ if (vLeapData[sGesture].DirY > 0) | |
;~ asGestures.Insert("Up") | |
;~ else asGestures.Insert("Down") | |
;~ vLeapData[sGesture].DirD | |
;~ } | |
;~ return asGestures | |
;~ } | |
;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment