leapmotion-ts
Version:
TypeScript framework for Leap Motion.
1,044 lines (1,042 loc) • 130 kB
JavaScript
var __extends = (this && this.__extends) || function(d, b) {
for (var p in b)
if (b.hasOwnProperty(p)) d[p] = b[p];
function __() {
this.constructor = d;
}
__.prototype = b.prototype;
d.prototype = new __();
};
define(["require", "exports"], function(require, exports) {
/**
* The EventDispatcher class provides strongly typed events.
*/
var EventDispatcher = (function() {
function EventDispatcher() {
this.listeners = [];
}
EventDispatcher.prototype.hasEventListener = function(type, listener) {
var exists = false;
for (var i = 0; i < this.listeners.length; i++) {
if (this.listeners[i].type === type && this.listeners[i].listener === listener) {
exists = true;
break;
}
}
return exists;
};
EventDispatcher.prototype.addEventListener = function(typeStr, listenerFunction) {
if (this.hasEventListener(typeStr, listenerFunction))
return;
this.listeners.push({
type: typeStr,
listener: listenerFunction
});
};
EventDispatcher.prototype.removeEventListener = function(typeStr, listenerFunction) {
for (var i = 0; i < this.listeners.length; i++) {
if (this.listeners[i].type === typeStr && this.listeners[i].listener === listenerFunction)
this.listeners.splice(i, 1);
}
};
EventDispatcher.prototype.dispatchEvent = function(event) {
for (var i = 0; i < this.listeners.length; i++) {
if (this.listeners[i].type === event.getType())
this.listeners[i].listener.call(this, event);
}
};
return EventDispatcher;
})();
exports.EventDispatcher = EventDispatcher;
var DefaultListener = (function(_super) {
__extends(DefaultListener, _super);
function DefaultListener() {
_super.call(this);
}
DefaultListener.prototype.onConnect = function(controller) {
controller.dispatchEvent(new LeapEvent(LeapEvent.LEAPMOTION_CONNECTED, this));
};
DefaultListener.prototype.onDisconnect = function(controller) {
controller.dispatchEvent(new LeapEvent(LeapEvent.LEAPMOTION_DISCONNECTED, this));
};
DefaultListener.prototype.onExit = function(controller) {
controller.dispatchEvent(new LeapEvent(LeapEvent.LEAPMOTION_EXIT, this));
};
DefaultListener.prototype.onFrame = function(controller, frame) {
controller.dispatchEvent(new LeapEvent(LeapEvent.LEAPMOTION_FRAME, this, frame));
};
DefaultListener.prototype.onInit = function(controller) {
controller.dispatchEvent(new LeapEvent(LeapEvent.LEAPMOTION_INIT, this));
};
return DefaultListener;
})(EventDispatcher);
exports.DefaultListener = DefaultListener;
var LeapEvent = (function() {
function LeapEvent(type, targetListener, frame) {
if (frame === void 0) {
frame = null;
}
this._type = type;
this._target = targetListener;
this.frame = frame;
}
LeapEvent.prototype.getTarget = function() {
return this._target;
};
LeapEvent.prototype.getType = function() {
return this._type;
};
LeapEvent.LEAPMOTION_INIT = "leapMotionInit";
LeapEvent.LEAPMOTION_CONNECTED = "leapMotionConnected";
LeapEvent.LEAPMOTION_DISCONNECTED = "leapMotionDisconnected";
LeapEvent.LEAPMOTION_EXIT = "leapMotionExit";
LeapEvent.LEAPMOTION_FRAME = "leapMotionFrame";
return LeapEvent;
})();
exports.LeapEvent = LeapEvent;
/**
* LeapUtil is a collection of static utility functions.
*
*/
var LeapUtil = (function() {
function LeapUtil() {}
/**
* Convert an angle measure from radians to degrees.
*
* @param radians
* @return The value, in degrees.
*
*/
LeapUtil.toDegrees = function(radians) {
return radians * 180 / Math.PI;
};
/**
* Determines if a value is equal to or less than 0.00001.
*
* @return True, if equal to or less than 0.00001; false otherwise.
*/
LeapUtil.isNearZero = function(value) {
return Math.abs(value) <= LeapUtil.EPSILON;
};
/**
* Determines if all Vector3 components is equal to or less than 0.00001.
*
* @return True, if equal to or less than 0.00001; false otherwise.
*/
LeapUtil.vectorIsNearZero = function(inVector) {
return this.isNearZero(inVector.x) && this.isNearZero(inVector.y) && this.isNearZero(inVector.z);
};
/**
* Create a new matrix with just the rotation block from the argument matrix
*/
LeapUtil.extractRotation = function(mtxTransform) {
return new Matrix(mtxTransform.xBasis, mtxTransform.yBasis, mtxTransform.zBasis);
};
/**
* Returns a matrix representing the inverse rotation by simple transposition of the rotation block.
*/
LeapUtil.rotationInverse = function(mtxRot) {
return new Matrix(new Vector3(mtxRot.xBasis.x, mtxRot.yBasis.x, mtxRot.zBasis.x), new Vector3(mtxRot.xBasis.y, mtxRot.yBasis.y, mtxRot.zBasis.y), new Vector3(mtxRot.xBasis.z, mtxRot.yBasis.z, mtxRot.zBasis.z));
};
/**
* Returns a matrix that is the orthonormal inverse of the argument matrix.
* This is only valid if the input matrix is orthonormal
* (the basis vectors are mutually perpendicular and of length 1)
*/
LeapUtil.rigidInverse = function(mtxTransform) {
var rigidInverse = this.rotationInverse(mtxTransform);
rigidInverse.origin = rigidInverse.transformDirection(mtxTransform.origin.opposite());
return rigidInverse;
};
LeapUtil.componentWiseMin = function(vLHS, vRHS) {
return new Vector3(Math.min(vLHS.x, vRHS.x), Math.min(vLHS.y, vRHS.y), Math.min(vLHS.z, vRHS.z));
};
LeapUtil.componentWiseMax = function(vLHS, vRHS) {
return new Vector3(Math.max(vLHS.x, vRHS.x), Math.max(vLHS.y, vRHS.y), Math.max(vLHS.z, vRHS.z));
};
LeapUtil.componentWiseScale = function(vLHS, vRHS) {
return new Vector3(vLHS.x * vRHS.x, vLHS.y * vRHS.y, vLHS.z * vRHS.z);
};
LeapUtil.componentWiseReciprocal = function(inVector) {
return new Vector3(1.0 / inVector.x, 1.0 / inVector.y, 1.0 / inVector.z);
};
LeapUtil.minComponent = function(inVector) {
return Math.min(inVector.x, Math.min(inVector.y, inVector.z));
};
LeapUtil.maxComponent = function(inVector) {
return Math.max(inVector.x, Math.max(inVector.y, inVector.z));
};
/**
* Compute the polar/spherical heading of a vector direction in z/x plane
*/
LeapUtil.heading = function(inVector) {
return Math.atan2(inVector.z, inVector.x);
};
/**
* Compute the spherical elevation of a vector direction in y above the z/x plane
*/
LeapUtil.elevation = function(inVector) {
return Math.atan2(inVector.y, Math.sqrt(inVector.z * inVector.z + inVector.x * inVector.x));
};
/**
* Set magnitude to 1 and bring heading to [-Pi,Pi], elevation into [-Pi/2, Pi/2]
*
* @param vSpherical The Vector3 to convert.
* @return The normalized spherical Vector3.
*
*/
LeapUtil.normalizeSpherical = function(vSpherical) {
var fHeading = vSpherical.y;
var fElevation = vSpherical.z;
while (fElevation <= -Math.PI)
fElevation += LeapUtil.TWO_PI;
while (fElevation > Math.PI)
fElevation -= LeapUtil.TWO_PI;
if (Math.abs(fElevation) > LeapUtil.HALF_PI) {
fHeading += Math.PI;
fElevation = fElevation > 0 ? (Math.PI - fElevation) : -(Math.PI + fElevation);
}
while (fHeading <= -Math.PI)
fHeading += LeapUtil.TWO_PI;
while (fHeading > Math.PI)
fHeading -= LeapUtil.TWO_PI;
return new Vector3(1, fHeading, fElevation);
};
/**
* Convert from Cartesian (rectangular) coordinates to spherical coordinates
* (magnitude, heading, elevation).
*
* @param vCartesian The Vector3 to convert.
* @return The cartesian Vector3 converted to spherical.
*
*/
LeapUtil.cartesianToSpherical = function(vCartesian) {
return new Vector3(vCartesian.magnitude(), this.heading(vCartesian), this.elevation(vCartesian));
};
/**
* Convert from spherical coordinates (magnitude, heading, elevation) to
* Cartesian (rectangular) coordinates.
*
* @param vSpherical The Vector3 to convert.
* @return The spherical Vector3 converted to cartesian.
*
*/
LeapUtil.sphericalToCartesian = function(vSpherical) {
var fMagnitude = vSpherical.x;
var fCosHeading = Math.cos(vSpherical.y);
var fSinHeading = Math.sin(vSpherical.y);
var fCosElevation = Math.cos(vSpherical.z);
var fSinElevation = Math.sin(vSpherical.z);
return new Vector3(fCosHeading * fCosElevation * fMagnitude, fSinElevation * fMagnitude, fSinHeading * fCosElevation * fMagnitude);
};
/**
* Clamps a value between a minimum Number and maximum Number value.
*
* @param inVal The number to clamp.
* @param minVal The minimum value.
* @param maxVal The maximum value.
* @return The value clamped between minVal and maxVal.
*
*/
LeapUtil.clamp = function(inVal, minVal, maxVal) {
return (inVal < minVal) ? minVal : ((inVal > maxVal) ? maxVal : inVal);
};
/**
* Linearly interpolates between two Numbers.
*
* @param a A number.
* @param b A number.
* @param coefficient The interpolation coefficient [0-1].
* @return The interpolated number.
*
*/
LeapUtil.lerp = function(a, b, coefficient) {
return a + ((b - a) * coefficient);
};
/**
* Linearly interpolates between two Vector3 objects.
*
* @param vec1 A Vector3 object.
* @param vec2 A Vector3 object.
* @param coefficient The interpolation coefficient [0-1].
* @return A new interpolated Vector3 object.
*
*/
LeapUtil.lerpVector = function(vec1, vec2, coefficient) {
return vec1.plus(vec2.minus(vec1).multiply(coefficient));
};
/** The constant pi as a single precision floating point number. */
LeapUtil.PI = 3.1415926536;
/**
* The constant ratio to convert an angle measure from degrees to radians.
* Multiply a value in degrees by this constant to convert to radians.
*/
LeapUtil.DEG_TO_RAD = 0.0174532925;
/**
* The constant ratio to convert an angle measure from radians to degrees.
* Multiply a value in radians by this constant to convert to degrees.
*/
LeapUtil.RAD_TO_DEG = 57.295779513;
/**
* Pi * 2.
*/
LeapUtil.TWO_PI = Math.PI + Math.PI;
/**
* Pi * 0.5.
*/
LeapUtil.HALF_PI = Math.PI * 0.5;
/**
* Represents the smallest positive single value greater than zero.
*/
LeapUtil.EPSILON = 0.00001;
return LeapUtil;
})();
exports.LeapUtil = LeapUtil;
/**
* The Controller class is your main interface to the Leap Motion Controller.
*
* <p>Create an instance of this Controller class to access frames of tracking
* data and configuration information. Frame data can be polled at any time using
* the <code>Controller::frame()</code> . Call <code>frame()</code> or <code>frame(0)</code>
* to get the most recent frame. Set the history parameter to a positive integer
* to access previous frames. A controller stores up to 60 frames in its frame history.</p>
*
* <p>Polling is an appropriate strategy for applications which already have an
* intrinsic update loop, such as a game. You can also implement the Leap::Listener
* interface to handle events as they occur. The Leap dispatches events to the listener
* upon initialization and exiting, on connection changes, and when a new frame
* of tracking data is available. When these events occur, the controller object
* invokes the appropriate callback defined in the Listener interface.</p>
*
* <p>To access frames of tracking data as they become available:</p>
*
* <ul>
* <li>Implement the Listener interface and override the <code>Listener::onFrame()</code> .</li>
* <li>In your <code>Listener::onFrame()</code> , call the <code>Controller::frame()</code> to access the newest frame of tracking data.</li>
* <li>To start receiving frames, create a Controller object and add event listeners to the <code>Controller::addEventListener()</code> .</li>
* </ul>
*
* <p>When an instance of a Controller object has been initialized,
* it calls the <code>Listener::onInit()</code> when the listener is ready for use.
* When a connection is established between the controller and the Leap,
* the controller calls the <code>Listener::onConnect()</code> . At this point,
* your application will start receiving frames of data. The controller calls
* the <code>Listener::onFrame()</code> each time a new frame is available.
* If the controller loses its connection with the Leap software or
* device for any reason, it calls the <code>Listener::onDisconnect()</code> .
* If the listener is removed from the controller or the controller is destroyed,
* it calls the <code>Listener::onExit()</code> . At that point, unless the listener
* is added to another controller again, it will no longer receive frames of tracking data.</p>
*
* @author logotype
*
*/
var Controller = (function(_super) {
__extends(Controller, _super);
/**
* Constructs a Controller object.
* @param host IP or hostname of the computer running the Leap software.
* (currently only supported for socket connections).
*
*/
function Controller(host) {
var _this = this;
if (host === void 0) {
host = null;
}
_super.call(this);
/**
* @private
* History of frame of tracking data from the Leap.
*/
this.frameHistory = [];
/**
* @private
* Reports whether this Controller is connected to the Leap Motion Controller.
*/
this._isConnected = false;
/**
* @private
* Reports whether gestures is enabled.
*/
this._isGesturesEnabled = false;
this.listener = new DefaultListener();
if (!host) {
this.connection = new WebSocket("ws://localhost:6437/v6.json");
} else {
this.connection = new WebSocket("ws://" + host + ":6437/v6.json");
}
this.listener.onInit(this);
this.connection.onopen = function(event) {
_this._isConnected = true;
_this.listener.onConnect(_this);
var focusedState = {};
focusedState.focused = true;
_this.connection.send(JSON.stringify(focusedState));
};
this.connection.onclose = function(data) {
_this._isConnected = false;
_this.listener.onDisconnect(_this);
};
this.connection.onmessage = function(data) {
var i;
var json;
var currentFrame;
var hand;
var pointable;
var gesture;
var isTool;
var length;
var type;
json = JSON.parse(data.data);
currentFrame = new Frame();
currentFrame.controller = _this;
// Hands
if (typeof json.hands !== "undefined") {
i = 0;
length = json.hands.length;
for (i = 0; i < length; i++) {
hand = new Hand();
hand.frame = currentFrame;
hand.direction = new Vector3(json.hands[i].direction[0], json.hands[i].direction[1], json.hands[i].direction[2]);
hand.id = json.hands[i].id;
hand.palmNormal = new Vector3(json.hands[i].palmNormal[0], json.hands[i].palmNormal[1], json.hands[i].palmNormal[2]);
hand.palmPosition = new Vector3(json.hands[i].palmPosition[0], json.hands[i].palmPosition[1], json.hands[i].palmPosition[2]);
hand.stabilizedPalmPosition = new Vector3(json.hands[i].stabilizedPalmPosition[0], json.hands[i].stabilizedPalmPosition[1], json.hands[i].stabilizedPalmPosition[2]);
hand.palmVelocity = new Vector3(json.hands[i].palmPosition[0], json.hands[i].palmPosition[1], json.hands[i].palmPosition[2]);
hand.rotation = new Matrix(new Vector3(json.hands[i].r[0][0], json.hands[i].r[0][1], json.hands[i].r[0][2]), new Vector3(json.hands[i].r[1][0], json.hands[i].r[1][1], json.hands[i].r[1][2]), new Vector3(json.hands[i].r[2][0], json.hands[i].r[2][1], json.hands[i].r[2][2]));
hand.scaleFactorNumber = json.hands[i].s;
hand.sphereCenter = new Vector3(json.hands[i].sphereCenter[0], json.hands[i].sphereCenter[1], json.hands[i].sphereCenter[2]);
hand.sphereRadius = json.hands[i].sphereRadius;
hand.timeVisible = json.hands[i].timeVisible;
hand.translationVector = new Vector3(json.hands[i].t[0], json.hands[i].t[1], json.hands[i].t[2]);
currentFrame.hands.push(hand);
}
}
currentFrame.id = json.id;
currentFrame.currentFramesPerSecond = json.currentFramesPerSecond;
// InteractionBox
if (typeof json.interactionBox !== "undefined") {
currentFrame.interactionBox = new InteractionBox();
currentFrame.interactionBox.center = new Vector3(json.interactionBox.center[0], json.interactionBox.center[1], json.interactionBox.center[2]);
currentFrame.interactionBox.width = json.interactionBox.size[0];
currentFrame.interactionBox.height = json.interactionBox.size[1];
currentFrame.interactionBox.depth = json.interactionBox.size[2];
}
// Pointables
if (typeof json.pointables !== "undefined") {
i = 0;
length = json.pointables.length;
for (i = 0; i < length; i++) {
isTool = json.pointables[i].tool;
if (isTool)
pointable = new Tool();
else
pointable = new Finger();
pointable.frame = currentFrame;
pointable.id = json.pointables[i].id;
pointable.hand = Controller.getHandByID(currentFrame, json.pointables[i].handId);
pointable.length = json.pointables[i].length;
pointable.direction = new Vector3(json.pointables[i].direction[0], json.pointables[i].direction[1], json.pointables[i].direction[2]);
pointable.tipPosition = new Vector3(json.pointables[i].tipPosition[0], json.pointables[i].tipPosition[1], json.pointables[i].tipPosition[2]);
pointable.stabilizedTipPosition = new Vector3(json.pointables[i].stabilizedTipPosition[0], json.pointables[i].stabilizedTipPosition[1], json.pointables[i].stabilizedTipPosition[2]);
pointable.tipVelocity = new Vector3(json.pointables[i].tipVelocity[0], json.pointables[i].tipVelocity[1], json.pointables[i].tipVelocity[2]);
pointable.touchDistance = json.pointables[i].touchDistance;
pointable.timeVisible = json.pointables[i].timeVisible;
currentFrame.pointables.push(pointable);
switch (json.pointables[i].touchZone) {
case "hovering":
pointable.touchZone = Zone.ZONE_HOVERING;
break;
case "touching":
pointable.touchZone = Zone.ZONE_TOUCHING;
break;
default:
pointable.touchZone = Zone.ZONE_NONE;
break;
}
if (pointable.hand)
pointable.hand.pointables.push(pointable);
if (isTool) {
pointable.isTool = true;
pointable.isFinger = false;
pointable.width = json.pointables[i].width;
currentFrame.tools.push(pointable);
if (pointable.hand)
pointable.hand.tools.push(pointable);
} else {
pointable.isTool = false;
pointable.isFinger = true;
currentFrame.fingers.push(pointable);
if (pointable.hand)
pointable.hand.fingers.push(pointable);
}
}
}
// Gestures
if (typeof json.gestures !== "undefined") {
i = 0;
length = json.gestures.length;
for (i = 0; i < length; i++) {
switch (json.gestures[i].type) {
case "circle":
gesture = new CircleGesture();
type = Type.TYPE_CIRCLE;
var circle = gesture;
circle.center = new Vector3(json.gestures[i].center[0], json.gestures[i].center[1], json.gestures[i].center[2]);
circle.normal = new Vector3(json.gestures[i].normal[0], json.gestures[i].normal[1], json.gestures[i].normal[2]);
circle.progress = json.gestures[i].progress;
circle.radius = json.gestures[i].radius;
break;
case "swipe":
gesture = new SwipeGesture();
type = Type.TYPE_SWIPE;
var swipe = gesture;
swipe.startPosition = new Vector3(json.gestures[i].startPosition[0], json.gestures[i].startPosition[1], json.gestures[i].startPosition[2]);
swipe.position = new Vector3(json.gestures[i].position[0], json.gestures[i].position[1], json.gestures[i].position[2]);
swipe.direction = new Vector3(json.gestures[i].direction[0], json.gestures[i].direction[1], json.gestures[i].direction[2]);
swipe.speed = json.gestures[i].speed;
break;
case "screenTap":
gesture = new ScreenTapGesture();
type = Type.TYPE_SCREEN_TAP;
var screenTap = gesture;
screenTap.position = new Vector3(json.gestures[i].position[0], json.gestures[i].position[1], json.gestures[i].position[2]);
screenTap.direction = new Vector3(json.gestures[i].direction[0], json.gestures[i].direction[1], json.gestures[i].direction[2]);
screenTap.progress = json.gestures[i].progress;
break;
case "keyTap":
gesture = new KeyTapGesture();
type = Type.TYPE_KEY_TAP;
var keyTap = gesture;
keyTap.position = new Vector3(json.gestures[i].position[0], json.gestures[i].position[1], json.gestures[i].position[2]);
keyTap.direction = new Vector3(json.gestures[i].direction[0], json.gestures[i].direction[1], json.gestures[i].direction[2]);
keyTap.progress = json.gestures[i].progress;
break;
default:
throw new Error("unkown gesture type");
}
var j = 0;
var lengthInner = 0;
if (typeof json.gestures[i].handIds !== "undefined") {
j = 0;
lengthInner = json.gestures[i].handIds.length;
for (j = 0; j < lengthInner; ++j) {
var gestureHand = Controller.getHandByID(currentFrame, json.gestures[i].handIds[j]);
gesture.hands.push(gestureHand);
}
}
if (typeof json.gestures[i].pointableIds !== "undefined") {
j = 0;
lengthInner = json.gestures[i].pointableIds.length;
for (j = 0; j < lengthInner; ++j) {
var gesturePointable = Controller.getPointableByID(currentFrame, json.gestures[i].pointableIds[j]);
if (gesturePointable) {
gesture.pointables.push(gesturePointable);
}
}
if (gesture instanceof CircleGesture && gesture.pointables.length > 0) {
gesture.pointable = gesture.pointables[0];
}
}
gesture.frame = currentFrame;
gesture.id = json.gestures[i].id;
gesture.duration = json.gestures[i].duration;
gesture.durationSeconds = gesture.duration / 1000000;
switch (json.gestures[i].state) {
case "start":
gesture.state = State.STATE_START;
break;
case "update":
gesture.state = State.STATE_UPDATE;
break;
case "stop":
gesture.state = State.STATE_STOP;
break;
default:
gesture.state = State.STATE_INVALID;
}
gesture.type = type;
currentFrame._gestures.push(gesture);
}
}
// Rotation (since last frame), interpolate for smoother motion
if (json.r)
currentFrame.rotation = new Matrix(new Vector3(json.r[0][0], json.r[0][1], json.r[0][2]), new Vector3(json.r[1][0], json.r[1][1], json.r[1][2]), new Vector3(json.r[2][0], json.r[2][1], json.r[2][2]));
// Scale factor (since last frame), interpolate for smoother motion
currentFrame.scaleFactorNumber = json.s;
// Translation (since last frame), interpolate for smoother motion
if (json.t)
currentFrame.translationVector = new Vector3(json.t[0], json.t[1], json.t[2]);
// Timestamp
currentFrame.timestamp = json.timestamp;
// Add frame to history
if (_this.frameHistory.length > 59)
_this.frameHistory.splice(59, 1);
_this.frameHistory.unshift(_this.latestFrame);
_this.latestFrame = currentFrame;
_this.listener.onFrame(_this, _this.latestFrame);
};
}
/**
* Finds a Hand object by ID.
*
* @param frame The Frame object in which the Hand contains
* @param id The ID of the Hand object
* @return The Hand object if found, otherwise null
*
*/
Controller.getHandByID = function(frame, id) {
var returnValue = null;
var i = 0;
for (i = 0; i < frame.hands.length; i++) {
if (frame.hands[i].id === id) {
returnValue = frame.hands[i];
break;
}
}
return returnValue;
};
/**
* Finds a Pointable object by ID.
*
* @param frame The Frame object in which the Pointable contains
* @param id The ID of the Pointable object
* @return The Pointable object if found, otherwise null
*
*/
Controller.getPointableByID = function(frame, id) {
var returnValue = null;
var i = 0;
for (i = 0; i < frame.pointables.length; i++) {
if (frame.pointables[i].id === id) {
returnValue = frame.pointables[i];
break;
}
}
return returnValue;
};
/**
* Returns a frame of tracking data from the Leap.
*
* <p>Use the optional history parameter to specify which frame to retrieve.
* Call <code>frame()</code> or <code>frame(0)</code> to access the most recent frame;
* call <code>frame(1)</code> to access the previous frame, and so on. If you use a history value
* greater than the number of stored frames, then the controller returns
* an invalid frame.</p>
*
* @param history The age of the frame to return, counting backwards from
* the most recent frame (0) into the past and up to the maximum age (59).
*
* @return The specified frame; or, if no history parameter is specified,
* the newest frame. If a frame is not available at the specified
* history position, an invalid Frame is returned.
*
*/
Controller.prototype.frame = function(history) {
if (history === void 0) {
history = 0;
}
if (history >= this.frameHistory.length)
return Frame.invalid();
else
return this.frameHistory[history];
};
/**
* Update the object that receives direct updates from the Leap Motion Controller.
*
* <p>The default listener will make the controller dispatch flash events.
* You can override this behaviour, by implementing the IListener interface
* in your own classes, and use this method to set the listener to your
* own implementation.</p>
*
* @param listener
*/
Controller.prototype.setListener = function(listener) {
this.listener = listener;
};
/**
* Enables or disables reporting of a specified gesture type.
*
* <p>By default, all gesture types are disabled. When disabled, gestures of
* the disabled type are never reported and will not appear in the frame
* gesture list.</p>
*
* <p>As a performance optimization, only enable recognition for the types
* of movements that you use in your application.</p>
*
* @param type The type of gesture to enable or disable. Must be a member of the Gesture::Type enumeration.
* @param enable True, to enable the specified gesture type; False, to disable.
*
*/
Controller.prototype.enableGesture = function(type, enable) {
if (enable === void 0) {
enable = true;
}
var enableObject = {};
if (enable) {
this._isGesturesEnabled = true;
enableObject.enableGestures = true;
} else {
this._isGesturesEnabled = false;
enableObject.enableGestures = false;
}
this.connection.send(JSON.stringify(enableObject));
};
/**
* Reports whether the specified gesture type is enabled.
*
* @param type The Gesture.TYPE parameter.
* @return True, if the specified type is enabled; false, otherwise.
*
*/
Controller.prototype.isGestureEnabled = function(type) {
return this._isGesturesEnabled;
};
/**
* Reports whether this Controller is connected to the Leap Motion Controller.
*
* <p>When you first create a Controller object, <code>isConnected()</code> returns false.
* After the controller finishes initializing and connects to
* the Leap, <code>isConnected()</code> will return true.</p>
*
* <p>You can either handle the onConnect event using a event listener
* or poll the <code>isConnected()</code> if you need to wait for your
* application to be connected to the Leap before performing
* some other operation.</p>
*
* @return True, if connected; false otherwise.
*
*/
Controller.prototype.isConnected = function() {
return this._isConnected;
};
return Controller;
})(EventDispatcher);
exports.Controller = Controller;
/**
* The InteractionBox class represents a box-shaped region completely within
* the field of view of the Leap Motion controller.
*
* <p>The interaction box is an axis-aligned rectangular prism and provides
* normalized coordinates for hands, fingers, and tools within this box.
* The InteractionBox class can make it easier to map positions in the
* Leap Motion coordinate system to 2D or 3D coordinate systems used
* for application drawing.</p>
*
* <p>The InteractionBox region is defined by a center and dimensions along the x, y, and z axes.</p>
*
* @author logotype
*
*/
var InteractionBox = (function() {
function InteractionBox() {}
/**
* Converts a position defined by normalized InteractionBox coordinates
* into device coordinates in millimeters.
*
* This function performs the inverse of normalizePoint().
*
* @param normalizedPosition The input position in InteractionBox coordinates.
* @return The corresponding denormalized position in device coordinates.
*
*/
InteractionBox.prototype.denormalizePoint = function(normalizedPosition) {
var vec = Vector3.invalid();
vec.x = (normalizedPosition.x - 0.5) * this.width + this.center.x;
vec.y = (normalizedPosition.y - 0.5) * this.height + this.center.y;
vec.z = (normalizedPosition.z - 0.5) * this.depth + this.center.z;
return vec;
};
/**
* Normalizes the coordinates of a point using the interaction box.
*
* <p>Coordinates from the Leap Motion frame of reference (millimeters) are
* converted to a range of [0..1] such that the minimum value of the
* InteractionBox maps to 0 and the maximum value of the InteractionBox maps to 1.</p>
*
* @param position The input position in device coordinates.
* @param clamp Whether or not to limit the output value to the range [0,1]
* when the input position is outside the InteractionBox. Defaults to true.
* @return The normalized position.
*
*/
InteractionBox.prototype.normalizePoint = function(position, clamp) {
if (clamp === void 0) {
clamp = true;
}
var vec = Vector3.invalid();
vec.x = ((position.x - this.center.x) / this.width) + 0.5;
vec.y = ((position.y - this.center.y) / this.height) + 0.5;
vec.z = ((position.z - this.center.z) / this.depth) + 0.5;
if (clamp) {
vec.x = Math.min(Math.max(vec.x, 0), 1);
vec.y = Math.min(Math.max(vec.y, 0), 1);
vec.z = Math.min(Math.max(vec.z, 0), 1);
}
return vec;
};
/**
* Reports whether this is a valid InteractionBox object.
* @return True, if this InteractionBox object contains valid data.
*
*/
InteractionBox.prototype.isValid = function() {
return this.center.isValid() && this.width > 0 && this.height > 0 && this.depth > 0;
};
/**
* Compare InteractionBox object equality/inequality.
*
* <p>Two InteractionBox objects are equal if and only if both InteractionBox
* objects represent the exact same InteractionBox and both InteractionBoxes are valid.</p>
*
* @param other
* @return
*
*/
InteractionBox.prototype.isEqualTo = function(other) {
if (!this.isValid() || !other.isValid())
return false;
if (!this.center.isEqualTo(other.center))
return false;
if (this.depth != other.depth)
return false;
if (this.height != other.height)
return false;
if (this.width != other.width)
return false;
return true;
};
/**
* Returns an invalid InteractionBox object.
*
* <p>You can use the instance returned by this function in comparisons
* testing whether a given InteractionBox instance is valid or invalid.
* (You can also use the <code>InteractionBox.isValid()</code> function.)</p>
*
* @return The invalid InteractionBox instance.
*
*/
InteractionBox.invalid = function() {
return new InteractionBox();
};
/**
* Writes a brief, human readable description of the InteractionBox object.
* @return A description of the InteractionBox as a string.
*
*/
InteractionBox.prototype.toString = function() {
return "[InteractionBox depth:" + this.depth + " height:" + this.height + " width:" + this.width + "]";
};
return InteractionBox;
})();
exports.InteractionBox = InteractionBox;
/**
* The Pointable class reports the physical characteristics of a detected finger or tool.
* Both fingers and tools are classified as Pointable objects. Use the Pointable.isFinger
* property to determine whether a Pointable object represents a finger. Use the
* Pointable.isTool property to determine whether a Pointable object represents a tool.
* The Leap classifies a detected entity as a tool when it is thinner, straighter,
* and longer than a typical finger.
*
* <p>Note that Pointable objects can be invalid, which means that they do not contain valid
* tracking data and do not correspond to a physical entity. Invalid Pointable objects
* can be the result of asking for a Pointable object using an ID from an earlier frame
* when no Pointable objects with that ID exist in the current frame. A Pointable object
* created from the Pointable constructor is also invalid. Test for validity with
* the <code>Pointable.isValid()</code> function.</p>
*
* @author logotype
*
*/
/**
* Defines the values for reporting the state of a Pointable object in relation to an adaptive touch plane.
*/
var Zone;
(function(Zone) {
/**
* The Pointable object is too far from the plane to be considered hovering or touching.
*
* <p>Defines the values for reporting the state of a Pointable object in relation to an adaptive touch plane.</p>
*/
Zone[Zone["ZONE_NONE"] = 0] = "ZONE_NONE";
/**
* The Pointable object is close to, but not touching the plane.
*
* <p>Defines the values for reporting the state of a Pointable object in relation to an adaptive touch plane.</p>
*/
Zone[Zone["ZONE_HOVERING"] = 1] = "ZONE_HOVERING";
/**
* The Pointable has penetrated the plane.
*
* <p>Defines the values for reporting the state of a Pointable object in relation to an adaptive touch plane.</p>
*/
Zone[Zone["ZONE_TOUCHING"] = 2] = "ZONE_TOUCHING";
})(Zone || (Zone = {}));
var Pointable = (function() {
function Pointable() {
/**
* The current touch zone of this Pointable object.
*
* <p>The Leap Motion software computes the touch zone based on a
* floating touch plane that adapts to the user's finger movement
* and hand posture. The Leap Motion software interprets purposeful
* movements toward this plane as potential touch points.
* When a Pointable moves close to the adaptive touch plane,
* it enters the "hovering" zone. When a Pointable reaches or
* passes through the plane, it enters the "touching" zone.</p>
*
* <p>The possible states are present in the Zone enum of this class:</p>
*
* <code>Zone.NONE – The Pointable is outside the hovering zone.
* Zone.HOVERING – The Pointable is close to, but not touching the touch plane.
* Zone.TOUCHING – The Pointable has penetrated the touch plane.</code>
*
* <p>The touchDistance value provides a normalized indication of the
* distance to the touch plane when the Pointable is in the hovering
* or touching zones.</p>
*
*/
this.touchZone = Zone.ZONE_NONE;
/**
* A value proportional to the distance between this Pointable
* object and the adaptive touch plane.
*
* <p>The touch distance is a value in the range [-1, 1].
* The value 1.0 indicates the Pointable is at the far edge of
* the hovering zone. The value 0 indicates the Pointable is
* just entering the touching zone. A value of -1.0 indicates
* the Pointable is firmly within the touching zone.
* Values in between are proportional to the distance from the plane.
* Thus, the touchDistance of 0.5 indicates that the Pointable
* is halfway into the hovering zone.</p>
*
* <p>You can use the touchDistance value to modulate visual
* feedback given to the user as their fingers close in on a
* touch target, such as a button.</p>
*
*/
this.touchDistance = 0;
/**
* The estimated length of the finger or tool in millimeters.
*
* <p>The reported length is the visible length of the finger or tool from
* the hand to tip.</p>
*
* <p>If the length isn't known, then a value of 0 is returned.</p>
*/
this.length = 0;
/**
* The estimated width of the finger or tool in millimeters.
*
* <p>The reported width is the average width of the visible portion
* of the finger or tool from the hand to the tip.</p>
*
* <p>If the width isn't known, then a value of 0 is returned.</p>
*/
this.width = 0;
this.direction = Vector3.invalid();
this.tipPosition = Vector3.invalid();
this.tipVelocity = Vector3.invalid();
}
/**
* Reports whether this is a valid Pointable object.
* @return True if <code>direction</code>, <code>tipPosition</code> and <code>tipVelocity</code> are true.
*/
Pointable.prototype.isValid = function() {
var returnValue = false;
if ((this.direction && this.direction.isValid()) && (this.tipPosition && this.tipPosition.isValid()) && (this.tipVelocity && this.tipVelocity.isValid()))
returnValue = true;
return returnValue;
};
/**
* Compare Pointable object equality/inequality.
*
* <p>Two Pointable objects are equal if and only if both Pointable
* objects represent the exact same physical entities in
* the same frame and both Pointable objects are valid.</p>
*
* @param other The Pointable to compare with.
* @return True; if equal, False otherwise.
*
*/
Pointable.prototype.isEqualTo = function(other) {
if (!this.isValid() || !other.isValid())
return false;
if (this.frame != other.frame)
return false;
if (this.hand != other.hand)
return false;
if (!this.direction.isEqualTo(other.direction))
return false;
if (this.length != other.length)
return false;
if (this.width != other.width)
return false;
if (this.id != other.id)
return false;
if (!this.tipPosition.isEqualTo(other.tipPosition))
return false;
if (!this.tipVelocity.isEqualTo(other.tipVelocity))
return false;
if (this.isFinger != other.isFinger || this.isTool != other.isTool)
return false;
return true;
};
/**
* Returns an invalid Pointable object.
*
* <p>You can use the instance returned by this in
* comparisons testing whether a given Pointable instance
* is valid or invalid.<br/>
* (You can also use the <code>Pointable.isValid()</code> function.)</p>
*
* @return The invalid Pointable instance.
*
*/
Pointable.invalid = function() {
return new Pointable();
};
/**
* A string containing a brief, human readable description of the Pointable object.
*/
Pointable.prototype.toString = function() {
return "[Pointable direction: " + this.direction + " tipPosition: " + this.tipPosition + " tipVelocity: " + this.tipVelocity + "]";
};
return Pointable;
})();
exports.Pointable = Pointable;
/**
* The Gesture class represents a recognized movement by the user.
*
* <p>The Leap watches the activity within its field of view for certain movement
* patterns typical of a user gesture or command. For example, a movement from
* side to side with the hand can indicate a swipe gesture, while a finger poking
* forward can indicate a screen tap gesture.</p>
*
* <p>When the Leap recognizes a gesture, it assigns an ID and adds a Gesture object
* to the frame gesture list. For continuous gestures, which occur over many frames,
* the Leap updates the gesture by adding a Gesture object having the same ID and
* updated properties in each subsequent frame.</p>
*
* <p><strong>Important: Recognition for each type of gesture must be enabled using the
* <code>Controller.enableGesture()</code> function; otherwise no gestures are recognized
* or reported.</strong></p>
*
* <p>Subclasses of Gesture define the properties for the specific movement
* patterns recognized by the Leap.</p>
*
* <p>The Gesture subclasses for include:
* <pre>
* CircleGesture – A circular movement by a finger.
* SwipeGesture – A straight line movement by the hand with fingers extended.
* ScreenTapGesture – A forward tapping movement by a finger.
* KeyTapGesture – A downward tapping movement by a finger.
* </pre>
* </p>
*
* <p>Circle and swipe gestures are continuous and these objects can have a state
* of start, update, and stop.</p>
*
* <p>The screen tap gesture is a discrete gesture. The Leap only creates a single
* ScreenTa