File: src/BB.LeapMotion.js
/**
* A module implementing LeapMotion Sensor
* @module BB.LeapMotion
*/
// module to get x, y from leapmotion
// used leap-0.6.4.js as needed for the leapmotion to function
define(['./BB'],
function(BB){
'use strict';
/**
*
* @property minX
* @type {Number}
* @default 0
*/
var minX = 0;
var outputminX = 0;
var xscale= 0;
var widthX =0;
var minY = 0;
var outputminY= 0;
var yscale= 0;
var heightY = 0;
var minZ = 0;
var outputminZ= 0;
var zscale= 0;
var depthZ = 0;
/**
* A module implementing LeapMotion Sensor.
*
* __Note:__ The constructor makes throws an Error if the LeapMotion library is not already loaded.
* If the LeapMotion library is not imported "missing LeapMotion library " should appear in the console,
* check LeapMotion docs or LiBB examples to see how to import library from html.
* Method enables the LeapMotion module to start
* obtaining the X,Y values from the sensor, these values must be called if needed.
* Method also allows to detect if a gesture has occured.
* @class BB.LeapMotion
* @param {Canvas} canvas The created canvas that must be given to the LeapMotion module if canX and canvasY variable want to be used.
* @param {Boolean} getXY Boolean value to enable/disable access to the X,Y values from LeapMotion Sensor .
* @param {Boolean} getGestures Boolean value to enable/disable access to the getGestures from LeapMotion Sensor.
* @constructor
*/
BB.LeapMotion = function(config){
// create variables that can be accesed later on to be able to have the data
// canvasX, canvasY each will contain a numeric value that represent the position.
/**
* The pointers X position as given by the LeapMotion Sensor
* @property y
* @type {Number}
* @default 0
*/
BB.LeapMotion.prototype.x = 0;
/**
* The pointers Y position as given by the LeapMotion Sensor
* @property y
* @type {Number}
* @default 0
*/
BB.LeapMotion.prototype.y = 0;
/**
* The pointers Z position as given by the LeapMotion Sensor
* @property z
* @type {Number}
* @default 0
*/
BB.LeapMotion.prototype.z = 0;
/**
* Boolean value corresponding to the grab gesture detected by the LeapMotion sensor.
* @property grab
* @type {Gesture}
* @default false
*/
BB.LeapMotion.prototype.grab = false;
/**
* Boolean value corresponding to the pinch gesture detected by the LeapMotion sensor.
* @property pinch
* @type {Gesture}
* @default false
*/
BB.LeapMotion.prototype.pinch = false;
/**
* Boolean value corresponding to the circle gesture detected by the LeapMotion sensor.
* @property circle
* @type {Gesture}
* @default false
*/
BB.LeapMotion.prototype.circle = false;
/**
* Boolean value corresponding to the keytap gesture detected by the LeapMotion sensor.
* @property keytap
* @type {Gesture}
* @default false
*/
BB.LeapMotion.prototype.keytap = false;
/**
* Boolean value corresponding to the screenTap gesture detected by the LeapMotion sensor.
* @property screenTap
* @type {Gesture}
* @default false
*/
BB.LeapMotion.prototype.screenTap = false;
/**
* Boolean value corresponding to the swipe gesture detected by the LeapMotion sensor.
* @property swipe
* @type {Gesture}
* @default false
*/
BB.LeapMotion.prototype.swipe = false;
/**
* Boolean value corresponding to the clockwise detected by the LeapMotion.
* @property clockwise
* @type {Gesture}
* @default false
*/
BB.LeapMotion.prototype.clockwise = false;
/**
* Double especifies the radius of the circle gesture in mm.
* @property circleradius
* @type {Number}
* @default 0
*/
BB.LeapMotion.prototype.circleradius = 0;
/**
* Boolean value indicating if the LeapMotion device is streaming data.
* @property deviceStreaming
* @type {Connection indicator}
* @default false
*/
BB.LeapMotion.prototype.deviceStreaming = false;
/**
* Objetc containing the values of the last frame registered by the leapmotion sensor.
* @property lastFrame
* @type {Object}
* @default null
*/
BB.LeapMotion.prototype.lastFrame = null;
if(typeof Leap === 'undefined'){
throw new Error(' missing LeapMotion library ');
}
// using Leap. controller to create the connection to our sensor
var controller = new Leap.Controller({enableGestures:true});
var canvasGiven = false;
// gets data from config
var canvas = (config && typeof config.canvasConstructor === 'object') ? config.canvasConstructor : null;
var getGestures = (config && typeof config.gesturesEnabled === 'boolean') ? config.gesturesEnabled : false;
var mapDimension = (config && typeof config.mapDimension === 'number') ? config.mapDimension : 2;
if(canvas === null){
console.log("No canvas given therefor x and y are only available setting mapX,mapY and mapZ if necessary");
canvasGiven = false;
}else{canvasGiven = true;}
// the controller.on method lets us se what the sensor is telling us on each frame
// frames are sent 200 frames per second
controller.on("frame",function(frame){
// allowing user to acces frame therefor access all the data from the sdk
BB.LeapMotion.prototype.lastFrame = frame;
// frame.pointables allows us to detect when a frame has a pointable.(hand,finger)
if(frame.pointables.length>0){
var pointable = frame.pointables[0];
// creates and interaction box it provides normalized coordinates for hands, fingers, and tools within this box.
var interactionBox = frame.interactionBox;
// provides the stabalized tip position
var normalizedPosition = interactionBox.normalizePoint(pointable.stabilizedTipPosition, true);
if(canvasGiven && mapDimension === 2){
// Convert the normalized coordinates to span the canvas
BB.LeapMotion.prototype.x = canvas.width * normalizedPosition[0];
BB.LeapMotion.prototype.y = canvas.height * (1 - normalizedPosition[1]);
}
if(!canvasGiven && mapDimension ===3){
// Convert the normalized coordinates to span the canvas
BB.LeapMotion.prototype.x = widthX * normalizedPosition[0];
//BB.LeapMotion.prototype.x = outputminX + (normalizedPosition[0] - minX)* xscale;
BB.LeapMotion.prototype.y = heightY * (1 - normalizedPosition[1]);
//BB.LeapMotion.prototype.y = -1 *(outputminY + (normalizedPosition[1] - minY)* yscale);
//BB.LeapMotion.prototype.z = depthZ * normalizedPosition[2] ;
BB.LeapMotion.prototype.z = depthZ * (outputminZ + (normalizedPosition[2] - minZ)* zscale);
}
}
// make all vars false when no hand detected fromo the LeaMotion
if(frame.hands.length === 0){
BB.LeapMotion.prototype.grab = false;
BB.LeapMotion.prototype.pinch = false;
BB.LeapMotion.prototype.circle = false;
BB.LeapMotion.prototype.keytap = false;
BB.LeapMotion.prototype.screenTap = false;
BB.LeapMotion.prototype.swipe = false;
}
// detects hands and makes sure user wants to check for gestures.
if(frame.hands.length > 0 && getGestures){
var hand = frame.hands[0];
var position = hand.palmPosition;
// when a frame detects a hand and gestures are wanted it will give true when gesture grab
if(hand.grabStrength == 1){
BB.LeapMotion.prototype.grab = true;
BB.LeapMotion.prototype.pinch = false;
BB.LeapMotion.prototype.circle = false;
BB.LeapMotion.prototype.keytap = false;
BB.LeapMotion.prototype.screenTap = false;
BB.LeapMotion.prototype.swipe = false;
}else{
BB.LeapMotion.prototype.grab = false;
}
// when a frame detects a hand and gestures are wanted it will give true when gesture pinch
if(hand.pinchStrength == 1){
BB.LeapMotion.prototype.grab = false;
BB.LeapMotion.prototype.pinch = true;
BB.LeapMotion.prototype.circle = false;
BB.LeapMotion.prototype.keytap = false;
BB.LeapMotion.prototype.screenTap = false;
BB.LeapMotion.prototype.swipe = false;
}else{
BB.LeapMotion.prototype.pinch = false;
}
}
// when no gestures are being detected all the pre loaded gestures are falsed. (circle,keytap,screenTap,swipe)
if(frame.valid && frame.gestures.length === 0){
BB.LeapMotion.prototype.circle = false;
BB.LeapMotion.prototype.keytap = false;
BB.LeapMotion.prototype.screenTap = false;
BB.LeapMotion.prototype.swipe = false;
}
// when a gesture is detected it will take each gesture and set to true the var that corresponds to the gesture
// making the gesture available to the user
if(frame.valid && frame.gestures.length > 0){
frame.gestures.forEach(function(gesture){
switch (gesture.type){
case "circle":
BB.LeapMotion.prototype.circle = true;
var pointableID = gesture.pointableIds[0];
var direction = frame.pointable(pointableID).direction;
var dotProduct = Leap.vec3.dot(direction, gesture.normal);
if(dotProduct > 0){
// detects if the circle gesture is clockwise and sets var.
BB.LeapMotion.prototype.clockwise = true;
}else{ BB.LeapMotion.prototype.clockwise = false;}
BB.LeapMotion.prototype.circleradius = gesture.radius.toFixed(1) ;
break;
case "keyTap":
BB.LeapMotion.prototype.keytap = true;
break;
case "screenTap":
BB.LeapMotion.prototype.screenTap = true;
break;
case "swipe":
BB.LeapMotion.prototype.swipe = true;
break;
}
});
}
});
// connecto to the leap motion sensor to get data
controller.connect();
controller.on('deviceStreaming', onStreaming);
function onStreaming(){
BB.LeapMotion.prototype.deviceStreaming = true;
}
controller.on('deviceStopped', onStopped);
function onStopped(){
BB.LeapMotion.prototype.deviceStreaming = false;
}
};
BB.LeapMotion.mapX = function(configX){
//minX = (configX && typeof configX.minX === 'number') ? configX.minX : 0;
//var maxX = (configX && typeof configX.maxX === 'number') ? configX.maxX : 0;
widthX = (configX && typeof configX.widthX === 'number') ? configX.widthX : null;
// outputminX = - widthX /2;
//var outputmaxX = widthX /2;
// if( outputminX !== null && outputmaxX !== null ){
// xscale = (outputmaxX - outputminX)/(maxX - minX);
// }
if(widthX === null){
console.log(" widthX parameter missing on mapX method (Not needed if a canvas is given)");
}
};
BB.LeapMotion.mapY = function(configY){
//minY = (configY && typeof configY.minY === 'number') ? configY.minY : 0;
//var maxY = (configY && typeof configY.maxY === 'number') ? configY.maxY : 0;
heightY = (configY && typeof configY.heightY === 'number') ? configY.heightY : null;
//outputminY = - heightY /2;
// var outputmaxY = heightY /2;
// if( outputminY !== null && outputmaxY !== null ){
// yscale = (outputmaxY - outputminY)/(maxY - minY);
// }
if(heightY === null){
console.log(" heightY parameter missing on mapY method (Not needed if a canvas is given)");
}
};
BB.LeapMotion.mapZ = function(configZ){
minZ = (configZ && typeof configZ.minZ === 'number') ? configZ.minZ : 0;
var maxZ = (configZ && typeof configZ.maxZ === 'number') ? configZ.maxZ : 0;
depthZ = (configZ && typeof configZ.depthZ === 'number') ? configZ.depthZ : null;
outputminZ = - depthZ /2;
var outputmaxZ = depthZ /2;
if( outputminZ !== null && outputmaxZ !== null ){
zscale = (outputmaxZ - outputminZ)/(maxZ - minZ);
}
if(depthZ === null){
console.log(" depthZ parameter missing on mapZ method (Must be present for 3D mapping)");
}
};
return BB.LeapMotion;
});