#!/usr/bin/env node const Device = require("./lib/device"); const Motion = require("./lib/motion"); //const clone = x => JSON.parse(JSON.stringify(x)); // Deep //const clone = x => Object.assign({}, x); // Shallow const path = "/dev/input/"+process.argv[2]; console.log("Opening %s", path); const device = new Device(path); const motion = new Motion(device, 600/*ms for long*/); device.on("open", () => { console.log(device.id); device.grab(); }); //device.on("EV_ABS", e => console.log(e)); //device.on("EV_SYN", e => console.log(e)); motion.on("error", console.error) //.on("short", e => console.log("short", e)) //.on("long", e => console.log("long", e)) .on("short", doShort).on("long", doLong); const flipBits = (n,w) => parseInt(n.toString(2).padStart(w,0).split("").reverse().join(""),2); const eucDist = (a,b) => Math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2); //const avgDist = (a,b) => (eucDist(a.i,b.i) + eucDist(a.f,b.f))/2; const selfDist = a => eucDist(a.i, a.f); const getAngle2 = (a,b) => Math.atan2(a.y-b.y, b.x-a.x)*180/Math.PI; const getAngle = a => getAngle2(a.i,a.f); const isLeft = (a,b,c) => ((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x))>0; // What side of a-b is c on const state = { homed: false, homes: [], angle: 0, fingerDist: 0, }; const dotRadius = 0.3; // Tap/reach boundary in terms of between-finger distance const swipeLength = 0.2; // Tap/swipe threshold in terms of between-finger distance function doLong(e) { if (e.length < 3) return; // Pinky finger optional! const between = []; for (let i=0;i a.dist-b.dist); const fingerDist = between[0].dist; if (e.map(p => selfDist(p)).reduce((a,d) => a || (d >= fingerDist*swipeLength), false)) return; // Moved too far const biggest = between.pop(); const sides = e.reduce((a,p,i) => { if (i != biggest.a && i != biggest.b) a.push({i:i,s:isLeft(e[biggest.a].m, e[biggest.b].m, p.m)}); return a; }, []); const side = 2*sides.reduce((a,s)=>a+s.s,0)>sides.length; const indexFinger = biggest[side?"b":"a"]; state.homes = []; state.homes.push(e[indexFinger].m); sides.map(f => Object.assign({d:eucDist(e[indexFinger].m, e[f.i].m)}, f)) .sort((a,b) => a.d-b.d).forEach(f => state.homes.push(e[f.i].m)); state.homes.push(e[biggest[side?"a":"b"]].m); state.angle = getAngle2(state.homes[0], state.homes[state.homes.length-1]); state.fingerDist = fingerDist; state.homed = true; //console.log(JSON.stringify(state.homes), state.angle, state.fingerDist); console.log("Homed"); } const prettyFingers = n => "("+n.toString(2).padStart(state.homes.length,0) .split("").map(f => f=="1"?"#":" ").reverse().join("")+")"; function doShort(e) { if (!state.homed) return; // Gesture types: // - Tap: // - inside tap | 0 // - reaching tap | 1 // - Swipe: // - Vertical: // - from in down | 2 // - from up in | 3 // - swipe up | 4 // - Horizontal: // - from in left | 5 // - from in right | 6 const isTap = (e.reduce((a,p) => a+selfDist(p),0) / e.length) < state.fingerDist * swipeLength; if (isTap) { const fingers = e.map(p => state.homes .map((f,i) => ({i,d:eucDist(f, p.m)})) .sort((a,b)=>a.d-b.d)[0]); const fingerCode = fingers.reduce((a,f) => a+2**f.i, 0); // TODO reaching taps console.log("tap", prettyFingers(fingerCode)); } else { // TODO swipes console.log("swipe"); } }