LeapConnector.prototype.getBasis = function (frame) { var handLast = frame.hands[0]; var dir = handLast.direction; var nor = handLast.palmNormal; var dxn = Leap.vec3.create(); Leap.vec3.cross(dxn, dir, nor); var basisLast = [].concat(dir).concat(nor); basisLast[6] = dxn[0]; basisLast[7] = dxn[1]; basisLast[8] = dxn[2]; return basisLast; }
controller.on( 'frame' , function( frame ){ var gestures = frame.gestures, circle, pointable, direction, normal; // Check if is there any gesture going on if(gestures.length > 0) { // In this example we will focus only on the first gesture, for the sake of simplicity if(gestures[0].type == 'circle') { circle = gestures[0]; // Get Pointable object circle.pointable = frame.pointable(circle.pointableIds[0]); // Reset circle gesture variables as nedded, not really necessary in this case if(circle.state == 'start') { clockwise = true; } else if (circle.state == 'update') { direction = circle.pointable.direction; // Check if pointable exists if(direction) { normal = circle.normal; // Check if product of vectors is going forwards or backwards // Since Leap uses a right hand rule system // forward is into the screen, while backwards is out of it clockwise = Leap.vec3.dot(direction, normal) > 0; if(clockwise) { //Do clockwose stuff if(isDoingWork){ return; } isDoingWork = true; OSX.volume.set("clockwise", function(vol){ isDoingWork = false; }) } else { //Do counterclockwise stuff if(isDoingWork){ return; } isDoingWork = true; OSX.volume.set("counter-clockwise", function(vol){ isDoingWork = false; }) } } } } } });
LeapConnector.prototype.axisAngle = function(rotation) { //NOTE: Index = 3*row + col when basis vector var axis = [ rotation[5] - rotation[7], rotation[6] - rotation[2], rotation[1] - rotation[3] ]; var sin = Leap.vec3.len(axis); var cos = ((rotation[0] + rotation[4] + rotation[8]) - 1.0)*0.5; var angle = Math.atan2(sin,cos); if (-1e-6 < sin && sin < 1e-6) { axis = [0, 0, 0]; angle = 0; return [axis, angle]; } Leap.vec3.scale(axis, axis, 1 / sin); return [axis,angle]; }
if(hand.type=="right" && hand.confidence > .7) hand.fingers.forEach(function(finger) { if(finger.type== 0 ) { //THUMB var bottomBone = finger.proximal; var middlePartBone = finger.medial; var topPartBone = finger.distal; var topVec= getVec(topPartBone); var middlePartVec= getVec(middlePartBone); var bottomPartVec= getVec(bottomBone); var bottomVec = leapjs.vec3.create(); leapjs.vec3.add(bottomVec,bottomPartVec,middlePartVec); var handDirection = hand.direction; var handVec = leapjs.vec3.fromValues(handDirection[0],handDirection[1],handDirection[2]); var bottomDeg=getDegBetween(bottomVec,handVec); var topDeg=getDegBetween(bottomVec,topVec); result["1_T"]=topDeg; result["1_B"]=bottomDeg; result["hasData"]=true; } else if(finger.type==1) { // INDEX var distalVec= getVec(finger.distal); var medialVec= getVec(finger.medial); var proximalVec = getVec(finger.proximal); var metaVec = getVec(finger.metacarpal); var topVec = leapjs.vec3.create(); leapjs.vec3.add(topVec,medialVec,distalVec); var topDeg=getDegBetween(proximalVec,topVec); var bottomDeg=getDegBetween(metaVec,proximalVec); result["2_T"]=topDeg; result["2_B"]=bottomDeg; result["hasData"]=true; } else if(finger.type==3) { // INDEX var medialVec= getVec(finger.medial); var distalVec= getVec(finger.distal); var metaVec = getVec(finger.metacarpal); var proximalVec = getVec(finger.proximal); var topVec = leapjs.vec3.create(); leapjs.vec3.add(topVec,medialVec,distalVec); var bottomDeg=getDegBetween(metaVec,proximalVec); var topDeg=getDegBetween(proximalVec,topVec); result["3_T"]=topDeg; result["3_B"]=bottomDeg; result["hasData"]=true; } });
// Utility function to calculate the angle between two vectors function getDegBetween(vec1,vec2) { var bottomCos=leapjs.vec3.dot(vec1,vec2) / leapjs.vec3.len(vec1) / leapjs.vec3.len(vec2); return radToDeg(mathjs.acos(bottomCos)); }
// Retrieve the vector of a bone function getVec(bone) { var dir = bone.direction(); var vec = leapjs.vec3.fromValues(dir[0],dir[1],dir[2]); return vec; }
LeapConnector.prototype.onFrame = function(frame) { var that = this; if (frame.hands.length == 1) { if (frame.fingers.length > 3) { var lastFrame = this.controller.frame(1); if (lastFrame && lastFrame.hands.length > 0) { // rotate that.lastY = -1; that.lastX = -1; var hand = frame.hands[0]; var translation = hand.translation(lastFrame); // comment out for the moment until JS Leap Motion rotation bugs are fixed. that.gestureDispatcher.triggerPan(translation[0], -translation[1]); that.gestureDispatcher.triggerZoom(-translation[2]); var basis = that.getBasis(frame); var basisLast = that.getBasis(lastFrame); Leap.mat3.transpose(basisLast, basisLast); var rotation = Leap.mat3.create(); Leap.mat3.multiply(rotation, basis, basisLast) //Get axis-angle representation var axisAngle = that.axisAngle(rotation); Leap.vec3.scale(axisAngle[0], axisAngle[0], axisAngle[1]); // yaw pitch roll are slightly better, but there still seems to be an issue. // var xRotation = this.limitRotation(hand.pitch() - lastFrame.hands[0].pitch()); // var yRotation = this.limitRotation(hand.yaw() - lastFrame.hands[0].yaw()); // var zRotation = this.limitRotation(hand.roll() - lastFrame.hands[0].roll()); // these rotations have an issue where changes aren't updated properly. // see https://github.com/leapmotion/leapjs/issues/188 // var xRotation = this.limitRotation(hand.rotationAngle(lastFrame, [ 1, 0, 0 ])); // var yRotation = this.limitRotation(hand.rotationAngle(lastFrame, [ 0, 1, 0 ])); // var zRotation = this.limitRotation(hand.rotationAngle(lastFrame, [ 0, 0, 1 ])); // console.log('lastframe1: ' + that.lastFrame); if (Math.abs(axisAngle[0][0]) > MIN_ROTATION || Math.abs(axisAngle[0][1]) > MIN_ROTATION || Math.abs(axisAngle[0][2]) > MIN_ROTATION) { that.gestureDispatcher.triggerRotate(this.limitRotation(axisAngle[0][0]), this.limitRotation(axisAngle[0][1]), this.limitRotation(axisAngle[0][2])); } } } else if (frame.fingers.length > 0) { try { var finger = frame.fingers[0]; var hit = Pointer(finger); // Vector hit = // controller.locatedScreens().closestScreenHit(finger).intersect(finger,true); var zVel = finger.tipVelocity.getZ[2]; var absZVel = Math.abs(zVel); var y = hit.getY(); var x = hit.getX(); if (absZVel > 50 && that.lastY != -1) { var scale = 2500 / (absZVel * absZVel); x = (hit.x - that.lastX) * scale + that.lastX; y = (hit.y - that.lastY) * scale + that.lastY; } that.lastY = y; that.lastX = x; if (!isNaN(x) && !isNaN(y)) { // that.gestureDispatcher.point(x, y); } } catch (e) { console.log('error occurred when pointing: ' + e); } } else { that.lastY = -1; that.lastX = -1; } var i; if (frame.gestures) { frame.gestures.forEach(function(gesture) { switch (gesture.type) { case 'swipe': that.gestureDispatcher.reset(); break; case 'screenTap': that.gestureDispatcher.selectMouseCursor(); // add a delay so the app can process the mouse clicks. setTimeout(that.gestureDispatcher.zoomToSelection, 100); break; case 'keyTap': that.gestureDispatcher.zoomToSelection(); break; default: console.log("Unknown gesture type: " + gesture.type); break; } }); } } // console.log('lastframe2: ' + that.lastFrame); }