├── .gitattributes ├── .gitignore ├── .jshintrc ├── .npmignore ├── LICENSE ├── README.md ├── dist ├── aframe-leap-hands.js ├── aframe-leap-hands.js.map ├── aframe-leap-hands.module.js ├── aframe-leap-hands.module.js.map ├── aframe-leap-hands.umd.js └── aframe-leap-hands.umd.js.map ├── examples ├── grabbing │ └── index.html ├── index.js └── leap-hands │ └── index.html ├── lib ├── circular-array.js ├── leap.hand-hold.js ├── leap.hand-mesh.js ├── leap.js └── leap.transform.js ├── package-lock.json ├── package.json └── src ├── helpers ├── hand-body.js └── intersector.js ├── index.js ├── leap-hand.js └── leap-system.js /.gitattributes: -------------------------------------------------------------------------------- 1 | text=true 2 | * eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | **/bundle.js 4 | npm-debug.log 5 | build 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "THREE": false, 4 | "AFRAME": false, 5 | "CANNON": false, 6 | "window": false, 7 | "document": false, 8 | "require": false, 9 | "console": false, 10 | "module": false 11 | }, 12 | "bitwise": false, 13 | "browser": true, 14 | "eqeqeq": true, 15 | "esnext": true, 16 | "expr": true, 17 | "forin": true, 18 | "immed": true, 19 | "latedef": "nofunc", 20 | "laxbreak": true, 21 | "maxlen": 100, 22 | "newcap": true, 23 | "noarg": true, 24 | "noempty": true, 25 | "noyield": true, 26 | "quotmark": "single", 27 | "smarttabs": false, 28 | "trailing": true, 29 | "undef": true, 30 | "unused": true, 31 | "white": false 32 | } 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | assets 2 | build 3 | examples 4 | scripts 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Don McCurdy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A-Frame VR `leap-hand` for Leap Motion 2 | 3 | [![Latest NPM release](https://img.shields.io/npm/v/aframe-leap-hands.svg)](https://www.npmjs.com/package/aframe-leap-hands) 4 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/donmccurdy/aframe-leap-hands/master/LICENSE) 5 | ![Work in progress](https://img.shields.io/badge/status-experimental-orange.svg) 6 | 7 | A-Frame VR component for Leap Motion controller. 8 | 9 | ![4d731aec-193d-463c-8189-d54c5e023206-20847-0002ac9dc2db9992](https://cloud.githubusercontent.com/assets/1848368/23005782/0909f4aa-f3cc-11e6-83f3-072b53374000.gif) 10 | 11 | ## Installation (Scripts) 12 | 13 | In the [dist/](https://github.com/openleap/aframe-leap-hands/tree/master/dist) folder, download either the minified or unminified build. Include the scripts on your page, and all components are automatically registered for you. 14 | 15 | Or, use a CDN-hosted version: 16 | 17 | ```html 18 | 19 | ``` 20 | 21 | ## Installation (NPM) 22 | 23 | Using NPM and Browserify or Webpack: 24 | 25 | ```bash 26 | npm install --save aframe-leap-hands 27 | ``` 28 | 29 | ```javascript 30 | require('aframe'); 31 | require('aframe-leap-hands'); 32 | ``` 33 | 34 | ## Usage 35 | 36 | ```html 37 | 38 | 39 | 40 | 41 | ``` 42 | 43 | ## Options 44 | 45 | ### `leap-hand` component: 46 | 47 | | Property | Default | Description | 48 | |--------------------|--------------|-------------| 49 | | hand | — | `left` or `right` | 50 | | enablePhysics | false | Adds a physics body for [aframe-physics-system](https://github.com/donmccurdy/aframe-physics-system). | 51 | | holdDistance | 0.2 | Holding distance, in meters. | 52 | | holdDebounce | 100 | Debouncing on grip, in milliseconds. | 53 | | holdSelector | `[holdable]` | Selector that limits which objects may be held. | 54 | | holdSensitivity | 0.95 | 0—1. | 55 | | releaseSensitivity | 0.75 | 0–1. | 56 | | debug | false | Shows a grip target indicator. | 57 | 58 | ### `leap` system: 59 | 60 | | Property | Default | Description | 61 | |------------|-----------|-------------| 62 | | vr | true | If true, sets default VR position and quaternion. | 63 | | scale | 0.001 | | 64 | | position | `0 0 0` | | 65 | | quaternion | `0 0 0 1` | | 66 | 67 | For example, to set both hands to **desktop** configuration: 68 | 69 | ```html 70 | 71 | 72 | 73 | 74 | ``` 75 | 76 | ## References: 77 | 78 | ### Official 79 | 80 | + [Leap JS](https://github.com/leapmotion/leapjs) 81 | + [Leap JS Plugins](https://github.com/leapmotion/leapjs-plugins) 82 | + [Leap JS cont'd](https://developer.leapmotion.com/javascript) 83 | + [Leap JS Network](https://github.com/leapmotion/leapjs-network) 84 | + [Leap JS Widgets](https://github.com/leapmotion/leapjs-widgets) 85 | + [Leap JS Rigged THREE.js Hand](https://github.com/leapmotion/leapjs-rigged-hand) 86 | 87 | ### Third-party 88 | 89 | + THREE.LeapMotion Wrapper [discussion](https://community.leapmotion.com/t/three-js-wrapper/769) and [GitHub](https://github.com/scottbyrns/THREE.LeapMotion) 90 | -------------------------------------------------------------------------------- /dist/aframe-leap-hands.js: -------------------------------------------------------------------------------- 1 | !function(t,e,n){function i(n,o){if(!e[n]){if(!t[n]){var s="function"==typeof require&&require;if(!o&&s)return s(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var a=e[n]={exports:{}};t[n][0].call(a.exports,function(e){var r=t[n][1][e];return i(r||e)},a,a.exports)}return e[n].exports}for(var r="function"==typeof require&&require,o=0;o=this.size||t>=this._buf.length))return this._buf[(this.pos-t-1)%this.size]},i.prototype.push=function(t){return this._buf[this.pos%this.size]=t,this.pos++}},{}],3:[function(t,e,n){var i=t("../protocol").chooseProtocol,r=t("events").EventEmitter,o=t("underscore"),s=e.exports=function(t){this.opts=o.defaults(t||{},{host:"127.0.0.1",enableGestures:!1,scheme:this.getScheme(),port:this.getPort(),background:!1,optimizeHMD:!1,requestProtocolVersion:s.defaultProtocolVersion}),this.host=this.opts.host,this.port=this.opts.port,this.scheme=this.opts.scheme,this.protocolVersionVerified=!1,this.background=null,this.optimizeHMD=null,this.on("ready",function(){this.enableGestures(this.opts.enableGestures),this.setBackground(this.opts.background),this.setOptimizeHMD(this.opts.optimizeHMD),this.opts.optimizeHMD?console.log("Optimized for head mounted display usage."):console.log("Optimized for desktop usage.")})};s.defaultProtocolVersion=6,s.prototype.getUrl=function(){return this.scheme+"//"+this.host+":"+this.port+"/v"+this.opts.requestProtocolVersion+".json"},s.prototype.getScheme=function(){return"ws:"},s.prototype.getPort=function(){return 6437},s.prototype.setBackground=function(t){this.opts.background=t,this.protocol&&this.protocol.sendBackground&&this.background!==this.opts.background&&(this.background=this.opts.background,this.protocol.sendBackground(this,this.opts.background))},s.prototype.setOptimizeHMD=function(t){this.opts.optimizeHMD=t,this.protocol&&this.protocol.sendOptimizeHMD&&this.optimizeHMD!==this.opts.optimizeHMD&&(this.optimizeHMD=this.opts.optimizeHMD,this.protocol.sendOptimizeHMD(this,this.opts.optimizeHMD))},s.prototype.handleOpen=function(){this.connected||(this.connected=!0,this.emit("connect"))},s.prototype.enableGestures=function(t){this.gesturesEnabled=!!t,this.send(this.protocol.encode({enableGestures:this.gesturesEnabled}))},s.prototype.handleClose=function(t,e){this.connected&&(this.disconnect(),1001===t&&this.opts.requestProtocolVersion>1&&(this.protocolVersionVerified?this.protocolVersionVerified=!1:this.opts.requestProtocolVersion--),this.startReconnection())},s.prototype.startReconnection=function(){var t=this;this.reconnectionTimer||(this.reconnectionTimer=setInterval(function(){t.reconnect()},500))},s.prototype.stopReconnection=function(){this.reconnectionTimer=clearInterval(this.reconnectionTimer)},s.prototype.disconnect=function(t){if(t||this.stopReconnection(),this.socket)return this.socket.close(),delete this.socket,delete this.protocol,delete this.background,delete this.optimizeHMD,delete this.focusedState,this.connected&&(this.connected=!1,this.emit("disconnect")),!0},s.prototype.reconnect=function(){this.connected?this.stopReconnection():(this.disconnect(!0),this.connect())},s.prototype.handleData=function(t){var e,n=JSON.parse(t);void 0===this.protocol?(e=this.protocol=i(n),this.protocolVersionVerified=!0,this.emit("ready")):e=this.protocol(n),this.emit(e.type,e)},s.prototype.connect=function(){if(!this.socket)return this.socket=this.setupSocket(),!0},s.prototype.send=function(t){this.socket.send(t)},s.prototype.reportFocus=function(t){this.connected&&this.focusedState!==t&&(this.focusedState=t,this.emit(this.focusedState?"focus":"blur"),this.protocol&&this.protocol.sendFocused&&this.protocol.sendFocused(this,this.focusedState))},o.extend(s.prototype,r.prototype)},{"../protocol":15,events:21,underscore:24}],4:[function(t,e,n){var i=e.exports=t("./base"),r=t("underscore"),o=e.exports=function(t){i.call(this,t);var e=this;this.on("ready",function(){e.startFocusLoop()}),this.on("disconnect",function(){e.stopFocusLoop()})};r.extend(o.prototype,i.prototype),o.__proto__=i,o.prototype.useSecure=function(){return"https:"===location.protocol},o.prototype.getScheme=function(){return this.useSecure()?"wss:":"ws:"},o.prototype.getPort=function(){return this.useSecure()?6436:6437},o.prototype.setupSocket=function(){var t=this,e=new WebSocket(this.getUrl());return e.onopen=function(){t.handleOpen()},e.onclose=function(e){t.handleClose(e.code,e.reason)},e.onmessage=function(e){t.handleData(e.data)},e.onerror=function(e){t.useSecure()&&"wss:"===t.scheme&&(t.scheme="ws:",t.port=6437,t.disconnect(),t.connect())},e},o.prototype.startFocusLoop=function(){if(!this.focusDetectorTimer){var t=this,e=null;e=void 0!==document.hidden?"hidden":void 0!==document.mozHidden?"mozHidden":void 0!==document.msHidden?"msHidden":void 0!==document.webkitHidden?"webkitHidden":void 0,void 0===t.windowVisible&&(t.windowVisible=void 0===e||!1===document[e]);var n=window.addEventListener("focus",function(e){t.windowVisible=!0,r()}),i=window.addEventListener("blur",function(e){t.windowVisible=!1,r()});this.on("disconnect",function(){window.removeEventListener("focus",n),window.removeEventListener("blur",i)});var r=function(){var n=void 0===e||!1===document[e];t.reportFocus(n&&t.windowVisible)};r(),this.focusDetectorTimer=setInterval(r,100)}},o.prototype.stopFocusLoop=function(){this.focusDetectorTimer&&(clearTimeout(this.focusDetectorTimer),delete this.focusDetectorTimer)}},{"./base":3,underscore:24}],5:[function(t,e,n){var i=t("__browserify_process"),r=t("./frame"),o=t("./hand"),s=t("./pointable"),a=t("./finger"),c=t("./circular_buffer"),u=t("./pipeline"),h=t("events").EventEmitter,l=t("./gesture").gestureListener,p=t("./dialog"),d=t("underscore"),f=e.exports=function(e){var n=this;e=d.defaults(e||{},{inNode:void 0!==i&&i.versions&&i.versions.node}),this.inNode=e.inNode,e=d.defaults(e||{},{frameEventName:this.useAnimationLoop()?"animationFrame":"deviceFrame",suppressAnimationLoop:!this.useAnimationLoop(),loopWhileDisconnected:!0,useAllPlugins:!1,checkVersion:!0}),this.animationFrameRequested=!1,this.onAnimationFrame=function(t){n.lastConnectionFrame.valid&&n.emit("animationFrame",n.lastConnectionFrame),n.emit("frameEnd",t),n.loopWhileDisconnected&&(!1!==n.connection.focusedState||n.connection.opts.background)?window.requestAnimationFrame(n.onAnimationFrame):n.animationFrameRequested=!1},this.suppressAnimationLoop=e.suppressAnimationLoop,this.loopWhileDisconnected=e.loopWhileDisconnected,this.frameEventName=e.frameEventName,this.useAllPlugins=e.useAllPlugins,this.history=new c(200),this.lastFrame=r.Invalid,this.lastValidFrame=r.Invalid,this.lastConnectionFrame=r.Invalid,this.accumulatedGestures=[],this.checkVersion=e.checkVersion,this.connectionType=void 0===e.connectionType?this.inBrowser()?t("./connection/browser"):t("./connection/node"):e.connectionType,this.connection=new this.connectionType(e),this.streamingCount=0,this.devices={},this.plugins={},this._pluginPipelineSteps={},this._pluginExtendedMethods={},e.useAllPlugins&&this.useRegisteredPlugins(),this.setupFrameEvents(e),this.setupConnectionEvents(),this.startAnimationLoop()};f.prototype.gesture=function(t,e){var n=l(this,t);return void 0!==e&&n.stop(e),n},f.prototype.setBackground=function(t){return this.connection.setBackground(t),this},f.prototype.setOptimizeHMD=function(t){return this.connection.setOptimizeHMD(t),this},f.prototype.inBrowser=function(){return!this.inNode},f.prototype.useAnimationLoop=function(){return this.inBrowser()&&!this.inBackgroundPage()},f.prototype.inBackgroundPage=function(){return"undefined"!=typeof chrome&&chrome.extension&&chrome.extension.getBackgroundPage&&chrome.extension.getBackgroundPage()===window},f.prototype.connect=function(){return this.connection.connect(),this},f.prototype.streaming=function(){return this.streamingCount>0},f.prototype.connected=function(){return!!this.connection.connected},f.prototype.startAnimationLoop=function(){this.suppressAnimationLoop||this.animationFrameRequested||(this.animationFrameRequested=!0,window.requestAnimationFrame(this.onAnimationFrame))},f.prototype.disconnect=function(){return this.connection.disconnect(),this},f.prototype.frame=function(t){return this.history.get(t)||r.Invalid},f.prototype.loop=function(t){return t&&("function"==typeof t?this.on(this.frameEventName,t):this.setupFrameEvents(t)),this.connect()},f.prototype.addStep=function(t){this.pipeline||(this.pipeline=new u(this)),this.pipeline.addStep(t)},f.prototype.processFrame=function(t){t.gestures&&(this.accumulatedGestures=this.accumulatedGestures.concat(t.gestures)),this.lastConnectionFrame=t,this.startAnimationLoop(),this.emit("deviceFrame",t)},f.prototype.processFinishedFrame=function(t){if(this.lastFrame=t,t.valid&&(this.lastValidFrame=t),t.controller=this,t.historyIdx=this.history.push(t),t.gestures){t.gestures=this.accumulatedGestures,this.accumulatedGestures=[];for(var e=0;e!=t.gestures.length;e++)this.emit("gesture",t.gestures[e],t)}this.pipeline&&((t=this.pipeline.run(t))||(t=r.Invalid)),this.emit("frame",t),this.emitHandEvents(t)},f.prototype.emitHandEvents=function(t){for(var e=0;e0){for(var e in t.devices)t.emit("deviceStopped",t.devices[e]),t.emit("deviceRemoved",t.devices[e]);for(var e in t.emit("streamingStopped",t.devices[e]),t.streamingCount=0,t.devices)delete t.devices[e]}};this.connection.on("focus",function(){t.loopWhileDisconnected&&t.startAnimationLoop(),t.emit("focus")}),this.connection.on("blur",function(){t.emit("blur")}),this.connection.on("protocol",function(e){e.on("beforeFrameCreated",function(e){t.emit("beforeFrameCreated",e)}),e.on("afterFrameCreated",function(e,n){t.emit("afterFrameCreated",e,n)}),t.emit("protocol",e)}),this.connection.on("ready",function(){t.checkVersion&&!t.inNode&&t.checkOutOfDate(),t.emit("ready")}),this.connection.on("connect",function(){t.emit("connect"),t.connection.removeListener("frame",e),t.connection.on("frame",e)}),this.connection.on("disconnect",function(){t.emit("disconnect"),n()}),this.connection.on("deviceConnect",function(i){i.state?(t.emit("deviceConnected"),t.connection.removeListener("frame",e),t.connection.on("frame",e)):(t.emit("deviceDisconnected"),n())}),this.connection.on("deviceEvent",function(e){var n=e.state,i=t.devices[n.id],r={};for(var o in n)i&&i.hasOwnProperty(o)&&i[o]==n[o]||(r[o]=!0);t.devices[n.id]=n,r.attached&&t.emit(n.attached?"deviceAttached":"deviceRemoved",n),r.streaming&&(n.streaming?(t.streamingCount++,t.emit("deviceStreaming",n),1==t.streamingCount&&t.emit("streamingStarted",n),r.attached||t.emit("deviceConnected")):r.attached&&n.attached||(t.streamingCount--,t.emit("deviceStopped",n),0==t.streamingCount&&t.emit("streamingStopped",n),t.emit("deviceDisconnected")))}),this.on("newListener",function(t,e){"deviceConnected"!=t&&"deviceDisconnected"!=t||console.warn(t+" events are depricated. Consider using 'streamingStarted/streamingStopped' or 'deviceStreaming/deviceStopped' instead")})},f.prototype.checkOutOfDate=function(){console.assert(this.connection&&this.connection.protocol);var t=this.connection.protocol.serviceVersion,e=this.connection.protocol.version,n=this.connectionType.defaultProtocolVersion;return n>e&&(console.warn("Your Protocol Version is v"+e+", this app was designed for v"+n),p.warnOutOfDate({sV:t,pV:e}),!0)},f._pluginFactories={},f.plugin=function(t,e){return this._pluginFactories[t]&&console.warn('Plugin "'+t+'" already registered'),this._pluginFactories[t]=e},f.plugins=function(){return d.keys(this._pluginFactories)};var m=function(t,e,n){-1!=["beforeFrameCreated","afterFrameCreated"].indexOf(e)?this.on(e,n):(this.pipeline||(this.pipeline=new u(this)),this._pluginPipelineSteps[t]||(this._pluginPipelineSteps[t]=[]),this._pluginPipelineSteps[t].push(this.pipeline.addWrappedStep(e,n)))},v=function(t,e,n){var i;switch(this._pluginExtendedMethods[t]||(this._pluginExtendedMethods[t]=[]),e){case"frame":i=r;break;case"hand":i=o;break;case"pointable":i=s,d.extend(a.prototype,n),d.extend(a.Invalid,n);break;case"finger":i=a;break;default:throw t+' specifies invalid object type "'+e+'" for prototypical extension'}d.extend(i.prototype,n),d.extend(i.Invalid,n),this._pluginExtendedMethods[t].push([i,n])};f.prototype.use=function(t,e){var n,i,r,o;if(!(i="function"==typeof t?t:f._pluginFactories[t]))throw"Leap Plugin "+t+" not found.";if(e||(e={}),this.plugins[t])return d.extend(this.plugins[t],e),this;for(r in this.plugins[t]=e,o=i.call(this,e))"function"==typeof(n=o[r])?m.call(this,t,r,n):v.call(this,t,r,n);return this},f.prototype.stopUsing=function(t){var e,n=this._pluginPipelineSteps[t],i=this._pluginExtendedMethods[t],r=0;if(this.plugins[t]){if(n)for(r=0;rUpgrade",{onclick:function(t){if("leapjs-decline-upgrade"!=t.target.id){var n=window.open(e,"_blank","height=800,width=1000,location=1,menubar=1,resizable=1,status=1,toolbar=1,scrollbars=1");window.focus&&n.focus()}return i.hide(),!0},onmousemove:function(t){t.target==document.getElementById("leapjs-decline-upgrade")?(document.getElementById("leapjs-decline-upgrade").style.color="#000",document.getElementById("leapjs-decline-upgrade").style.boxShadow="0px 0px 2px #5daa00",document.getElementById("leapjs-accept-upgrade").style.color="#444",document.getElementById("leapjs-accept-upgrade").style.boxShadow="none"):(document.getElementById("leapjs-accept-upgrade").style.color="#000",document.getElementById("leapjs-accept-upgrade").style.boxShadow="0px 0px 2px #5daa00",document.getElementById("leapjs-decline-upgrade").style.color="#444",document.getElementById("leapjs-decline-upgrade").style.boxShadow="none")},onmouseout:function(){document.getElementById("leapjs-decline-upgrade").style.color="#444",document.getElementById("leapjs-decline-upgrade").style.boxShadow="none",document.getElementById("leapjs-accept-upgrade").style.color="#444",document.getElementById("leapjs-accept-upgrade").style.boxShadow="none"}})).show()},r.hasWarnedBones=!1,r.warnBones=function(){this.hasWarnedBones||(this.hasWarnedBones=!0,console.warn("Your Leap Service is out of date"),void 0!==i&&i.versions&&i.versions.node||this.warnOutOfDate({reason:"bones"}))}},{__browserify_process:22}],7:[function(t,e,n){var i=t("./pointable"),r=t("./bone"),o=t("./dialog"),s=t("underscore"),a=e.exports=function(t){i.call(this,t),this.dipPosition=t.dipPosition,this.pipPosition=t.pipPosition,this.mcpPosition=t.mcpPosition,this.carpPosition=t.carpPosition,this.extended=t.extended,this.type=t.type,this.finger=!0,this.positions=[this.carpPosition,this.mcpPosition,this.pipPosition,this.dipPosition,this.tipPosition],t.bases?this.addBones(t):o.warnBones()};s.extend(a.prototype,i.prototype),a.prototype.addBones=function(t){this.metacarpal=new r(this,{type:0,width:this.width,prevJoint:this.carpPosition,nextJoint:this.mcpPosition,basis:t.bases[0]}),this.proximal=new r(this,{type:1,width:this.width,prevJoint:this.mcpPosition,nextJoint:this.pipPosition,basis:t.bases[1]}),this.medial=new r(this,{type:2,width:this.width,prevJoint:this.pipPosition,nextJoint:this.dipPosition,basis:t.bases[2]}),this.distal=new r(this,{type:3,width:this.width,prevJoint:this.dipPosition,nextJoint:t.btipPosition,basis:t.bases[3]}),this.bones=[this.metacarpal,this.proximal,this.medial,this.distal]},a.prototype.toString=function(){return"Finger [ id:"+this.id+" "+this.length+"mmx | width:"+this.width+"mm | direction:"+this.direction+" ]"},a.Invalid={valid:!1}},{"./bone":1,"./dialog":6,"./pointable":14,underscore:24}],8:[function(t,e,n){var i=t("./hand"),r=t("./pointable"),o=t("./gesture").createGesture,s=t("gl-matrix"),a=s.mat3,c=s.vec3,u=t("./interaction_box"),h=t("./finger"),l=t("underscore"),p=e.exports=function(t){if(this.valid=!0,this.id=t.id,this.timestamp=t.timestamp,this.hands=[],this.handsMap={},this.pointables=[],this.tools=[],this.fingers=[],t.interactionBox&&(this.interactionBox=new u(t.interactionBox)),this.gestures=[],this.pointablesMap={},this._translation=t.t,this._rotation=l.flatten(t.r),this._scaleFactor=t.s,this.data=t,this.type="frame",this.currentFrameRate=t.currentFrameRate,t.gestures)for(var e=0,n=t.gestures.length;e!=n;e++)this.gestures.push(o(t.gestures[e]));this.postprocessData(t)};p.prototype.postprocessData=function(t){t||(t=this.data);for(var e=0,n=t.hands.length;e!=n;e++){var o=new i(t.hands[e]);o.frame=this,this.hands.push(o),this.handsMap[o.id]=o}t.pointables=l.sortBy(t.pointables,function(t){return t.id});for(var s=0,a=t.pointables.length;s!=a;s++){var c=t.pointables[s],u=c.dipPosition?new h(c):new r(c);u.frame=this,this.addPointable(u)}},p.prototype.addPointable=function(t){if(this.pointables.push(t),this.pointablesMap[t.id]=t,(t.tool?this.tools:this.fingers).push(t),void 0!==t.handId&&this.handsMap.hasOwnProperty(t.handId)){var e=this.handsMap[t.handId];switch(e.pointables.push(t),(t.tool?e.tools:e.fingers).push(t),t.type){case 0:e.thumb=t;break;case 1:e.indexFinger=t;break;case 2:e.middleFinger=t;break;case 3:e.ringFinger=t;break;case 4:e.pinky=t}}},p.prototype.tool=function(t){var e=this.pointable(t);return e.tool?e:r.Invalid},p.prototype.pointable=function(t){return this.pointablesMap[t]||r.Invalid},p.prototype.finger=function(t){var e=this.pointable(t);return e.tool?r.Invalid:e},p.prototype.hand=function(t){return this.handsMap[t]||i.Invalid},p.prototype.rotationAngle=function(t,e){if(!this.valid||!t.valid)return 0;var n=this.rotationMatrix(t),i=Math.acos(.5*(n[0]+n[4]+n[8]-1));if(i=isNaN(i)?0:i,void 0!==e){var r=this.rotationAxis(t);i*=c.dot(r,c.normalize(c.create(),e))}return i},p.prototype.rotationAxis=function(t){return this.valid&&t.valid?c.normalize(c.create(),[this._rotation[7]-t._rotation[5],this._rotation[2]-t._rotation[6],this._rotation[3]-t._rotation[1]]):c.create()},p.prototype.rotationMatrix=function(t){if(!this.valid||!t.valid)return a.create();var e=a.transpose(a.create(),this._rotation);return a.multiply(a.create(),t._rotation,e)},p.prototype.scaleFactor=function(t){return this.valid&&t.valid?Math.exp(this._scaleFactor-t._scaleFactor):1},p.prototype.translation=function(t){return this.valid&&t.valid?c.subtract(c.create(),this._translation,t._translation):c.create()},p.prototype.toString=function(){var t="Frame [ id:"+this.id+" | timestamp:"+this.timestamp+" | Hand count:("+this.hands.length+") | Pointable count:("+this.pointables.length+")";return this.gestures&&(t+=" | Gesture count:("+this.gestures.length+")"),t+=" ]"},p.prototype.dump=function(){var t="";t+="Frame Info:
",t+=this.toString(),t+="

Hands:
";for(var e=0,n=this.hands.length;e!=n;e++)t+=" "+this.hands[e].toString()+"
";t+="

Pointables:
";for(var i=0,r=this.pointables.length;i!=r;i++)t+=" "+this.pointables[i].toString()+"
";if(this.gestures){t+="

Gestures:
";for(var o=0,s=this.gestures.length;o!=s;o++)t+=" "+this.gestures[o].toString()+"
"}return t+="

Raw JSON:
",t+=JSON.stringify(this.data)},p.Invalid={valid:!1,hands:[],fingers:[],tools:[],gestures:[],pointables:[],pointable:function(){return r.Invalid},finger:function(){return r.Invalid},hand:function(){return i.Invalid},toString:function(){return"invalid frame"},dump:function(){return this.toString()},rotationAngle:function(){return 0},rotationMatrix:function(){return a.create()},rotationAxis:function(){return c.create()},scaleFactor:function(){return 1},translation:function(){return c.create()}}},{"./finger":7,"./gesture":9,"./hand":10,"./interaction_box":12,"./pointable":14,"gl-matrix":23,underscore:24}],9:[function(t,e,n){var i=t("gl-matrix").vec3,r=t("events").EventEmitter,o=t("underscore"),s=(n.createGesture=function(t){var e;switch(t.type){case"circle":e=new a(t);break;case"swipe":e=new c(t);break;case"screenTap":e=new u(t);break;case"keyTap":e=new h(t);break;default:throw"unknown gesture type"}return e.id=t.id,e.handIds=t.handIds.slice(),e.pointableIds=t.pointableIds.slice(),e.duration=t.duration,e.state=t.state,e.type=t.type,e},n.gestureListener=function(t,e){var n={},i={};t.on("gesture",function(t,r){if(t.type==e){if(("start"==t.state||"stop"==t.state)&&void 0===i[t.id]){var a=new s(t,r);i[t.id]=a,o.each(n,function(t,e){a.on(e,t)})}i[t.id].update(t,r),"stop"==t.state&&delete i[t.id]}});var r={start:function(t){return n.start=t,r},stop:function(t){return n.stop=t,r},complete:function(t){return n.stop=t,r},update:function(t){return n.update=t,r}};return r},n.Gesture=function(t,e){this.gestures=[t],this.frames=[e]});s.prototype.update=function(t,e){this.lastGesture=t,this.lastFrame=e,this.gestures.push(t),this.frames.push(e),this.emit(t.state,this)},s.prototype.translation=function(){return i.subtract(i.create(),this.lastGesture.startPosition,this.lastGesture.position)},o.extend(s.prototype,r.prototype);var a=function(t){this.center=t.center,this.normal=t.normal,this.progress=t.progress,this.radius=t.radius};a.prototype.toString=function(){return"CircleGesture ["+JSON.stringify(this)+"]"};var c=function(t){this.startPosition=t.startPosition,this.position=t.position,this.direction=t.direction,this.speed=t.speed};c.prototype.toString=function(){return"SwipeGesture ["+JSON.stringify(this)+"]"};var u=function(t){this.position=t.position,this.direction=t.direction,this.progress=t.progress};u.prototype.toString=function(){return"ScreenTapGesture ["+JSON.stringify(this)+"]"};var h=function(t){this.position=t.position,this.direction=t.direction,this.progress=t.progress};h.prototype.toString=function(){return"KeyTapGesture ["+JSON.stringify(this)+"]"}},{events:21,"gl-matrix":23,underscore:24}],10:[function(t,e,n){var i=t("./pointable"),r=t("./bone"),o=t("gl-matrix"),s=o.mat3,a=o.vec3,c=t("underscore"),u=e.exports=function(t){this.id=t.id,this.palmPosition=t.palmPosition,this.direction=t.direction,this.palmVelocity=t.palmVelocity,this.palmNormal=t.palmNormal,this.sphereCenter=t.sphereCenter,this.sphereRadius=t.sphereRadius,this.valid=!0,this.pointables=[],this.fingers=[],this.arm=t.armBasis?new r(this,{type:4,width:t.armWidth,prevJoint:t.elbow,nextJoint:t.wrist,basis:t.armBasis}):null,this.tools=[],this._translation=t.t,this._rotation=c.flatten(t.r),this._scaleFactor=t.s,this.timeVisible=t.timeVisible,this.stabilizedPalmPosition=t.stabilizedPalmPosition,this.type=t.type,this.grabStrength=t.grabStrength,this.pinchStrength=t.pinchStrength,this.confidence=t.confidence};u.prototype.finger=function(t){var e=this.frame.finger(t);return e&&e.handId==this.id?e:i.Invalid},u.prototype.rotationAngle=function(t,e){if(!this.valid||!t.valid)return 0;if(!t.hand(this.id).valid)return 0;var n=this.rotationMatrix(t),i=Math.acos(.5*(n[0]+n[4]+n[8]-1));if(i=isNaN(i)?0:i,void 0!==e){var r=this.rotationAxis(t);i*=a.dot(r,a.normalize(a.create(),e))}return i},u.prototype.rotationAxis=function(t){if(!this.valid||!t.valid)return a.create();var e=t.hand(this.id);return e.valid?a.normalize(a.create(),[this._rotation[7]-e._rotation[5],this._rotation[2]-e._rotation[6],this._rotation[3]-e._rotation[1]]):a.create()},u.prototype.rotationMatrix=function(t){if(!this.valid||!t.valid)return s.create();var e=t.hand(this.id);if(!e.valid)return s.create();var n=s.transpose(s.create(),this._rotation);return s.multiply(s.create(),e._rotation,n)},u.prototype.scaleFactor=function(t){if(!this.valid||!t.valid)return 1;var e=t.hand(this.id);return e.valid?Math.exp(this._scaleFactor-e._scaleFactor):1},u.prototype.translation=function(t){if(!this.valid||!t.valid)return a.create();var e=t.hand(this.id);return e.valid?[this._translation[0]-e._translation[0],this._translation[1]-e._translation[1],this._translation[2]-e._translation[2]]:a.create()},u.prototype.toString=function(){return"Hand ("+this.type+") [ id: "+this.id+" | palm velocity:"+this.palmVelocity+" | sphere center:"+this.sphereCenter+" ] "},u.prototype.pitch=function(){return Math.atan2(this.direction[1],-this.direction[2])},u.prototype.yaw=function(){return Math.atan2(this.direction[0],-this.direction[2])},u.prototype.roll=function(){return Math.atan2(this.palmNormal[0],-this.palmNormal[1])},u.Invalid={valid:!1,fingers:[],tools:[],pointables:[],left:!1,pointable:function(){return i.Invalid},finger:function(){return i.Invalid},toString:function(){return"invalid frame"},dump:function(){return this.toString()},rotationAngle:function(){return 0},rotationMatrix:function(){return s.create()},rotationAxis:function(){return a.create()},scaleFactor:function(){return 1},translation:function(){return a.create()}}},{"./bone":1,"./pointable":14,"gl-matrix":23,underscore:24}],11:[function(t,e,n){e.exports={Controller:t("./controller"),Frame:t("./frame"),Gesture:t("./gesture"),Hand:t("./hand"),Pointable:t("./pointable"),Finger:t("./finger"),InteractionBox:t("./interaction_box"),CircularBuffer:t("./circular_buffer"),UI:t("./ui"),JSONProtocol:t("./protocol").JSONProtocol,glMatrix:t("gl-matrix"),mat3:t("gl-matrix").mat3,vec3:t("gl-matrix").vec3,loopController:void 0,version:t("./version.js"),_:t("underscore"),EventEmitter:t("events").EventEmitter,loop:function(t,e){return t&&void 0===e&&"[object Function]"==={}.toString.call(t)&&(e=t,t={}),this.loopController?t&&this.loopController.setupFrameEvents(t):this.loopController=new this.Controller(t),this.loopController.loop(e),this.loopController},plugin:function(t,e){this.Controller.plugin(t,e)}}},{"./circular_buffer":2,"./controller":5,"./finger":7,"./frame":8,"./gesture":9,"./hand":10,"./interaction_box":12,"./pointable":14,"./protocol":15,"./ui":16,"./version.js":19,events:21,"gl-matrix":23,underscore:24}],12:[function(t,e,n){var i=t("gl-matrix").vec3,r=e.exports=function(t){this.valid=!0,this.center=t.center,this.size=t.size,this.width=t.size[0],this.height=t.size[1],this.depth=t.size[2]};r.prototype.denormalizePoint=function(t){return i.fromValues((t[0]-.5)*this.size[0]+this.center[0],(t[1]-.5)*this.size[1]+this.center[1],(t[2]-.5)*this.size[2]+this.center[2])},r.prototype.normalizePoint=function(t,e){var n=i.fromValues((t[0]-this.center[0])/this.size[0]+.5,(t[1]-this.center[1])/this.size[1]+.5,(t[2]-this.center[2])/this.size[2]+.5);return e&&(n[0]=Math.min(Math.max(n[0],0),1),n[1]=Math.min(Math.max(n[1],0),1),n[2]=Math.min(Math.max(n[2],0),1)),n},r.prototype.toString=function(){return"InteractionBox [ width:"+this.width+" | height:"+this.height+" | depth:"+this.depth+" ]"},r.Invalid={valid:!1}},{"gl-matrix":23}],13:[function(t,e,n){var i=e.exports=function(t){this.steps=[],this.controller=t};i.prototype.addStep=function(t){this.steps.push(t)},i.prototype.run=function(t){for(var e=this.steps.length,n=0;n!=e&&t;n++)t=this.steps[n](t);return t},i.prototype.removeStep=function(t){var e=this.steps.indexOf(t);if(-1===e)throw"Step not found in pipeline";this.steps.splice(e,1)},i.prototype.addWrappedStep=function(t,e){var n=this.controller,i=function(i){var r,o,s;for(o=0,s=(r="frame"==t?[i]:i[t+"s"]||[]).length;o=this.start.x&&n.x<=this.end.x&&n.y>=this.start.y&&n.y<=this.end.y&&n.z>=this.start.z&&n.z<=this.end.z)return!0}return!1},o.prototype.listener=function(t){var e=this;return t&&t.nearThreshold&&this.setupNearRegion(t.nearThreshold),function(t){return e.updatePosition(t)}},o.prototype.clipper=function(){var t=this;return function(e){return t.updatePosition(e),t.enteredFrame?e:null}},o.prototype.setupNearRegion=function(t){var e=this.nearRegion=new o([this.start.x-t,this.start.y-t,this.start.z-t],[this.end.x+t,this.end.y+t,this.end.z+t]),n=this;e.on("enter",function(t){n.emit("near",t)}),e.on("exit",function(t){n.emit("far",t)}),n.on("exit",function(t){n.emit("near",t)})},o.prototype.updatePosition=function(t){return this.nearRegion&&this.nearRegion.updatePosition(t),this.hasPointables(t)&&null==this.enteredFrame?(this.enteredFrame=t,this.emit("enter",this.enteredFrame)):this.hasPointables(t)||null==this.enteredFrame||(this.enteredFrame=null,this.emit("exit",this.enteredFrame)),t},o.prototype.normalize=function(t){return new Vector([(t.x-this.start.x)/(this.end.x-this.start.x),(t.y-this.start.y)/(this.end.y-this.start.y),(t.z-this.start.z)/(this.end.z-this.start.z)])},o.prototype.mapToXY=function(t,e,n){var i=this.normalize(t),r=i.x,o=i.y;return r>1?r=1:r<-1&&(r=-1),o>1?o=1:o<-1&&(o=-1),[(r+1)/2*e,(1-o)/2*n,i.z]},r.extend(o.prototype,i.prototype)},{events:21,underscore:24}],19:[function(t,e,n){e.exports={full:"0.6.4",major:0,minor:6,dot:4}},{}],20:[function(t,e,n){},{}],21:[function(t,e,n){var i=t("__browserify_process");i.EventEmitter||(i.EventEmitter=function(){});var r=n.EventEmitter=i.EventEmitter,o="function"==typeof Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};r.prototype.setMaxListeners=function(t){this._events||(this._events={}),this._events.maxListeners=t},r.prototype.emit=function(t){if("error"===t&&(!this._events||!this._events.error||o(this._events.error)&&!this._events.error.length))throw arguments[1]instanceof Error?arguments[1]:new Error("Uncaught, unspecified 'error' event.");if(!this._events)return!1;var e=this._events[t];if(!e)return!1;if("function"==typeof e){switch(arguments.length){case 1:e.call(this);break;case 2:e.call(this,arguments[1]);break;case 3:e.call(this,arguments[1],arguments[2]);break;default:var n=Array.prototype.slice.call(arguments,1);e.apply(this,n)}return!0}if(o(e)){n=Array.prototype.slice.call(arguments,1);for(var i=e.slice(),r=0,s=i.length;r0&&this._events[t].length>n&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),console.trace());this._events[t].push(e)}else this._events[t]=[this._events[t],e];else this._events[t]=e;return this},r.prototype.on=r.prototype.addListener,r.prototype.once=function(t,e){var n=this;return n.on(t,function i(){n.removeListener(t,i),e.apply(this,arguments)}),this},r.prototype.removeListener=function(t,e){if("function"!=typeof e)throw new Error("removeListener only takes instances of Function");if(!this._events||!this._events[t])return this;var n=this._events[t];if(o(n)){var i=function(t,e){if(t.indexOf)return t.indexOf(e);for(var n=0;n0&&n.shift()())},!0),function(t){n.push(t),window.postMessage("process-tick","*")}}return function(t){setTimeout(t,0)}}(),i.title="browser",i.browser=!0,i.env={},i.argv=[],i.binding=function(t){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(t){throw new Error("process.chdir is not supported")}},{}],23:[function(t,e,n){var i,r;i=this,r={},void 0===n?"function"==typeof define&&"object"==typeof define.amd&&define.amd?(r.exports={},define(function(){return r.exports})):r.exports="undefined"!=typeof window?window:i:r.exports=n,function(t){var e;if(!n)var n="undefined"!=typeof Float32Array?Float32Array:Array;if(!i)var i=Math.random;var r={setMatrixArrayType:function(t){n=t}};void 0!==t&&(t.glMatrix=r);var o=Math.PI/180;r.toRadian=function(t){return t*o};var s,a={create:function(){var t=new n(2);return t[0]=0,t[1]=0,t},clone:function(t){var e=new n(2);return e[0]=t[0],e[1]=t[1],e},fromValues:function(t,e){var i=new n(2);return i[0]=t,i[1]=e,i},copy:function(t,e){return t[0]=e[0],t[1]=e[1],t},set:function(t,e,n){return t[0]=e,t[1]=n,t},add:function(t,e,n){return t[0]=e[0]+n[0],t[1]=e[1]+n[1],t},subtract:function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t}};a.sub=a.subtract,a.multiply=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t},a.mul=a.multiply,a.divide=function(t,e,n){return t[0]=e[0]/n[0],t[1]=e[1]/n[1],t},a.div=a.divide,a.min=function(t,e,n){return t[0]=Math.min(e[0],n[0]),t[1]=Math.min(e[1],n[1]),t},a.max=function(t,e,n){return t[0]=Math.max(e[0],n[0]),t[1]=Math.max(e[1],n[1]),t},a.scale=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t},a.scaleAndAdd=function(t,e,n,i){return t[0]=e[0]+n[0]*i,t[1]=e[1]+n[1]*i,t},a.distance=function(t,e){var n=e[0]-t[0],i=e[1]-t[1];return Math.sqrt(n*n+i*i)},a.dist=a.distance,a.squaredDistance=function(t,e){var n=e[0]-t[0],i=e[1]-t[1];return n*n+i*i},a.sqrDist=a.squaredDistance,a.length=function(t){var e=t[0],n=t[1];return Math.sqrt(e*e+n*n)},a.len=a.length,a.squaredLength=function(t){var e=t[0],n=t[1];return e*e+n*n},a.sqrLen=a.squaredLength,a.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t},a.normalize=function(t,e){var n=e[0],i=e[1],r=n*n+i*i;return r>0&&(r=1/Math.sqrt(r),t[0]=e[0]*r,t[1]=e[1]*r),t},a.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]},a.cross=function(t,e,n){var i=e[0]*n[1]-e[1]*n[0];return t[0]=t[1]=0,t[2]=i,t},a.lerp=function(t,e,n,i){var r=e[0],o=e[1];return t[0]=r+i*(n[0]-r),t[1]=o+i*(n[1]-o),t},a.random=function(t,e){e=e||1;var n=2*i()*Math.PI;return t[0]=Math.cos(n)*e,t[1]=Math.sin(n)*e,t},a.transformMat2=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[2]*r,t[1]=n[1]*i+n[3]*r,t},a.transformMat2d=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[2]*r+n[4],t[1]=n[1]*i+n[3]*r+n[5],t},a.transformMat3=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[3]*r+n[6],t[1]=n[1]*i+n[4]*r+n[7],t},a.transformMat4=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[4]*r+n[12],t[1]=n[1]*i+n[5]*r+n[13],t},a.forEach=(s=a.create(),function(t,e,n,i,r,o){var a,c;for(e||(e=2),n||(n=0),c=i?Math.min(i*e+n,t.length):t.length,a=n;a0&&(o=1/Math.sqrt(o),t[0]=e[0]*o,t[1]=e[1]*o,t[2]=e[2]*o),t},c.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},c.cross=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=n[0],a=n[1],c=n[2];return t[0]=r*c-o*a,t[1]=o*s-i*c,t[2]=i*a-r*s,t},c.lerp=function(t,e,n,i){var r=e[0],o=e[1],s=e[2];return t[0]=r+i*(n[0]-r),t[1]=o+i*(n[1]-o),t[2]=s+i*(n[2]-s),t},c.random=function(t,e){e=e||1;var n=2*i()*Math.PI,r=2*i()-1,o=Math.sqrt(1-r*r)*e;return t[0]=Math.cos(n)*o,t[1]=Math.sin(n)*o,t[2]=r*e,t},c.transformMat4=function(t,e,n){var i=e[0],r=e[1],o=e[2];return t[0]=n[0]*i+n[4]*r+n[8]*o+n[12],t[1]=n[1]*i+n[5]*r+n[9]*o+n[13],t[2]=n[2]*i+n[6]*r+n[10]*o+n[14],t},c.transformMat3=function(t,e,n){var i=e[0],r=e[1],o=e[2];return t[0]=i*n[0]+r*n[3]+o*n[6],t[1]=i*n[1]+r*n[4]+o*n[7],t[2]=i*n[2]+r*n[5]+o*n[8],t},c.transformQuat=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=n[0],a=n[1],c=n[2],u=n[3],h=u*i+a*o-c*r,l=u*r+c*i-s*o,p=u*o+s*r-a*i,d=-s*i-a*r-c*o;return t[0]=h*u+d*-s+l*-c-p*-a,t[1]=l*u+d*-a+p*-s-h*-c,t[2]=p*u+d*-c+h*-a-l*-s,t},c.rotateX=function(t,e,n,i){var r=[],o=[];return r[0]=e[0]-n[0],r[1]=e[1]-n[1],r[2]=e[2]-n[2],o[0]=r[0],o[1]=r[1]*Math.cos(i)-r[2]*Math.sin(i),o[2]=r[1]*Math.sin(i)+r[2]*Math.cos(i),t[0]=o[0]+n[0],t[1]=o[1]+n[1],t[2]=o[2]+n[2],t},c.rotateY=function(t,e,n,i){var r=[],o=[];return r[0]=e[0]-n[0],r[1]=e[1]-n[1],r[2]=e[2]-n[2],o[0]=r[2]*Math.sin(i)+r[0]*Math.cos(i),o[1]=r[1],o[2]=r[2]*Math.cos(i)-r[0]*Math.sin(i),t[0]=o[0]+n[0],t[1]=o[1]+n[1],t[2]=o[2]+n[2],t},c.rotateZ=function(t,e,n,i){var r=[],o=[];return r[0]=e[0]-n[0],r[1]=e[1]-n[1],r[2]=e[2]-n[2],o[0]=r[0]*Math.cos(i)-r[1]*Math.sin(i),o[1]=r[0]*Math.sin(i)+r[1]*Math.cos(i),o[2]=r[2],t[0]=o[0]+n[0],t[1]=o[1]+n[1],t[2]=o[2]+n[2],t},c.forEach=function(){var t=c.create();return function(e,n,i,r,o,s){var a,c;for(n||(n=3),i||(i=0),c=r?Math.min(r*n+i,e.length):e.length,a=i;a0&&(s=1/Math.sqrt(s),t[0]=e[0]*s,t[1]=e[1]*s,t[2]=e[2]*s,t[3]=e[3]*s),t},u.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},u.lerp=function(t,e,n,i){var r=e[0],o=e[1],s=e[2],a=e[3];return t[0]=r+i*(n[0]-r),t[1]=o+i*(n[1]-o),t[2]=s+i*(n[2]-s),t[3]=a+i*(n[3]-a),t},u.random=function(t,e){return e=e||1,t[0]=i(),t[1]=i(),t[2]=i(),t[3]=i(),u.normalize(t,t),u.scale(t,t,e),t},u.transformMat4=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=e[3];return t[0]=n[0]*i+n[4]*r+n[8]*o+n[12]*s,t[1]=n[1]*i+n[5]*r+n[9]*o+n[13]*s,t[2]=n[2]*i+n[6]*r+n[10]*o+n[14]*s,t[3]=n[3]*i+n[7]*r+n[11]*o+n[15]*s,t},u.transformQuat=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=n[0],a=n[1],c=n[2],u=n[3],h=u*i+a*o-c*r,l=u*r+c*i-s*o,p=u*o+s*r-a*i,d=-s*i-a*r-c*o;return t[0]=h*u+d*-s+l*-c-p*-a,t[1]=l*u+d*-a+p*-s-h*-c,t[2]=p*u+d*-c+h*-a-l*-s,t},u.forEach=function(){var t=u.create();return function(e,n,i,r,o,s){var a,c;for(n||(n=4),i||(i=0),c=r?Math.min(r*n+i,e.length):e.length,a=i;a.999999?(t[0]=0,t[1]=0,t[2]=0,t[3]=1,t):(c.cross(f,e,n),t[0]=f[0],t[1]=f[1],t[2]=f[2],t[3]=1+i,g.normalize(t,t))}),g.setAxes=(y=p.create(),function(t,e,n,i){return y[0]=n[0],y[3]=n[1],y[6]=n[2],y[1]=i[0],y[4]=i[1],y[7]=i[2],y[2]=-e[0],y[5]=-e[1],y[8]=-e[2],g.normalize(t,g.fromMat3(t,y))}),g.clone=u.clone,g.fromValues=u.fromValues,g.copy=u.copy,g.set=u.set,g.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},g.setAxisAngle=function(t,e,n){n*=.5;var i=Math.sin(n);return t[0]=i*e[0],t[1]=i*e[1],t[2]=i*e[2],t[3]=Math.cos(n),t},g.add=u.add,g.multiply=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=e[3],a=n[0],c=n[1],u=n[2],h=n[3];return t[0]=i*h+s*a+r*u-o*c,t[1]=r*h+s*c+o*a-i*u,t[2]=o*h+s*u+i*c-r*a,t[3]=s*h-i*a-r*c-o*u,t},g.mul=g.multiply,g.scale=u.scale,g.rotateX=function(t,e,n){n*=.5;var i=e[0],r=e[1],o=e[2],s=e[3],a=Math.sin(n),c=Math.cos(n);return t[0]=i*c+s*a,t[1]=r*c+o*a,t[2]=o*c-r*a,t[3]=s*c-i*a,t},g.rotateY=function(t,e,n){n*=.5;var i=e[0],r=e[1],o=e[2],s=e[3],a=Math.sin(n),c=Math.cos(n);return t[0]=i*c-o*a,t[1]=r*c+s*a,t[2]=o*c+i*a,t[3]=s*c-r*a,t},g.rotateZ=function(t,e,n){n*=.5;var i=e[0],r=e[1],o=e[2],s=e[3],a=Math.sin(n),c=Math.cos(n);return t[0]=i*c+r*a,t[1]=r*c-i*a,t[2]=o*c+s*a,t[3]=s*c-o*a,t},g.calculateW=function(t,e){var n=e[0],i=e[1],r=e[2];return t[0]=n,t[1]=i,t[2]=r,t[3]=-Math.sqrt(Math.abs(1-n*n-i*i-r*r)),t},g.dot=u.dot,g.lerp=u.lerp,g.slerp=function(t,e,n,i){var r,o,s,a,c,u=e[0],h=e[1],l=e[2],p=e[3],d=n[0],f=n[1],m=n[2],v=n[3];return(o=u*d+h*f+l*m+p*v)<0&&(o=-o,d=-d,f=-f,m=-m,v=-v),1-o>1e-6?(r=Math.acos(o),s=Math.sin(r),a=Math.sin((1-i)*r)/s,c=Math.sin(i*r)/s):(a=1-i,c=i),t[0]=a*u+c*d,t[1]=a*h+c*f,t[2]=a*l+c*m,t[3]=a*p+c*v,t},g.invert=function(t,e){var n=e[0],i=e[1],r=e[2],o=e[3],s=n*n+i*i+r*r+o*o,a=s?1/s:0;return t[0]=-n*a,t[1]=-i*a,t[2]=-r*a,t[3]=o*a,t},g.conjugate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t},g.length=u.length,g.len=g.length,g.squaredLength=u.squaredLength,g.sqrLen=g.squaredLength,g.normalize=u.normalize,g.fromMat3=function(t,e){var n,i=e[0]+e[4]+e[8];if(i>0)n=Math.sqrt(i+1),t[3]=.5*n,t[0]=(e[7]-e[5])*(n=.5/n),t[1]=(e[2]-e[6])*n,t[2]=(e[3]-e[1])*n;else{var r=0;e[4]>e[0]&&(r=1),e[8]>e[3*r+r]&&(r=2);var o=(r+1)%3,s=(r+2)%3;n=Math.sqrt(e[3*r+r]-e[3*o+o]-e[3*s+s]+1),t[r]=.5*n,t[3]=(e[3*s+o]-e[3*o+s])*(n=.5/n),t[o]=(e[3*o+r]+e[3*r+o])*n,t[s]=(e[3*s+r]+e[3*r+s])*n}return t},g.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},void 0!==t&&(t.quat=g)}(r.exports)},{}],24:[function(t,e,n){(function(){var t=this,i=t._,r={},o=Array.prototype,s=Object.prototype,a=Function.prototype,c=o.push,u=o.slice,h=o.concat,l=s.toString,p=s.hasOwnProperty,d=o.forEach,f=o.map,m=o.reduce,v=o.reduceRight,y=o.filter,g=o.every,w=o.some,b=o.indexOf,M=o.lastIndexOf,x=Array.isArray,E=Object.keys,_=a.bind,S=function(t){return t instanceof S?t:this instanceof S?void(this._wrapped=t):new S(t)};void 0!==n?(void 0!==e&&e.exports&&(n=e.exports=S),n._=S):t._=S,S.VERSION="1.4.4";var P=S.each=S.forEach=function(t,e,n){if(null!=t)if(d&&t.forEach===d)t.forEach(e,n);else if(t.length===+t.length){for(var i=0,o=t.length;i2;if(null==t&&(t=[]),m&&t.reduce===m)return i&&(e=S.bind(e,i)),r?t.reduce(e,n):t.reduce(e);if(P(t,function(t,o,s){r?n=e.call(i,n,t,o,s):(n=t,r=!0)}),!r)throw new TypeError(F);return n},S.reduceRight=S.foldr=function(t,e,n,i){var r=arguments.length>2;if(null==t&&(t=[]),v&&t.reduceRight===v)return i&&(e=S.bind(e,i)),r?t.reduceRight(e,n):t.reduceRight(e);var o=t.length;if(o!==+o){var s=S.keys(t);o=s.length}if(P(t,function(a,c,u){c=s?s[--o]:--o,r?n=e.call(i,n,t[c],c,u):(n=t[c],r=!0)}),!r)throw new TypeError(F);return n},S.find=S.detect=function(t,e,n){var i;return A(t,function(t,r,o){if(e.call(n,t,r,o))return i=t,!0}),i},S.filter=S.select=function(t,e,n){var i=[];return null==t?i:y&&t.filter===y?t.filter(e,n):(P(t,function(t,r,o){e.call(n,t,r,o)&&(i[i.length]=t)}),i)},S.reject=function(t,e,n){return S.filter(t,function(t,i,r){return!e.call(n,t,i,r)},n)},S.every=S.all=function(t,e,n){e||(e=S.identity);var i=!0;return null==t?i:g&&t.every===g?t.every(e,n):(P(t,function(t,o,s){if(!(i=i&&e.call(n,t,o,s)))return r}),!!i)};var A=S.some=S.any=function(t,e,n){e||(e=S.identity);var i=!1;return null==t?i:w&&t.some===w?t.some(e,n):(P(t,function(t,o,s){if(i||(i=e.call(n,t,o,s)))return r}),!!i)};S.contains=S.include=function(t,e){return null!=t&&(b&&t.indexOf===b?-1!=t.indexOf(e):A(t,function(t){return t===e}))},S.invoke=function(t,e){var n=u.call(arguments,2),i=S.isFunction(e);return S.map(t,function(t){return(i?e:t[e]).apply(t,n)})},S.pluck=function(t,e){return S.map(t,function(t){return t[e]})},S.where=function(t,e,n){return S.isEmpty(e)?n?null:[]:S[n?"find":"filter"](t,function(t){for(var n in e)if(e[n]!==t[n])return!1;return!0})},S.findWhere=function(t,e){return S.where(t,e,!0)},S.max=function(t,e,n){if(!e&&S.isArray(t)&&t[0]===+t[0]&&t.length<65535)return Math.max.apply(Math,t);if(!e&&S.isEmpty(t))return-Infinity;var i={computed:-Infinity,value:-Infinity};return P(t,function(t,r,o){var s=e?e.call(n,t,r,o):t;s>=i.computed&&(i={value:t,computed:s})}),i.value},S.min=function(t,e,n){if(!e&&S.isArray(t)&&t[0]===+t[0]&&t.length<65535)return Math.min.apply(Math,t);if(!e&&S.isEmpty(t))return Infinity;var i={computed:Infinity,value:Infinity};return P(t,function(t,r,o){var s=e?e.call(n,t,r,o):t;si||void 0===n)return 1;if(n>>1;n.call(i,t[a])=0})})},S.difference=function(t){var e=h.apply(o,u.call(arguments,1));return S.filter(t,function(t){return!S.contains(e,t)})},S.zip=function(){for(var t=u.call(arguments),e=S.max(S.pluck(t,"length")),n=new Array(e),i=0;i=0;n--)e=[t[n].apply(this,e)];return e[0]}},S.after=function(t,e){return t<=0?e():function(){if(--t<1)return e.apply(this,arguments)}},S.keys=E||function(t){if(t!==Object(t))throw new TypeError("Invalid object");var e=[];for(var n in t)S.has(t,n)&&(e[e.length]=n);return e},S.values=function(t){var e=[];for(var n in t)S.has(t,n)&&e.push(t[n]);return e},S.pairs=function(t){var e=[];for(var n in t)S.has(t,n)&&e.push([n,t[n]]);return e},S.invert=function(t){var e={};for(var n in t)S.has(t,n)&&(e[t[n]]=n);return e},S.functions=S.methods=function(t){var e=[];for(var n in t)S.isFunction(t[n])&&e.push(n);return e.sort()},S.extend=function(t){return P(u.call(arguments,1),function(e){if(e)for(var n in e)t[n]=e[n]}),t},S.pick=function(t){var e={},n=h.apply(o,u.call(arguments,1));return P(n,function(n){n in t&&(e[n]=t[n])}),e},S.omit=function(t){var e={},n=h.apply(o,u.call(arguments,1));for(var i in t)S.contains(n,i)||(e[i]=t[i]);return e},S.defaults=function(t){return P(u.call(arguments,1),function(e){if(e)for(var n in e)null==t[n]&&(t[n]=e[n])}),t},S.clone=function(t){return S.isObject(t)?S.isArray(t)?t.slice():S.extend({},t):t},S.tap=function(t,e){return e(t),t};var k=function(t,e,n,i){if(t===e)return 0!==t||1/t==1/e;if(null==t||null==e)return t===e;t instanceof S&&(t=t._wrapped),e instanceof S&&(e=e._wrapped);var r=l.call(t);if(r!=l.call(e))return!1;switch(r){case"[object String]":return t==String(e);case"[object Number]":return t!=+t?e!=+e:0==t?1/t==1/e:t==+e;case"[object Date]":case"[object Boolean]":return+t==+e;case"[object RegExp]":return t.source==e.source&&t.global==e.global&&t.multiline==e.multiline&&t.ignoreCase==e.ignoreCase}if("object"!=typeof t||"object"!=typeof e)return!1;for(var o=n.length;o--;)if(n[o]==t)return i[o]==e;n.push(t),i.push(e);var s=0,a=!0;if("[object Array]"==r){if(a=(s=t.length)==e.length)for(;s--&&(a=k(t[s],e[s],n,i)););}else{var c=t.constructor,u=e.constructor;if(c!==u&&!(S.isFunction(c)&&c instanceof c&&S.isFunction(u)&&u instanceof u))return!1;for(var h in t)if(S.has(t,h)&&(s++,!(a=S.has(e,h)&&k(t[h],e[h],n,i))))break;if(a){for(h in e)if(S.has(e,h)&&!s--)break;a=!s}}return n.pop(),i.pop(),a};S.isEqual=function(t,e){return k(t,e,[],[])},S.isEmpty=function(t){if(null==t)return!0;if(S.isArray(t)||S.isString(t))return 0===t.length;for(var e in t)if(S.has(t,e))return!1;return!0},S.isElement=function(t){return!(!t||1!==t.nodeType)},S.isArray=x||function(t){return"[object Array]"==l.call(t)},S.isObject=function(t){return t===Object(t)},P(["Arguments","Function","String","Number","Date","RegExp"],function(t){S["is"+t]=function(e){return l.call(e)=="[object "+t+"]"}}),S.isArguments(arguments)||(S.isArguments=function(t){return!(!t||!S.has(t,"callee"))}),S.isFunction=function(t){return"function"==typeof t},S.isFinite=function(t){return isFinite(t)&&!isNaN(parseFloat(t))},S.isNaN=function(t){return S.isNumber(t)&&t!=+t},S.isBoolean=function(t){return!0===t||!1===t||"[object Boolean]"==l.call(t)},S.isNull=function(t){return null===t},S.isUndefined=function(t){return void 0===t},S.has=function(t,e){return p.call(t,e)},S.noConflict=function(){return t._=i,this},S.identity=function(t){return t},S.times=function(t,e,n){for(var i=Array(t),r=0;r":">",'"':""","'":"'","/":"/"}};j.unescape=S.invert(j.escape);var R={escape:new RegExp("["+S.keys(j.escape).join("")+"]","g"),unescape:new RegExp("("+S.keys(j.unescape).join("|")+")","g")};S.each(["escape","unescape"],function(t){S[t]=function(e){return null==e?"":(""+e).replace(R[t],function(e){return j[t][e]})}}),S.result=function(t,e){if(null==t)return null;var n=t[e];return S.isFunction(n)?n.call(t):n},S.mixin=function(t){P(S.functions(t),function(e){var n=S[e]=t[e];S.prototype[e]=function(){var t=[this._wrapped];return c.apply(t,arguments),L.call(this,n.apply(S,t))}})};var D=0;S.uniqueId=function(t){var e=++D+"";return t?t+e:e},S.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var z=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n","\t":"t","\u2028":"u2028","\u2029":"u2029"},I=/\\|'|\r|\n|\t|\u2028|\u2029/g;S.template=function(t,e,n){var i;n=S.defaults({},n,S.templateSettings);var r=new RegExp([(n.escape||z).source,(n.interpolate||z).source,(n.evaluate||z).source].join("|")+"|$","g"),o=0,s="__p+='";t.replace(r,function(e,n,i,r,a){return s+=t.slice(o,a).replace(I,function(t){return"\\"+q[t]}),n&&(s+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),i&&(s+="'+\n((__t=("+i+"))==null?'':__t)+\n'"),r&&(s+="';\n"+r+"\n__p+='"),o=a+e.length,e}),s+="';\n",n.variable||(s="with(obj||{}){\n"+s+"}\n"),s="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+s+"return __p;\n";try{i=new Function(n.variable||"obj","_",s)}catch(t){throw t.source=s,t}if(e)return i(e,S);var a=function(t){return i.call(this,t,S)};return a.source="function("+(n.variable||"obj")+"){\n"+s+"}",a},S.chain=function(t){return S(t).chain()};var L=function(t){return this._chain?S(t).chain():t};S.mixin(S),P(["pop","push","reverse","shift","sort","splice","unshift"],function(t){var e=o[t];S.prototype[t]=function(){var n=this._wrapped;return e.apply(n,arguments),"shift"!=t&&"splice"!=t||0!==n.length||delete n[0],L.call(this,n)}}),P(["concat","join","slice"],function(t){var e=o[t];S.prototype[t]=function(){return L.call(this,e.apply(this._wrapped,arguments))}}),S.extend(S.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this)},{}],25:[function(t,e,n){"undefined"!=typeof window&&"function"!=typeof window.requestAnimationFrame&&(window.requestAnimationFrame=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){setTimeout(t,1e3/60)}),Leap=t("../lib/index")},{"../lib/index":11}]},{},[25]);var t=new THREE.Vector3,e=new THREE.Quaternion;Leap.Controller.plugin("transform",function(t){var e,n,i,r,o,s;return t||(t={}),e=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],s=new THREE.Matrix4,!0===t.vr&&(this.setOptimizeHMD(!0),t.quaternion=(new THREE.Quaternion).setFromRotationMatrix((new THREE.Matrix4).set(-1,0,0,0,0,0,-1,0,0,-1,0,0,0,0,0,1)),t.scale=.001,t.position=new THREE.Vector3(0,0,-.08)),"desktop"===t.vr&&(t.scale=.001),t.getTransform=function(n){var i;return t.matrix?(i="function"==typeof t.matrix?t.matrix(n):t.matrix,window.THREE&&i instanceof THREE.Matrix4?i.elements:i):t.position||t.quaternion||t.scale?(s.set.apply(s,e),t.quaternion&&s.makeRotationFromQuaternion("function"==typeof t.quaternion?t.quaternion(n):t.quaternion),t.position&&s.setPosition("function"==typeof t.position?t.position(n):t.position),s.elements):e},t.getScale=function(e){return isNaN(t.scale)||(t.scale=new THREE.Vector3(t.scale,t.scale,t.scale)),"function"==typeof t.scale?t.scale(e):t.scale},r=function(t,e){var n,i,r,o;for(o=[],i=0,r=e.length;i1&&this.armBones[i].quaternion.multiply(a),this.armMesh.add(this.armBones[i]);for(this.armSpheres=[],i=0;i<=3;i++)this.armSpheres.push(new THREE.Mesh(new THREE.SphereGeometry(n,t.segments,t.segments),this.material.clone())),this.armSpheres[i].material.color.setHex(t.jointColor),this.armSpheres[i].castShadow=!0,this.armSpheres[i].name="ArmSphere"+i,this.armMesh.add(this.armSpheres[i]);return this.object3D.add(this.armMesh),this}},u.prototype.traverse=function(t){for(var e,n=0;n<5;n++)for(var i=0,r=(e=this.fingerMeshes[n]).length;i(this.isHolding?this.data.releaseSensitivity:this.data.holdSensitivity);this.intersector.update(this.data,this.el.object3D,t,e),e&&!this.isHolding&&this.hold(t),!e&&this.isHolding&&this.release(t)}else this.isHolding&&this.release(null);t&&!this.isVisible&&(this.handMesh.show(),this.intersector.show()),!t&&this.isVisible&&(this.handMesh.hide(),this.intersector.hide()),this.isVisible=!!t},getHand:function(){var t=this.data,e=this.system.getFrame();return e.hands.length?e.hands[e.hands[0].type===t.hand?0:1]:null},hold:function(t){var e,n,i=this.getEventDetail(t);this.el.emit("leap-holdstart",i),e=[].slice.call(this.el.sceneEl.querySelectorAll(this.data.holdSelector)).map(function(t){return t.object3D}),n=this.intersector.intersectObjects(e,!0),this.holdTarget=n[0]&&n[0].object&&n[0].object.el,this.holdTarget&&this.holdTarget.emit("leap-holdstart",i),this.isHolding=!0},release:function(t){var e=this.getEventDetail(t);this.el.emit("leap-holdstop",e),this.holdTarget&&(this.holdTarget.emit("leap-holdstop",e),this.holdTarget=null),this.isHolding=!1},getEventDetail:function(t){return{hand:t,handID:this.handID,body:this.handBody?this.handBody.palmBody:null}}});function m(t){var e=0;t=t.array();for(var n=0;n=this.size||t>=this._buf.length))return this._buf[(this.pos-t-1)%this.size]},i.prototype.push=function(t){return this._buf[this.pos%this.size]=t,this.pos++}},{}],3:[function(t,e,n){var i=t("../protocol").chooseProtocol,r=t("events").EventEmitter,o=t("underscore"),s=e.exports=function(t){this.opts=o.defaults(t||{},{host:"127.0.0.1",enableGestures:!1,scheme:this.getScheme(),port:this.getPort(),background:!1,optimizeHMD:!1,requestProtocolVersion:s.defaultProtocolVersion}),this.host=this.opts.host,this.port=this.opts.port,this.scheme=this.opts.scheme,this.protocolVersionVerified=!1,this.background=null,this.optimizeHMD=null,this.on("ready",function(){this.enableGestures(this.opts.enableGestures),this.setBackground(this.opts.background),this.setOptimizeHMD(this.opts.optimizeHMD),this.opts.optimizeHMD?console.log("Optimized for head mounted display usage."):console.log("Optimized for desktop usage.")})};s.defaultProtocolVersion=6,s.prototype.getUrl=function(){return this.scheme+"//"+this.host+":"+this.port+"/v"+this.opts.requestProtocolVersion+".json"},s.prototype.getScheme=function(){return"ws:"},s.prototype.getPort=function(){return 6437},s.prototype.setBackground=function(t){this.opts.background=t,this.protocol&&this.protocol.sendBackground&&this.background!==this.opts.background&&(this.background=this.opts.background,this.protocol.sendBackground(this,this.opts.background))},s.prototype.setOptimizeHMD=function(t){this.opts.optimizeHMD=t,this.protocol&&this.protocol.sendOptimizeHMD&&this.optimizeHMD!==this.opts.optimizeHMD&&(this.optimizeHMD=this.opts.optimizeHMD,this.protocol.sendOptimizeHMD(this,this.opts.optimizeHMD))},s.prototype.handleOpen=function(){this.connected||(this.connected=!0,this.emit("connect"))},s.prototype.enableGestures=function(t){this.gesturesEnabled=!!t,this.send(this.protocol.encode({enableGestures:this.gesturesEnabled}))},s.prototype.handleClose=function(t,e){this.connected&&(this.disconnect(),1001===t&&this.opts.requestProtocolVersion>1&&(this.protocolVersionVerified?this.protocolVersionVerified=!1:this.opts.requestProtocolVersion--),this.startReconnection())},s.prototype.startReconnection=function(){var t=this;this.reconnectionTimer||(this.reconnectionTimer=setInterval(function(){t.reconnect()},500))},s.prototype.stopReconnection=function(){this.reconnectionTimer=clearInterval(this.reconnectionTimer)},s.prototype.disconnect=function(t){if(t||this.stopReconnection(),this.socket)return this.socket.close(),delete this.socket,delete this.protocol,delete this.background,delete this.optimizeHMD,delete this.focusedState,this.connected&&(this.connected=!1,this.emit("disconnect")),!0},s.prototype.reconnect=function(){this.connected?this.stopReconnection():(this.disconnect(!0),this.connect())},s.prototype.handleData=function(t){var e,n=JSON.parse(t);void 0===this.protocol?(e=this.protocol=i(n),this.protocolVersionVerified=!0,this.emit("ready")):e=this.protocol(n),this.emit(e.type,e)},s.prototype.connect=function(){if(!this.socket)return this.socket=this.setupSocket(),!0},s.prototype.send=function(t){this.socket.send(t)},s.prototype.reportFocus=function(t){this.connected&&this.focusedState!==t&&(this.focusedState=t,this.emit(this.focusedState?"focus":"blur"),this.protocol&&this.protocol.sendFocused&&this.protocol.sendFocused(this,this.focusedState))},o.extend(s.prototype,r.prototype)},{"../protocol":15,events:21,underscore:24}],4:[function(t,e,n){var i=e.exports=t("./base"),r=t("underscore"),o=e.exports=function(t){i.call(this,t);var e=this;this.on("ready",function(){e.startFocusLoop()}),this.on("disconnect",function(){e.stopFocusLoop()})};r.extend(o.prototype,i.prototype),o.__proto__=i,o.prototype.useSecure=function(){return"https:"===location.protocol},o.prototype.getScheme=function(){return this.useSecure()?"wss:":"ws:"},o.prototype.getPort=function(){return this.useSecure()?6436:6437},o.prototype.setupSocket=function(){var t=this,e=new WebSocket(this.getUrl());return e.onopen=function(){t.handleOpen()},e.onclose=function(e){t.handleClose(e.code,e.reason)},e.onmessage=function(e){t.handleData(e.data)},e.onerror=function(e){t.useSecure()&&"wss:"===t.scheme&&(t.scheme="ws:",t.port=6437,t.disconnect(),t.connect())},e},o.prototype.startFocusLoop=function(){if(!this.focusDetectorTimer){var t=this,e=null;e=void 0!==document.hidden?"hidden":void 0!==document.mozHidden?"mozHidden":void 0!==document.msHidden?"msHidden":void 0!==document.webkitHidden?"webkitHidden":void 0,void 0===t.windowVisible&&(t.windowVisible=void 0===e||!1===document[e]);var n=window.addEventListener("focus",function(e){t.windowVisible=!0,r()}),i=window.addEventListener("blur",function(e){t.windowVisible=!1,r()});this.on("disconnect",function(){window.removeEventListener("focus",n),window.removeEventListener("blur",i)});var r=function(){var n=void 0===e||!1===document[e];t.reportFocus(n&&t.windowVisible)};r(),this.focusDetectorTimer=setInterval(r,100)}},o.prototype.stopFocusLoop=function(){this.focusDetectorTimer&&(clearTimeout(this.focusDetectorTimer),delete this.focusDetectorTimer)}},{"./base":3,underscore:24}],5:[function(t,e,n){var i=t("__browserify_process"),r=t("./frame"),o=t("./hand"),s=t("./pointable"),a=t("./finger"),c=t("./circular_buffer"),u=t("./pipeline"),h=t("events").EventEmitter,l=t("./gesture").gestureListener,p=t("./dialog"),d=t("underscore"),f=e.exports=function(e){var n=this;e=d.defaults(e||{},{inNode:void 0!==i&&i.versions&&i.versions.node}),this.inNode=e.inNode,e=d.defaults(e||{},{frameEventName:this.useAnimationLoop()?"animationFrame":"deviceFrame",suppressAnimationLoop:!this.useAnimationLoop(),loopWhileDisconnected:!0,useAllPlugins:!1,checkVersion:!0}),this.animationFrameRequested=!1,this.onAnimationFrame=function(t){n.lastConnectionFrame.valid&&n.emit("animationFrame",n.lastConnectionFrame),n.emit("frameEnd",t),n.loopWhileDisconnected&&(!1!==n.connection.focusedState||n.connection.opts.background)?window.requestAnimationFrame(n.onAnimationFrame):n.animationFrameRequested=!1},this.suppressAnimationLoop=e.suppressAnimationLoop,this.loopWhileDisconnected=e.loopWhileDisconnected,this.frameEventName=e.frameEventName,this.useAllPlugins=e.useAllPlugins,this.history=new c(200),this.lastFrame=r.Invalid,this.lastValidFrame=r.Invalid,this.lastConnectionFrame=r.Invalid,this.accumulatedGestures=[],this.checkVersion=e.checkVersion,this.connectionType=void 0===e.connectionType?this.inBrowser()?t("./connection/browser"):t("./connection/node"):e.connectionType,this.connection=new this.connectionType(e),this.streamingCount=0,this.devices={},this.plugins={},this._pluginPipelineSteps={},this._pluginExtendedMethods={},e.useAllPlugins&&this.useRegisteredPlugins(),this.setupFrameEvents(e),this.setupConnectionEvents(),this.startAnimationLoop()};f.prototype.gesture=function(t,e){var n=l(this,t);return void 0!==e&&n.stop(e),n},f.prototype.setBackground=function(t){return this.connection.setBackground(t),this},f.prototype.setOptimizeHMD=function(t){return this.connection.setOptimizeHMD(t),this},f.prototype.inBrowser=function(){return!this.inNode},f.prototype.useAnimationLoop=function(){return this.inBrowser()&&!this.inBackgroundPage()},f.prototype.inBackgroundPage=function(){return"undefined"!=typeof chrome&&chrome.extension&&chrome.extension.getBackgroundPage&&chrome.extension.getBackgroundPage()===window},f.prototype.connect=function(){return this.connection.connect(),this},f.prototype.streaming=function(){return this.streamingCount>0},f.prototype.connected=function(){return!!this.connection.connected},f.prototype.startAnimationLoop=function(){this.suppressAnimationLoop||this.animationFrameRequested||(this.animationFrameRequested=!0,window.requestAnimationFrame(this.onAnimationFrame))},f.prototype.disconnect=function(){return this.connection.disconnect(),this},f.prototype.frame=function(t){return this.history.get(t)||r.Invalid},f.prototype.loop=function(t){return t&&("function"==typeof t?this.on(this.frameEventName,t):this.setupFrameEvents(t)),this.connect()},f.prototype.addStep=function(t){this.pipeline||(this.pipeline=new u(this)),this.pipeline.addStep(t)},f.prototype.processFrame=function(t){t.gestures&&(this.accumulatedGestures=this.accumulatedGestures.concat(t.gestures)),this.lastConnectionFrame=t,this.startAnimationLoop(),this.emit("deviceFrame",t)},f.prototype.processFinishedFrame=function(t){if(this.lastFrame=t,t.valid&&(this.lastValidFrame=t),t.controller=this,t.historyIdx=this.history.push(t),t.gestures){t.gestures=this.accumulatedGestures,this.accumulatedGestures=[];for(var e=0;e!=t.gestures.length;e++)this.emit("gesture",t.gestures[e],t)}this.pipeline&&((t=this.pipeline.run(t))||(t=r.Invalid)),this.emit("frame",t),this.emitHandEvents(t)},f.prototype.emitHandEvents=function(t){for(var e=0;e0){for(var e in t.devices)t.emit("deviceStopped",t.devices[e]),t.emit("deviceRemoved",t.devices[e]);for(var e in t.emit("streamingStopped",t.devices[e]),t.streamingCount=0,t.devices)delete t.devices[e]}};this.connection.on("focus",function(){t.loopWhileDisconnected&&t.startAnimationLoop(),t.emit("focus")}),this.connection.on("blur",function(){t.emit("blur")}),this.connection.on("protocol",function(e){e.on("beforeFrameCreated",function(e){t.emit("beforeFrameCreated",e)}),e.on("afterFrameCreated",function(e,n){t.emit("afterFrameCreated",e,n)}),t.emit("protocol",e)}),this.connection.on("ready",function(){t.checkVersion&&!t.inNode&&t.checkOutOfDate(),t.emit("ready")}),this.connection.on("connect",function(){t.emit("connect"),t.connection.removeListener("frame",e),t.connection.on("frame",e)}),this.connection.on("disconnect",function(){t.emit("disconnect"),n()}),this.connection.on("deviceConnect",function(i){i.state?(t.emit("deviceConnected"),t.connection.removeListener("frame",e),t.connection.on("frame",e)):(t.emit("deviceDisconnected"),n())}),this.connection.on("deviceEvent",function(e){var n=e.state,i=t.devices[n.id],r={};for(var o in n)i&&i.hasOwnProperty(o)&&i[o]==n[o]||(r[o]=!0);t.devices[n.id]=n,r.attached&&t.emit(n.attached?"deviceAttached":"deviceRemoved",n),r.streaming&&(n.streaming?(t.streamingCount++,t.emit("deviceStreaming",n),1==t.streamingCount&&t.emit("streamingStarted",n),r.attached||t.emit("deviceConnected")):r.attached&&n.attached||(t.streamingCount--,t.emit("deviceStopped",n),0==t.streamingCount&&t.emit("streamingStopped",n),t.emit("deviceDisconnected")))}),this.on("newListener",function(t,e){"deviceConnected"!=t&&"deviceDisconnected"!=t||console.warn(t+" events are depricated. Consider using 'streamingStarted/streamingStopped' or 'deviceStreaming/deviceStopped' instead")})},f.prototype.checkOutOfDate=function(){console.assert(this.connection&&this.connection.protocol);var t=this.connection.protocol.serviceVersion,e=this.connection.protocol.version,n=this.connectionType.defaultProtocolVersion;return n>e&&(console.warn("Your Protocol Version is v"+e+", this app was designed for v"+n),p.warnOutOfDate({sV:t,pV:e}),!0)},f._pluginFactories={},f.plugin=function(t,e){return this._pluginFactories[t]&&console.warn('Plugin "'+t+'" already registered'),this._pluginFactories[t]=e},f.plugins=function(){return d.keys(this._pluginFactories)};var m=function(t,e,n){-1!=["beforeFrameCreated","afterFrameCreated"].indexOf(e)?this.on(e,n):(this.pipeline||(this.pipeline=new u(this)),this._pluginPipelineSteps[t]||(this._pluginPipelineSteps[t]=[]),this._pluginPipelineSteps[t].push(this.pipeline.addWrappedStep(e,n)))},v=function(t,e,n){var i;switch(this._pluginExtendedMethods[t]||(this._pluginExtendedMethods[t]=[]),e){case"frame":i=r;break;case"hand":i=o;break;case"pointable":i=s,d.extend(a.prototype,n),d.extend(a.Invalid,n);break;case"finger":i=a;break;default:throw t+' specifies invalid object type "'+e+'" for prototypical extension'}d.extend(i.prototype,n),d.extend(i.Invalid,n),this._pluginExtendedMethods[t].push([i,n])};f.prototype.use=function(t,e){var n,i,r,o;if(!(i="function"==typeof t?t:f._pluginFactories[t]))throw"Leap Plugin "+t+" not found.";if(e||(e={}),this.plugins[t])return d.extend(this.plugins[t],e),this;for(r in this.plugins[t]=e,o=i.call(this,e))"function"==typeof(n=o[r])?m.call(this,t,r,n):v.call(this,t,r,n);return this},f.prototype.stopUsing=function(t){var e,n=this._pluginPipelineSteps[t],i=this._pluginExtendedMethods[t],r=0;if(this.plugins[t]){if(n)for(r=0;rUpgrade",{onclick:function(t){if("leapjs-decline-upgrade"!=t.target.id){var n=window.open(e,"_blank","height=800,width=1000,location=1,menubar=1,resizable=1,status=1,toolbar=1,scrollbars=1");window.focus&&n.focus()}return i.hide(),!0},onmousemove:function(t){t.target==document.getElementById("leapjs-decline-upgrade")?(document.getElementById("leapjs-decline-upgrade").style.color="#000",document.getElementById("leapjs-decline-upgrade").style.boxShadow="0px 0px 2px #5daa00",document.getElementById("leapjs-accept-upgrade").style.color="#444",document.getElementById("leapjs-accept-upgrade").style.boxShadow="none"):(document.getElementById("leapjs-accept-upgrade").style.color="#000",document.getElementById("leapjs-accept-upgrade").style.boxShadow="0px 0px 2px #5daa00",document.getElementById("leapjs-decline-upgrade").style.color="#444",document.getElementById("leapjs-decline-upgrade").style.boxShadow="none")},onmouseout:function(){document.getElementById("leapjs-decline-upgrade").style.color="#444",document.getElementById("leapjs-decline-upgrade").style.boxShadow="none",document.getElementById("leapjs-accept-upgrade").style.color="#444",document.getElementById("leapjs-accept-upgrade").style.boxShadow="none"}})).show()},r.hasWarnedBones=!1,r.warnBones=function(){this.hasWarnedBones||(this.hasWarnedBones=!0,console.warn("Your Leap Service is out of date"),void 0!==i&&i.versions&&i.versions.node||this.warnOutOfDate({reason:"bones"}))}},{__browserify_process:22}],7:[function(t,e,n){var i=t("./pointable"),r=t("./bone"),o=t("./dialog"),s=t("underscore"),a=e.exports=function(t){i.call(this,t),this.dipPosition=t.dipPosition,this.pipPosition=t.pipPosition,this.mcpPosition=t.mcpPosition,this.carpPosition=t.carpPosition,this.extended=t.extended,this.type=t.type,this.finger=!0,this.positions=[this.carpPosition,this.mcpPosition,this.pipPosition,this.dipPosition,this.tipPosition],t.bases?this.addBones(t):o.warnBones()};s.extend(a.prototype,i.prototype),a.prototype.addBones=function(t){this.metacarpal=new r(this,{type:0,width:this.width,prevJoint:this.carpPosition,nextJoint:this.mcpPosition,basis:t.bases[0]}),this.proximal=new r(this,{type:1,width:this.width,prevJoint:this.mcpPosition,nextJoint:this.pipPosition,basis:t.bases[1]}),this.medial=new r(this,{type:2,width:this.width,prevJoint:this.pipPosition,nextJoint:this.dipPosition,basis:t.bases[2]}),this.distal=new r(this,{type:3,width:this.width,prevJoint:this.dipPosition,nextJoint:t.btipPosition,basis:t.bases[3]}),this.bones=[this.metacarpal,this.proximal,this.medial,this.distal]},a.prototype.toString=function(){return"Finger [ id:"+this.id+" "+this.length+"mmx | width:"+this.width+"mm | direction:"+this.direction+" ]"},a.Invalid={valid:!1}},{"./bone":1,"./dialog":6,"./pointable":14,underscore:24}],8:[function(t,e,n){var i=t("./hand"),r=t("./pointable"),o=t("./gesture").createGesture,s=t("gl-matrix"),a=s.mat3,c=s.vec3,u=t("./interaction_box"),h=t("./finger"),l=t("underscore"),p=e.exports=function(t){if(this.valid=!0,this.id=t.id,this.timestamp=t.timestamp,this.hands=[],this.handsMap={},this.pointables=[],this.tools=[],this.fingers=[],t.interactionBox&&(this.interactionBox=new u(t.interactionBox)),this.gestures=[],this.pointablesMap={},this._translation=t.t,this._rotation=l.flatten(t.r),this._scaleFactor=t.s,this.data=t,this.type="frame",this.currentFrameRate=t.currentFrameRate,t.gestures)for(var e=0,n=t.gestures.length;e!=n;e++)this.gestures.push(o(t.gestures[e]));this.postprocessData(t)};p.prototype.postprocessData=function(t){t||(t=this.data);for(var e=0,n=t.hands.length;e!=n;e++){var o=new i(t.hands[e]);o.frame=this,this.hands.push(o),this.handsMap[o.id]=o}t.pointables=l.sortBy(t.pointables,function(t){return t.id});for(var s=0,a=t.pointables.length;s!=a;s++){var c=t.pointables[s],u=c.dipPosition?new h(c):new r(c);u.frame=this,this.addPointable(u)}},p.prototype.addPointable=function(t){if(this.pointables.push(t),this.pointablesMap[t.id]=t,(t.tool?this.tools:this.fingers).push(t),void 0!==t.handId&&this.handsMap.hasOwnProperty(t.handId)){var e=this.handsMap[t.handId];switch(e.pointables.push(t),(t.tool?e.tools:e.fingers).push(t),t.type){case 0:e.thumb=t;break;case 1:e.indexFinger=t;break;case 2:e.middleFinger=t;break;case 3:e.ringFinger=t;break;case 4:e.pinky=t}}},p.prototype.tool=function(t){var e=this.pointable(t);return e.tool?e:r.Invalid},p.prototype.pointable=function(t){return this.pointablesMap[t]||r.Invalid},p.prototype.finger=function(t){var e=this.pointable(t);return e.tool?r.Invalid:e},p.prototype.hand=function(t){return this.handsMap[t]||i.Invalid},p.prototype.rotationAngle=function(t,e){if(!this.valid||!t.valid)return 0;var n=this.rotationMatrix(t),i=Math.acos(.5*(n[0]+n[4]+n[8]-1));if(i=isNaN(i)?0:i,void 0!==e){var r=this.rotationAxis(t);i*=c.dot(r,c.normalize(c.create(),e))}return i},p.prototype.rotationAxis=function(t){return this.valid&&t.valid?c.normalize(c.create(),[this._rotation[7]-t._rotation[5],this._rotation[2]-t._rotation[6],this._rotation[3]-t._rotation[1]]):c.create()},p.prototype.rotationMatrix=function(t){if(!this.valid||!t.valid)return a.create();var e=a.transpose(a.create(),this._rotation);return a.multiply(a.create(),t._rotation,e)},p.prototype.scaleFactor=function(t){return this.valid&&t.valid?Math.exp(this._scaleFactor-t._scaleFactor):1},p.prototype.translation=function(t){return this.valid&&t.valid?c.subtract(c.create(),this._translation,t._translation):c.create()},p.prototype.toString=function(){var t="Frame [ id:"+this.id+" | timestamp:"+this.timestamp+" | Hand count:("+this.hands.length+") | Pointable count:("+this.pointables.length+")";return this.gestures&&(t+=" | Gesture count:("+this.gestures.length+")"),t+=" ]"},p.prototype.dump=function(){var t="";t+="Frame Info:
",t+=this.toString(),t+="

Hands:
";for(var e=0,n=this.hands.length;e!=n;e++)t+=" "+this.hands[e].toString()+"
";t+="

Pointables:
";for(var i=0,r=this.pointables.length;i!=r;i++)t+=" "+this.pointables[i].toString()+"
";if(this.gestures){t+="

Gestures:
";for(var o=0,s=this.gestures.length;o!=s;o++)t+=" "+this.gestures[o].toString()+"
"}return t+="

Raw JSON:
",t+=JSON.stringify(this.data)},p.Invalid={valid:!1,hands:[],fingers:[],tools:[],gestures:[],pointables:[],pointable:function(){return r.Invalid},finger:function(){return r.Invalid},hand:function(){return i.Invalid},toString:function(){return"invalid frame"},dump:function(){return this.toString()},rotationAngle:function(){return 0},rotationMatrix:function(){return a.create()},rotationAxis:function(){return c.create()},scaleFactor:function(){return 1},translation:function(){return c.create()}}},{"./finger":7,"./gesture":9,"./hand":10,"./interaction_box":12,"./pointable":14,"gl-matrix":23,underscore:24}],9:[function(t,e,n){var i=t("gl-matrix").vec3,r=t("events").EventEmitter,o=t("underscore"),s=(n.createGesture=function(t){var e;switch(t.type){case"circle":e=new a(t);break;case"swipe":e=new c(t);break;case"screenTap":e=new u(t);break;case"keyTap":e=new h(t);break;default:throw"unknown gesture type"}return e.id=t.id,e.handIds=t.handIds.slice(),e.pointableIds=t.pointableIds.slice(),e.duration=t.duration,e.state=t.state,e.type=t.type,e},n.gestureListener=function(t,e){var n={},i={};t.on("gesture",function(t,r){if(t.type==e){if(("start"==t.state||"stop"==t.state)&&void 0===i[t.id]){var a=new s(t,r);i[t.id]=a,o.each(n,function(t,e){a.on(e,t)})}i[t.id].update(t,r),"stop"==t.state&&delete i[t.id]}});var r={start:function(t){return n.start=t,r},stop:function(t){return n.stop=t,r},complete:function(t){return n.stop=t,r},update:function(t){return n.update=t,r}};return r},n.Gesture=function(t,e){this.gestures=[t],this.frames=[e]});s.prototype.update=function(t,e){this.lastGesture=t,this.lastFrame=e,this.gestures.push(t),this.frames.push(e),this.emit(t.state,this)},s.prototype.translation=function(){return i.subtract(i.create(),this.lastGesture.startPosition,this.lastGesture.position)},o.extend(s.prototype,r.prototype);var a=function(t){this.center=t.center,this.normal=t.normal,this.progress=t.progress,this.radius=t.radius};a.prototype.toString=function(){return"CircleGesture ["+JSON.stringify(this)+"]"};var c=function(t){this.startPosition=t.startPosition,this.position=t.position,this.direction=t.direction,this.speed=t.speed};c.prototype.toString=function(){return"SwipeGesture ["+JSON.stringify(this)+"]"};var u=function(t){this.position=t.position,this.direction=t.direction,this.progress=t.progress};u.prototype.toString=function(){return"ScreenTapGesture ["+JSON.stringify(this)+"]"};var h=function(t){this.position=t.position,this.direction=t.direction,this.progress=t.progress};h.prototype.toString=function(){return"KeyTapGesture ["+JSON.stringify(this)+"]"}},{events:21,"gl-matrix":23,underscore:24}],10:[function(t,e,n){var i=t("./pointable"),r=t("./bone"),o=t("gl-matrix"),s=o.mat3,a=o.vec3,c=t("underscore"),u=e.exports=function(t){this.id=t.id,this.palmPosition=t.palmPosition,this.direction=t.direction,this.palmVelocity=t.palmVelocity,this.palmNormal=t.palmNormal,this.sphereCenter=t.sphereCenter,this.sphereRadius=t.sphereRadius,this.valid=!0,this.pointables=[],this.fingers=[],this.arm=t.armBasis?new r(this,{type:4,width:t.armWidth,prevJoint:t.elbow,nextJoint:t.wrist,basis:t.armBasis}):null,this.tools=[],this._translation=t.t,this._rotation=c.flatten(t.r),this._scaleFactor=t.s,this.timeVisible=t.timeVisible,this.stabilizedPalmPosition=t.stabilizedPalmPosition,this.type=t.type,this.grabStrength=t.grabStrength,this.pinchStrength=t.pinchStrength,this.confidence=t.confidence};u.prototype.finger=function(t){var e=this.frame.finger(t);return e&&e.handId==this.id?e:i.Invalid},u.prototype.rotationAngle=function(t,e){if(!this.valid||!t.valid)return 0;if(!t.hand(this.id).valid)return 0;var n=this.rotationMatrix(t),i=Math.acos(.5*(n[0]+n[4]+n[8]-1));if(i=isNaN(i)?0:i,void 0!==e){var r=this.rotationAxis(t);i*=a.dot(r,a.normalize(a.create(),e))}return i},u.prototype.rotationAxis=function(t){if(!this.valid||!t.valid)return a.create();var e=t.hand(this.id);return e.valid?a.normalize(a.create(),[this._rotation[7]-e._rotation[5],this._rotation[2]-e._rotation[6],this._rotation[3]-e._rotation[1]]):a.create()},u.prototype.rotationMatrix=function(t){if(!this.valid||!t.valid)return s.create();var e=t.hand(this.id);if(!e.valid)return s.create();var n=s.transpose(s.create(),this._rotation);return s.multiply(s.create(),e._rotation,n)},u.prototype.scaleFactor=function(t){if(!this.valid||!t.valid)return 1;var e=t.hand(this.id);return e.valid?Math.exp(this._scaleFactor-e._scaleFactor):1},u.prototype.translation=function(t){if(!this.valid||!t.valid)return a.create();var e=t.hand(this.id);return e.valid?[this._translation[0]-e._translation[0],this._translation[1]-e._translation[1],this._translation[2]-e._translation[2]]:a.create()},u.prototype.toString=function(){return"Hand ("+this.type+") [ id: "+this.id+" | palm velocity:"+this.palmVelocity+" | sphere center:"+this.sphereCenter+" ] "},u.prototype.pitch=function(){return Math.atan2(this.direction[1],-this.direction[2])},u.prototype.yaw=function(){return Math.atan2(this.direction[0],-this.direction[2])},u.prototype.roll=function(){return Math.atan2(this.palmNormal[0],-this.palmNormal[1])},u.Invalid={valid:!1,fingers:[],tools:[],pointables:[],left:!1,pointable:function(){return i.Invalid},finger:function(){return i.Invalid},toString:function(){return"invalid frame"},dump:function(){return this.toString()},rotationAngle:function(){return 0},rotationMatrix:function(){return s.create()},rotationAxis:function(){return a.create()},scaleFactor:function(){return 1},translation:function(){return a.create()}}},{"./bone":1,"./pointable":14,"gl-matrix":23,underscore:24}],11:[function(t,e,n){e.exports={Controller:t("./controller"),Frame:t("./frame"),Gesture:t("./gesture"),Hand:t("./hand"),Pointable:t("./pointable"),Finger:t("./finger"),InteractionBox:t("./interaction_box"),CircularBuffer:t("./circular_buffer"),UI:t("./ui"),JSONProtocol:t("./protocol").JSONProtocol,glMatrix:t("gl-matrix"),mat3:t("gl-matrix").mat3,vec3:t("gl-matrix").vec3,loopController:void 0,version:t("./version.js"),_:t("underscore"),EventEmitter:t("events").EventEmitter,loop:function(t,e){return t&&void 0===e&&"[object Function]"==={}.toString.call(t)&&(e=t,t={}),this.loopController?t&&this.loopController.setupFrameEvents(t):this.loopController=new this.Controller(t),this.loopController.loop(e),this.loopController},plugin:function(t,e){this.Controller.plugin(t,e)}}},{"./circular_buffer":2,"./controller":5,"./finger":7,"./frame":8,"./gesture":9,"./hand":10,"./interaction_box":12,"./pointable":14,"./protocol":15,"./ui":16,"./version.js":19,events:21,"gl-matrix":23,underscore:24}],12:[function(t,e,n){var i=t("gl-matrix").vec3,r=e.exports=function(t){this.valid=!0,this.center=t.center,this.size=t.size,this.width=t.size[0],this.height=t.size[1],this.depth=t.size[2]};r.prototype.denormalizePoint=function(t){return i.fromValues((t[0]-.5)*this.size[0]+this.center[0],(t[1]-.5)*this.size[1]+this.center[1],(t[2]-.5)*this.size[2]+this.center[2])},r.prototype.normalizePoint=function(t,e){var n=i.fromValues((t[0]-this.center[0])/this.size[0]+.5,(t[1]-this.center[1])/this.size[1]+.5,(t[2]-this.center[2])/this.size[2]+.5);return e&&(n[0]=Math.min(Math.max(n[0],0),1),n[1]=Math.min(Math.max(n[1],0),1),n[2]=Math.min(Math.max(n[2],0),1)),n},r.prototype.toString=function(){return"InteractionBox [ width:"+this.width+" | height:"+this.height+" | depth:"+this.depth+" ]"},r.Invalid={valid:!1}},{"gl-matrix":23}],13:[function(t,e,n){var i=e.exports=function(t){this.steps=[],this.controller=t};i.prototype.addStep=function(t){this.steps.push(t)},i.prototype.run=function(t){for(var e=this.steps.length,n=0;n!=e&&t;n++)t=this.steps[n](t);return t},i.prototype.removeStep=function(t){var e=this.steps.indexOf(t);if(-1===e)throw"Step not found in pipeline";this.steps.splice(e,1)},i.prototype.addWrappedStep=function(t,e){var n=this.controller,i=function(i){var r,o,s;for(o=0,s=(r="frame"==t?[i]:i[t+"s"]||[]).length;o=this.start.x&&n.x<=this.end.x&&n.y>=this.start.y&&n.y<=this.end.y&&n.z>=this.start.z&&n.z<=this.end.z)return!0}return!1},o.prototype.listener=function(t){var e=this;return t&&t.nearThreshold&&this.setupNearRegion(t.nearThreshold),function(t){return e.updatePosition(t)}},o.prototype.clipper=function(){var t=this;return function(e){return t.updatePosition(e),t.enteredFrame?e:null}},o.prototype.setupNearRegion=function(t){var e=this.nearRegion=new o([this.start.x-t,this.start.y-t,this.start.z-t],[this.end.x+t,this.end.y+t,this.end.z+t]),n=this;e.on("enter",function(t){n.emit("near",t)}),e.on("exit",function(t){n.emit("far",t)}),n.on("exit",function(t){n.emit("near",t)})},o.prototype.updatePosition=function(t){return this.nearRegion&&this.nearRegion.updatePosition(t),this.hasPointables(t)&&null==this.enteredFrame?(this.enteredFrame=t,this.emit("enter",this.enteredFrame)):this.hasPointables(t)||null==this.enteredFrame||(this.enteredFrame=null,this.emit("exit",this.enteredFrame)),t},o.prototype.normalize=function(t){return new Vector([(t.x-this.start.x)/(this.end.x-this.start.x),(t.y-this.start.y)/(this.end.y-this.start.y),(t.z-this.start.z)/(this.end.z-this.start.z)])},o.prototype.mapToXY=function(t,e,n){var i=this.normalize(t),r=i.x,o=i.y;return r>1?r=1:r<-1&&(r=-1),o>1?o=1:o<-1&&(o=-1),[(r+1)/2*e,(1-o)/2*n,i.z]},r.extend(o.prototype,i.prototype)},{events:21,underscore:24}],19:[function(t,e,n){e.exports={full:"0.6.4",major:0,minor:6,dot:4}},{}],20:[function(t,e,n){},{}],21:[function(t,e,n){var i=t("__browserify_process");i.EventEmitter||(i.EventEmitter=function(){});var r=n.EventEmitter=i.EventEmitter,o="function"==typeof Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};r.prototype.setMaxListeners=function(t){this._events||(this._events={}),this._events.maxListeners=t},r.prototype.emit=function(t){if("error"===t&&(!this._events||!this._events.error||o(this._events.error)&&!this._events.error.length))throw arguments[1]instanceof Error?arguments[1]:new Error("Uncaught, unspecified 'error' event.");if(!this._events)return!1;var e=this._events[t];if(!e)return!1;if("function"==typeof e){switch(arguments.length){case 1:e.call(this);break;case 2:e.call(this,arguments[1]);break;case 3:e.call(this,arguments[1],arguments[2]);break;default:var n=Array.prototype.slice.call(arguments,1);e.apply(this,n)}return!0}if(o(e)){n=Array.prototype.slice.call(arguments,1);for(var i=e.slice(),r=0,s=i.length;r0&&this._events[t].length>n&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),console.trace());this._events[t].push(e)}else this._events[t]=[this._events[t],e];else this._events[t]=e;return this},r.prototype.on=r.prototype.addListener,r.prototype.once=function(t,e){var n=this;return n.on(t,function i(){n.removeListener(t,i),e.apply(this,arguments)}),this},r.prototype.removeListener=function(t,e){if("function"!=typeof e)throw new Error("removeListener only takes instances of Function");if(!this._events||!this._events[t])return this;var n=this._events[t];if(o(n)){var i=function(t,e){if(t.indexOf)return t.indexOf(e);for(var n=0;n0&&n.shift()())},!0),function(t){n.push(t),window.postMessage("process-tick","*")}}return function(t){setTimeout(t,0)}}(),i.title="browser",i.browser=!0,i.env={},i.argv=[],i.binding=function(t){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(t){throw new Error("process.chdir is not supported")}},{}],23:[function(t,e,n){var i,r;i=this,r={},void 0===n?"function"==typeof define&&"object"==typeof define.amd&&define.amd?(r.exports={},define(function(){return r.exports})):r.exports="undefined"!=typeof window?window:i:r.exports=n,function(t){var e;if(!n)var n="undefined"!=typeof Float32Array?Float32Array:Array;if(!i)var i=Math.random;var r={setMatrixArrayType:function(t){n=t}};void 0!==t&&(t.glMatrix=r);var o=Math.PI/180;r.toRadian=function(t){return t*o};var s,a={create:function(){var t=new n(2);return t[0]=0,t[1]=0,t},clone:function(t){var e=new n(2);return e[0]=t[0],e[1]=t[1],e},fromValues:function(t,e){var i=new n(2);return i[0]=t,i[1]=e,i},copy:function(t,e){return t[0]=e[0],t[1]=e[1],t},set:function(t,e,n){return t[0]=e,t[1]=n,t},add:function(t,e,n){return t[0]=e[0]+n[0],t[1]=e[1]+n[1],t},subtract:function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t}};a.sub=a.subtract,a.multiply=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t},a.mul=a.multiply,a.divide=function(t,e,n){return t[0]=e[0]/n[0],t[1]=e[1]/n[1],t},a.div=a.divide,a.min=function(t,e,n){return t[0]=Math.min(e[0],n[0]),t[1]=Math.min(e[1],n[1]),t},a.max=function(t,e,n){return t[0]=Math.max(e[0],n[0]),t[1]=Math.max(e[1],n[1]),t},a.scale=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t},a.scaleAndAdd=function(t,e,n,i){return t[0]=e[0]+n[0]*i,t[1]=e[1]+n[1]*i,t},a.distance=function(t,e){var n=e[0]-t[0],i=e[1]-t[1];return Math.sqrt(n*n+i*i)},a.dist=a.distance,a.squaredDistance=function(t,e){var n=e[0]-t[0],i=e[1]-t[1];return n*n+i*i},a.sqrDist=a.squaredDistance,a.length=function(t){var e=t[0],n=t[1];return Math.sqrt(e*e+n*n)},a.len=a.length,a.squaredLength=function(t){var e=t[0],n=t[1];return e*e+n*n},a.sqrLen=a.squaredLength,a.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t},a.normalize=function(t,e){var n=e[0],i=e[1],r=n*n+i*i;return r>0&&(r=1/Math.sqrt(r),t[0]=e[0]*r,t[1]=e[1]*r),t},a.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]},a.cross=function(t,e,n){var i=e[0]*n[1]-e[1]*n[0];return t[0]=t[1]=0,t[2]=i,t},a.lerp=function(t,e,n,i){var r=e[0],o=e[1];return t[0]=r+i*(n[0]-r),t[1]=o+i*(n[1]-o),t},a.random=function(t,e){e=e||1;var n=2*i()*Math.PI;return t[0]=Math.cos(n)*e,t[1]=Math.sin(n)*e,t},a.transformMat2=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[2]*r,t[1]=n[1]*i+n[3]*r,t},a.transformMat2d=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[2]*r+n[4],t[1]=n[1]*i+n[3]*r+n[5],t},a.transformMat3=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[3]*r+n[6],t[1]=n[1]*i+n[4]*r+n[7],t},a.transformMat4=function(t,e,n){var i=e[0],r=e[1];return t[0]=n[0]*i+n[4]*r+n[12],t[1]=n[1]*i+n[5]*r+n[13],t},a.forEach=(s=a.create(),function(t,e,n,i,r,o){var a,c;for(e||(e=2),n||(n=0),c=i?Math.min(i*e+n,t.length):t.length,a=n;a0&&(o=1/Math.sqrt(o),t[0]=e[0]*o,t[1]=e[1]*o,t[2]=e[2]*o),t},c.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},c.cross=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=n[0],a=n[1],c=n[2];return t[0]=r*c-o*a,t[1]=o*s-i*c,t[2]=i*a-r*s,t},c.lerp=function(t,e,n,i){var r=e[0],o=e[1],s=e[2];return t[0]=r+i*(n[0]-r),t[1]=o+i*(n[1]-o),t[2]=s+i*(n[2]-s),t},c.random=function(t,e){e=e||1;var n=2*i()*Math.PI,r=2*i()-1,o=Math.sqrt(1-r*r)*e;return t[0]=Math.cos(n)*o,t[1]=Math.sin(n)*o,t[2]=r*e,t},c.transformMat4=function(t,e,n){var i=e[0],r=e[1],o=e[2];return t[0]=n[0]*i+n[4]*r+n[8]*o+n[12],t[1]=n[1]*i+n[5]*r+n[9]*o+n[13],t[2]=n[2]*i+n[6]*r+n[10]*o+n[14],t},c.transformMat3=function(t,e,n){var i=e[0],r=e[1],o=e[2];return t[0]=i*n[0]+r*n[3]+o*n[6],t[1]=i*n[1]+r*n[4]+o*n[7],t[2]=i*n[2]+r*n[5]+o*n[8],t},c.transformQuat=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=n[0],a=n[1],c=n[2],u=n[3],h=u*i+a*o-c*r,l=u*r+c*i-s*o,p=u*o+s*r-a*i,d=-s*i-a*r-c*o;return t[0]=h*u+d*-s+l*-c-p*-a,t[1]=l*u+d*-a+p*-s-h*-c,t[2]=p*u+d*-c+h*-a-l*-s,t},c.rotateX=function(t,e,n,i){var r=[],o=[];return r[0]=e[0]-n[0],r[1]=e[1]-n[1],r[2]=e[2]-n[2],o[0]=r[0],o[1]=r[1]*Math.cos(i)-r[2]*Math.sin(i),o[2]=r[1]*Math.sin(i)+r[2]*Math.cos(i),t[0]=o[0]+n[0],t[1]=o[1]+n[1],t[2]=o[2]+n[2],t},c.rotateY=function(t,e,n,i){var r=[],o=[];return r[0]=e[0]-n[0],r[1]=e[1]-n[1],r[2]=e[2]-n[2],o[0]=r[2]*Math.sin(i)+r[0]*Math.cos(i),o[1]=r[1],o[2]=r[2]*Math.cos(i)-r[0]*Math.sin(i),t[0]=o[0]+n[0],t[1]=o[1]+n[1],t[2]=o[2]+n[2],t},c.rotateZ=function(t,e,n,i){var r=[],o=[];return r[0]=e[0]-n[0],r[1]=e[1]-n[1],r[2]=e[2]-n[2],o[0]=r[0]*Math.cos(i)-r[1]*Math.sin(i),o[1]=r[0]*Math.sin(i)+r[1]*Math.cos(i),o[2]=r[2],t[0]=o[0]+n[0],t[1]=o[1]+n[1],t[2]=o[2]+n[2],t},c.forEach=function(){var t=c.create();return function(e,n,i,r,o,s){var a,c;for(n||(n=3),i||(i=0),c=r?Math.min(r*n+i,e.length):e.length,a=i;a0&&(s=1/Math.sqrt(s),t[0]=e[0]*s,t[1]=e[1]*s,t[2]=e[2]*s,t[3]=e[3]*s),t},u.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},u.lerp=function(t,e,n,i){var r=e[0],o=e[1],s=e[2],a=e[3];return t[0]=r+i*(n[0]-r),t[1]=o+i*(n[1]-o),t[2]=s+i*(n[2]-s),t[3]=a+i*(n[3]-a),t},u.random=function(t,e){return e=e||1,t[0]=i(),t[1]=i(),t[2]=i(),t[3]=i(),u.normalize(t,t),u.scale(t,t,e),t},u.transformMat4=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=e[3];return t[0]=n[0]*i+n[4]*r+n[8]*o+n[12]*s,t[1]=n[1]*i+n[5]*r+n[9]*o+n[13]*s,t[2]=n[2]*i+n[6]*r+n[10]*o+n[14]*s,t[3]=n[3]*i+n[7]*r+n[11]*o+n[15]*s,t},u.transformQuat=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=n[0],a=n[1],c=n[2],u=n[3],h=u*i+a*o-c*r,l=u*r+c*i-s*o,p=u*o+s*r-a*i,d=-s*i-a*r-c*o;return t[0]=h*u+d*-s+l*-c-p*-a,t[1]=l*u+d*-a+p*-s-h*-c,t[2]=p*u+d*-c+h*-a-l*-s,t},u.forEach=function(){var t=u.create();return function(e,n,i,r,o,s){var a,c;for(n||(n=4),i||(i=0),c=r?Math.min(r*n+i,e.length):e.length,a=i;a.999999?(t[0]=0,t[1]=0,t[2]=0,t[3]=1,t):(c.cross(f,e,n),t[0]=f[0],t[1]=f[1],t[2]=f[2],t[3]=1+i,g.normalize(t,t))}),g.setAxes=(y=p.create(),function(t,e,n,i){return y[0]=n[0],y[3]=n[1],y[6]=n[2],y[1]=i[0],y[4]=i[1],y[7]=i[2],y[2]=-e[0],y[5]=-e[1],y[8]=-e[2],g.normalize(t,g.fromMat3(t,y))}),g.clone=u.clone,g.fromValues=u.fromValues,g.copy=u.copy,g.set=u.set,g.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},g.setAxisAngle=function(t,e,n){n*=.5;var i=Math.sin(n);return t[0]=i*e[0],t[1]=i*e[1],t[2]=i*e[2],t[3]=Math.cos(n),t},g.add=u.add,g.multiply=function(t,e,n){var i=e[0],r=e[1],o=e[2],s=e[3],a=n[0],c=n[1],u=n[2],h=n[3];return t[0]=i*h+s*a+r*u-o*c,t[1]=r*h+s*c+o*a-i*u,t[2]=o*h+s*u+i*c-r*a,t[3]=s*h-i*a-r*c-o*u,t},g.mul=g.multiply,g.scale=u.scale,g.rotateX=function(t,e,n){n*=.5;var i=e[0],r=e[1],o=e[2],s=e[3],a=Math.sin(n),c=Math.cos(n);return t[0]=i*c+s*a,t[1]=r*c+o*a,t[2]=o*c-r*a,t[3]=s*c-i*a,t},g.rotateY=function(t,e,n){n*=.5;var i=e[0],r=e[1],o=e[2],s=e[3],a=Math.sin(n),c=Math.cos(n);return t[0]=i*c-o*a,t[1]=r*c+s*a,t[2]=o*c+i*a,t[3]=s*c-r*a,t},g.rotateZ=function(t,e,n){n*=.5;var i=e[0],r=e[1],o=e[2],s=e[3],a=Math.sin(n),c=Math.cos(n);return t[0]=i*c+r*a,t[1]=r*c-i*a,t[2]=o*c+s*a,t[3]=s*c-o*a,t},g.calculateW=function(t,e){var n=e[0],i=e[1],r=e[2];return t[0]=n,t[1]=i,t[2]=r,t[3]=-Math.sqrt(Math.abs(1-n*n-i*i-r*r)),t},g.dot=u.dot,g.lerp=u.lerp,g.slerp=function(t,e,n,i){var r,o,s,a,c,u=e[0],h=e[1],l=e[2],p=e[3],d=n[0],f=n[1],m=n[2],v=n[3];return(o=u*d+h*f+l*m+p*v)<0&&(o=-o,d=-d,f=-f,m=-m,v=-v),1-o>1e-6?(r=Math.acos(o),s=Math.sin(r),a=Math.sin((1-i)*r)/s,c=Math.sin(i*r)/s):(a=1-i,c=i),t[0]=a*u+c*d,t[1]=a*h+c*f,t[2]=a*l+c*m,t[3]=a*p+c*v,t},g.invert=function(t,e){var n=e[0],i=e[1],r=e[2],o=e[3],s=n*n+i*i+r*r+o*o,a=s?1/s:0;return t[0]=-n*a,t[1]=-i*a,t[2]=-r*a,t[3]=o*a,t},g.conjugate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t},g.length=u.length,g.len=g.length,g.squaredLength=u.squaredLength,g.sqrLen=g.squaredLength,g.normalize=u.normalize,g.fromMat3=function(t,e){var n,i=e[0]+e[4]+e[8];if(i>0)n=Math.sqrt(i+1),t[3]=.5*n,t[0]=(e[7]-e[5])*(n=.5/n),t[1]=(e[2]-e[6])*n,t[2]=(e[3]-e[1])*n;else{var r=0;e[4]>e[0]&&(r=1),e[8]>e[3*r+r]&&(r=2);var o=(r+1)%3,s=(r+2)%3;n=Math.sqrt(e[3*r+r]-e[3*o+o]-e[3*s+s]+1),t[r]=.5*n,t[3]=(e[3*s+o]-e[3*o+s])*(n=.5/n),t[o]=(e[3*o+r]+e[3*r+o])*n,t[s]=(e[3*s+r]+e[3*r+s])*n}return t},g.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},void 0!==t&&(t.quat=g)}(r.exports)},{}],24:[function(t,e,n){(function(){var t=this,i=t._,r={},o=Array.prototype,s=Object.prototype,a=Function.prototype,c=o.push,u=o.slice,h=o.concat,l=s.toString,p=s.hasOwnProperty,d=o.forEach,f=o.map,m=o.reduce,v=o.reduceRight,y=o.filter,g=o.every,w=o.some,b=o.indexOf,M=o.lastIndexOf,x=Array.isArray,E=Object.keys,_=a.bind,S=function(t){return t instanceof S?t:this instanceof S?void(this._wrapped=t):new S(t)};void 0!==n?(void 0!==e&&e.exports&&(n=e.exports=S),n._=S):t._=S,S.VERSION="1.4.4";var P=S.each=S.forEach=function(t,e,n){if(null!=t)if(d&&t.forEach===d)t.forEach(e,n);else if(t.length===+t.length){for(var i=0,o=t.length;i2;if(null==t&&(t=[]),m&&t.reduce===m)return i&&(e=S.bind(e,i)),r?t.reduce(e,n):t.reduce(e);if(P(t,function(t,o,s){r?n=e.call(i,n,t,o,s):(n=t,r=!0)}),!r)throw new TypeError(F);return n},S.reduceRight=S.foldr=function(t,e,n,i){var r=arguments.length>2;if(null==t&&(t=[]),v&&t.reduceRight===v)return i&&(e=S.bind(e,i)),r?t.reduceRight(e,n):t.reduceRight(e);var o=t.length;if(o!==+o){var s=S.keys(t);o=s.length}if(P(t,function(a,c,u){c=s?s[--o]:--o,r?n=e.call(i,n,t[c],c,u):(n=t[c],r=!0)}),!r)throw new TypeError(F);return n},S.find=S.detect=function(t,e,n){var i;return A(t,function(t,r,o){if(e.call(n,t,r,o))return i=t,!0}),i},S.filter=S.select=function(t,e,n){var i=[];return null==t?i:y&&t.filter===y?t.filter(e,n):(P(t,function(t,r,o){e.call(n,t,r,o)&&(i[i.length]=t)}),i)},S.reject=function(t,e,n){return S.filter(t,function(t,i,r){return!e.call(n,t,i,r)},n)},S.every=S.all=function(t,e,n){e||(e=S.identity);var i=!0;return null==t?i:g&&t.every===g?t.every(e,n):(P(t,function(t,o,s){if(!(i=i&&e.call(n,t,o,s)))return r}),!!i)};var A=S.some=S.any=function(t,e,n){e||(e=S.identity);var i=!1;return null==t?i:w&&t.some===w?t.some(e,n):(P(t,function(t,o,s){if(i||(i=e.call(n,t,o,s)))return r}),!!i)};S.contains=S.include=function(t,e){return null!=t&&(b&&t.indexOf===b?-1!=t.indexOf(e):A(t,function(t){return t===e}))},S.invoke=function(t,e){var n=u.call(arguments,2),i=S.isFunction(e);return S.map(t,function(t){return(i?e:t[e]).apply(t,n)})},S.pluck=function(t,e){return S.map(t,function(t){return t[e]})},S.where=function(t,e,n){return S.isEmpty(e)?n?null:[]:S[n?"find":"filter"](t,function(t){for(var n in e)if(e[n]!==t[n])return!1;return!0})},S.findWhere=function(t,e){return S.where(t,e,!0)},S.max=function(t,e,n){if(!e&&S.isArray(t)&&t[0]===+t[0]&&t.length<65535)return Math.max.apply(Math,t);if(!e&&S.isEmpty(t))return-Infinity;var i={computed:-Infinity,value:-Infinity};return P(t,function(t,r,o){var s=e?e.call(n,t,r,o):t;s>=i.computed&&(i={value:t,computed:s})}),i.value},S.min=function(t,e,n){if(!e&&S.isArray(t)&&t[0]===+t[0]&&t.length<65535)return Math.min.apply(Math,t);if(!e&&S.isEmpty(t))return Infinity;var i={computed:Infinity,value:Infinity};return P(t,function(t,r,o){var s=e?e.call(n,t,r,o):t;si||void 0===n)return 1;if(n>>1;n.call(i,t[a])=0})})},S.difference=function(t){var e=h.apply(o,u.call(arguments,1));return S.filter(t,function(t){return!S.contains(e,t)})},S.zip=function(){for(var t=u.call(arguments),e=S.max(S.pluck(t,"length")),n=new Array(e),i=0;i=0;n--)e=[t[n].apply(this,e)];return e[0]}},S.after=function(t,e){return t<=0?e():function(){if(--t<1)return e.apply(this,arguments)}},S.keys=E||function(t){if(t!==Object(t))throw new TypeError("Invalid object");var e=[];for(var n in t)S.has(t,n)&&(e[e.length]=n);return e},S.values=function(t){var e=[];for(var n in t)S.has(t,n)&&e.push(t[n]);return e},S.pairs=function(t){var e=[];for(var n in t)S.has(t,n)&&e.push([n,t[n]]);return e},S.invert=function(t){var e={};for(var n in t)S.has(t,n)&&(e[t[n]]=n);return e},S.functions=S.methods=function(t){var e=[];for(var n in t)S.isFunction(t[n])&&e.push(n);return e.sort()},S.extend=function(t){return P(u.call(arguments,1),function(e){if(e)for(var n in e)t[n]=e[n]}),t},S.pick=function(t){var e={},n=h.apply(o,u.call(arguments,1));return P(n,function(n){n in t&&(e[n]=t[n])}),e},S.omit=function(t){var e={},n=h.apply(o,u.call(arguments,1));for(var i in t)S.contains(n,i)||(e[i]=t[i]);return e},S.defaults=function(t){return P(u.call(arguments,1),function(e){if(e)for(var n in e)null==t[n]&&(t[n]=e[n])}),t},S.clone=function(t){return S.isObject(t)?S.isArray(t)?t.slice():S.extend({},t):t},S.tap=function(t,e){return e(t),t};var k=function(t,e,n,i){if(t===e)return 0!==t||1/t==1/e;if(null==t||null==e)return t===e;t instanceof S&&(t=t._wrapped),e instanceof S&&(e=e._wrapped);var r=l.call(t);if(r!=l.call(e))return!1;switch(r){case"[object String]":return t==String(e);case"[object Number]":return t!=+t?e!=+e:0==t?1/t==1/e:t==+e;case"[object Date]":case"[object Boolean]":return+t==+e;case"[object RegExp]":return t.source==e.source&&t.global==e.global&&t.multiline==e.multiline&&t.ignoreCase==e.ignoreCase}if("object"!=typeof t||"object"!=typeof e)return!1;for(var o=n.length;o--;)if(n[o]==t)return i[o]==e;n.push(t),i.push(e);var s=0,a=!0;if("[object Array]"==r){if(a=(s=t.length)==e.length)for(;s--&&(a=k(t[s],e[s],n,i)););}else{var c=t.constructor,u=e.constructor;if(c!==u&&!(S.isFunction(c)&&c instanceof c&&S.isFunction(u)&&u instanceof u))return!1;for(var h in t)if(S.has(t,h)&&(s++,!(a=S.has(e,h)&&k(t[h],e[h],n,i))))break;if(a){for(h in e)if(S.has(e,h)&&!s--)break;a=!s}}return n.pop(),i.pop(),a};S.isEqual=function(t,e){return k(t,e,[],[])},S.isEmpty=function(t){if(null==t)return!0;if(S.isArray(t)||S.isString(t))return 0===t.length;for(var e in t)if(S.has(t,e))return!1;return!0},S.isElement=function(t){return!(!t||1!==t.nodeType)},S.isArray=x||function(t){return"[object Array]"==l.call(t)},S.isObject=function(t){return t===Object(t)},P(["Arguments","Function","String","Number","Date","RegExp"],function(t){S["is"+t]=function(e){return l.call(e)=="[object "+t+"]"}}),S.isArguments(arguments)||(S.isArguments=function(t){return!(!t||!S.has(t,"callee"))}),S.isFunction=function(t){return"function"==typeof t},S.isFinite=function(t){return isFinite(t)&&!isNaN(parseFloat(t))},S.isNaN=function(t){return S.isNumber(t)&&t!=+t},S.isBoolean=function(t){return!0===t||!1===t||"[object Boolean]"==l.call(t)},S.isNull=function(t){return null===t},S.isUndefined=function(t){return void 0===t},S.has=function(t,e){return p.call(t,e)},S.noConflict=function(){return t._=i,this},S.identity=function(t){return t},S.times=function(t,e,n){for(var i=Array(t),r=0;r":">",'"':""","'":"'","/":"/"}};j.unescape=S.invert(j.escape);var R={escape:new RegExp("["+S.keys(j.escape).join("")+"]","g"),unescape:new RegExp("("+S.keys(j.unescape).join("|")+")","g")};S.each(["escape","unescape"],function(t){S[t]=function(e){return null==e?"":(""+e).replace(R[t],function(e){return j[t][e]})}}),S.result=function(t,e){if(null==t)return null;var n=t[e];return S.isFunction(n)?n.call(t):n},S.mixin=function(t){P(S.functions(t),function(e){var n=S[e]=t[e];S.prototype[e]=function(){var t=[this._wrapped];return c.apply(t,arguments),L.call(this,n.apply(S,t))}})};var D=0;S.uniqueId=function(t){var e=++D+"";return t?t+e:e},S.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var z=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n","\t":"t","\u2028":"u2028","\u2029":"u2029"},I=/\\|'|\r|\n|\t|\u2028|\u2029/g;S.template=function(t,e,n){var i;n=S.defaults({},n,S.templateSettings);var r=new RegExp([(n.escape||z).source,(n.interpolate||z).source,(n.evaluate||z).source].join("|")+"|$","g"),o=0,s="__p+='";t.replace(r,function(e,n,i,r,a){return s+=t.slice(o,a).replace(I,function(t){return"\\"+q[t]}),n&&(s+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),i&&(s+="'+\n((__t=("+i+"))==null?'':__t)+\n'"),r&&(s+="';\n"+r+"\n__p+='"),o=a+e.length,e}),s+="';\n",n.variable||(s="with(obj||{}){\n"+s+"}\n"),s="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+s+"return __p;\n";try{i=new Function(n.variable||"obj","_",s)}catch(t){throw t.source=s,t}if(e)return i(e,S);var a=function(t){return i.call(this,t,S)};return a.source="function("+(n.variable||"obj")+"){\n"+s+"}",a},S.chain=function(t){return S(t).chain()};var L=function(t){return this._chain?S(t).chain():t};S.mixin(S),P(["pop","push","reverse","shift","sort","splice","unshift"],function(t){var e=o[t];S.prototype[t]=function(){var n=this._wrapped;return e.apply(n,arguments),"shift"!=t&&"splice"!=t||0!==n.length||delete n[0],L.call(this,n)}}),P(["concat","join","slice"],function(t){var e=o[t];S.prototype[t]=function(){return L.call(this,e.apply(this._wrapped,arguments))}}),S.extend(S.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this)},{}],25:[function(t,e,n){"undefined"!=typeof window&&"function"!=typeof window.requestAnimationFrame&&(window.requestAnimationFrame=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){setTimeout(t,1e3/60)}),Leap=t("../lib/index")},{"../lib/index":11}]},{},[25]);var t=new THREE.Vector3,e=new THREE.Quaternion;Leap.Controller.plugin("transform",function(t){var e,n,i,r,o,s;return t||(t={}),e=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],s=new THREE.Matrix4,!0===t.vr&&(this.setOptimizeHMD(!0),t.quaternion=(new THREE.Quaternion).setFromRotationMatrix((new THREE.Matrix4).set(-1,0,0,0,0,0,-1,0,0,-1,0,0,0,0,0,1)),t.scale=.001,t.position=new THREE.Vector3(0,0,-.08)),"desktop"===t.vr&&(t.scale=.001),t.getTransform=function(n){var i;return t.matrix?(i="function"==typeof t.matrix?t.matrix(n):t.matrix,window.THREE&&i instanceof THREE.Matrix4?i.elements:i):t.position||t.quaternion||t.scale?(s.set.apply(s,e),t.quaternion&&s.makeRotationFromQuaternion("function"==typeof t.quaternion?t.quaternion(n):t.quaternion),t.position&&s.setPosition("function"==typeof t.position?t.position(n):t.position),s.elements):e},t.getScale=function(e){return isNaN(t.scale)||(t.scale=new THREE.Vector3(t.scale,t.scale,t.scale)),"function"==typeof t.scale?t.scale(e):t.scale},r=function(t,e){var n,i,r,o;for(o=[],i=0,r=e.length;i1&&this.armBones[i].quaternion.multiply(a),this.armMesh.add(this.armBones[i]);for(this.armSpheres=[],i=0;i<=3;i++)this.armSpheres.push(new THREE.Mesh(new THREE.SphereGeometry(n,t.segments,t.segments),this.material.clone())),this.armSpheres[i].material.color.setHex(t.jointColor),this.armSpheres[i].castShadow=!0,this.armSpheres[i].name="ArmSphere"+i,this.armMesh.add(this.armSpheres[i]);return this.object3D.add(this.armMesh),this}},u.prototype.traverse=function(t){for(var e,n=0;n<5;n++)for(var i=0,r=(e=this.fingerMeshes[n]).length;i(this.isHolding?this.data.releaseSensitivity:this.data.holdSensitivity);this.intersector.update(this.data,this.el.object3D,t,e),e&&!this.isHolding&&this.hold(t),!e&&this.isHolding&&this.release(t)}else this.isHolding&&this.release(null);t&&!this.isVisible&&(this.handMesh.show(),this.intersector.show()),!t&&this.isVisible&&(this.handMesh.hide(),this.intersector.hide()),this.isVisible=!!t},getHand:function(){var t=this.data,e=this.system.getFrame();return e.hands.length?e.hands[e.hands[0].type===t.hand?0:1]:null},hold:function(t){var e,n,i=this.getEventDetail(t);this.el.emit("leap-holdstart",i),e=[].slice.call(this.el.sceneEl.querySelectorAll(this.data.holdSelector)).map(function(t){return t.object3D}),n=this.intersector.intersectObjects(e,!0),this.holdTarget=n[0]&&n[0].object&&n[0].object.el,this.holdTarget&&this.holdTarget.emit("leap-holdstart",i),this.isHolding=!0},release:function(t){var e=this.getEventDetail(t);this.el.emit("leap-holdstop",e),this.holdTarget&&(this.holdTarget.emit("leap-holdstop",e),this.holdTarget=null),this.isHolding=!1},getEventDetail:function(t){return{hand:t,handID:this.handID,body:this.handBody?this.handBody.palmBody:null}}});function m(t){var e=0;t=t.array();for(var n=0;n 2 | 3 | 4 | 5 | 6 | Examples • Grabbing 7 | 8 | 9 | 10 | 11 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 54 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | window.Leap = require('leapjs'); 2 | window.AFRAME = require('aframe'); 3 | require('aframe-extras'); 4 | require('aframe-physics-system'); 5 | require('../'); 6 | -------------------------------------------------------------------------------- /examples/leap-hands/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Examples • Leap Hands 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/circular-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Can't seem to install dep from NPM with microbundle. :/ 4 | * 5 | * Simple circular array data structure, for storing a finite-length list of values and 6 | * automatically dropping values that no longer fit in the array. All operations are O(1). 7 | * @param {number} size Maximum number of values to retain. 8 | */ 9 | function CircularArray (size) { 10 | this._index = 0; 11 | this._size = size; 12 | this._array = []; 13 | } 14 | 15 | CircularArray.prototype._incr = function () { this._index = ++this._index % this._size; }; 16 | CircularArray.prototype.array = function () { return this._array; }; 17 | CircularArray.prototype.push = function (value) { 18 | this._array[this._index] = value; 19 | this._incr(); 20 | }; 21 | 22 | export { CircularArray }; 23 | -------------------------------------------------------------------------------- /lib/leap.hand-hold.js: -------------------------------------------------------------------------------- 1 | const HandHold = function() { 2 | var dataFn, interFrameData; 3 | interFrameData = {}; 4 | dataFn = function(prefix, hashOrKey, value) { 5 | var dict, key, _name, _results; 6 | interFrameData[_name = prefix + this.id] || (interFrameData[_name] = []); 7 | dict = interFrameData[prefix + this.id]; 8 | if (value !== void 0) { 9 | return dict[hashOrKey] = value; 10 | } else if ({}.toString.call(hashOrKey) === '[object String]') { 11 | return dict[hashOrKey]; 12 | } else { 13 | _results = []; 14 | for (key in hashOrKey) { 15 | value = hashOrKey[key]; 16 | if (value === void 0) { 17 | _results.push(delete dict[key]); 18 | } else { 19 | _results.push(dict[key] = value); 20 | } 21 | } 22 | return _results; 23 | } 24 | }; 25 | return { 26 | hand: { 27 | data: function(hashOrKey, value) { 28 | return dataFn.call(this, 'h', hashOrKey, value); 29 | }, 30 | hold: function(object) { 31 | if (object) { 32 | return this.data({ 33 | holding: object 34 | }); 35 | } else { 36 | return this.hold(this.hovering()); 37 | } 38 | }, 39 | holding: function() { 40 | return this.data('holding'); 41 | }, 42 | release: function() { 43 | var release; 44 | release = this.data('holding'); 45 | this.data({ 46 | holding: void 0 47 | }); 48 | return release; 49 | }, 50 | hoverFn: function(getHover) { 51 | return this.data({ 52 | getHover: getHover 53 | }); 54 | }, 55 | hovering: function() { 56 | var getHover; 57 | if (getHover = this.data('getHover')) { 58 | return this._hovering || (this._hovering = getHover.call(this)); 59 | } 60 | } 61 | }, 62 | pointable: { 63 | data: function(hashOrKey, value) { 64 | return dataFn.call(this, 'p', hashOrKey, value); 65 | } 66 | } 67 | }; 68 | }; 69 | 70 | export { HandHold }; 71 | -------------------------------------------------------------------------------- /lib/leap.hand-mesh.js: -------------------------------------------------------------------------------- 1 | var DEFAULTS = { 2 | showArm: true, 3 | opacity: 1.0, 4 | segments: 16, 5 | boneScale: 1/6, 6 | boneColor: 0xFFFFFF, 7 | jointScale: 1/5, 8 | jointColor: null 9 | }, 10 | JOINT_COLORS = [0x5DAA00, 0xA00041], 11 | BASE_BONE_ROTATION = (new THREE.Quaternion()) 12 | .setFromEuler(new THREE.Euler(Math.PI / 2, 0, 0)), 13 | ARM_TOP_AND_BOTTOM_ROTATION = (new THREE.Quaternion()) 14 | .setFromEuler(new THREE.Euler(0, 0, Math.PI / 2)); 15 | 16 | var numInstances = 0; 17 | 18 | /** 19 | * Wrapper for a THREE.Mesh instance fitted to a single Leap Motion hand. 20 | * @param {Object} options 21 | */ 22 | function HandMesh(options) { 23 | this.options = options = Object.assign({}, DEFAULTS, options || {}); 24 | this.options.jointColor = this.options.jointColor || JOINT_COLORS[numInstances % 2]; 25 | this.object3D = new THREE.Object3D(); 26 | this.material = !isNaN(options.opacity) ? new THREE.MeshPhongMaterial({ 27 | fog: false, 28 | transparent: true, 29 | opacity: options.opacity 30 | }) : new THREE.MeshPhongMaterial({fog: false}); 31 | 32 | this.createFingers(); 33 | this.createArm(); 34 | numInstances++; 35 | } 36 | 37 | /** @return {Leap.HandMesh} */ 38 | HandMesh.prototype.createFingers = function () { 39 | var mesh, finger, boneCount, 40 | options = this.options, 41 | boneRadius = 40 * options.boneScale, 42 | jointRadius = 40 * options.jointScale; 43 | 44 | this.fingerMeshes = []; 45 | for (var i = 0; i < 5; i++) { 46 | finger = []; 47 | boneCount = i === 0 ? 3 : 4; 48 | for (var j = 0; j < boneCount; j++) { 49 | mesh = new THREE.Mesh( 50 | new THREE.SphereGeometry(jointRadius, options.segments, options.segments), 51 | this.material.clone() 52 | ); 53 | mesh.name = 'hand-bone-' + j; 54 | mesh.material.color.setHex(options.jointColor); 55 | // mesh.renderDepth = ((i * 9) + (2 * j)) / 36; 56 | this.object3D.add(mesh); 57 | finger.push(mesh); 58 | 59 | mesh = new THREE.Mesh( 60 | new THREE.CylinderGeometry(boneRadius, boneRadius, 40, options.segments), 61 | this.material.clone() 62 | ); 63 | mesh.name = 'hand-joint-' + j; 64 | mesh.material.color.setHex(options.boneColor); 65 | // mesh.renderDepth = ((i * 9) + (2 * j) + 1) / 36; 66 | this.object3D.add(mesh); 67 | finger.push(mesh); 68 | } 69 | 70 | mesh = new THREE.Mesh( 71 | new THREE.SphereGeometry(jointRadius, options.segments, options.segments), 72 | this.material.clone() 73 | ); 74 | mesh.material.color.setHex(options.jointColor); 75 | this.object3D.add(mesh); 76 | finger.push(mesh); 77 | this.fingerMeshes.push(finger); 78 | } 79 | return this; 80 | }; 81 | 82 | /** @return {Leap.HandMesh} */ 83 | HandMesh.prototype.createArm = function () { 84 | if (!this.options.showArm) return; 85 | 86 | var options = this.options, 87 | boneRadius = 40 * options.boneScale, 88 | jointRadius = 40 * options.jointScale; 89 | 90 | this.armMesh = new THREE.Object3D(); 91 | this.armBones = []; 92 | this.armSpheres = []; 93 | for (var i = 0; i <= 3; i++) { 94 | this.armBones.push( 95 | new THREE.Mesh( 96 | new THREE.CylinderGeometry( 97 | boneRadius, boneRadius, (i < 2 ? 1000 : 100), options.segments 98 | ), 99 | this.material.clone() 100 | ) 101 | ); 102 | this.armBones[i].material.color.setHex(options.boneColor); 103 | this.armBones[i].castShadow = true; 104 | this.armBones[i].name = 'ArmBone' + i; 105 | if (i > 1) { 106 | this.armBones[i].quaternion.multiply(ARM_TOP_AND_BOTTOM_ROTATION); 107 | } 108 | this.armMesh.add(this.armBones[i]); 109 | } 110 | this.armSpheres = []; 111 | for (i = 0; i <= 3; i++) { 112 | this.armSpheres.push(new THREE.Mesh( 113 | new THREE.SphereGeometry(jointRadius, options.segments, options.segments), 114 | this.material.clone() 115 | )); 116 | this.armSpheres[i].material.color.setHex(options.jointColor); 117 | this.armSpheres[i].castShadow = true; 118 | this.armSpheres[i].name = 'ArmSphere' + i; 119 | this.armMesh.add(this.armSpheres[i]); 120 | } 121 | this.object3D.add(this.armMesh); 122 | return this; 123 | }; 124 | 125 | /** 126 | * @param {function} callback 127 | * @return {Leap.HandMesh} 128 | */ 129 | HandMesh.prototype.traverse = function(callback) { 130 | for (var mesh, ref, i = 0; i < 5; i++) { 131 | ref = this.fingerMeshes[i]; 132 | for (var j = 0, len = ref.length; j < len; j++) { 133 | mesh = ref[j]; 134 | callback(mesh); 135 | } 136 | } 137 | if (this.armMesh) this.armMesh.traverse(callback); 138 | return this; 139 | }; 140 | 141 | /** 142 | * @param {Leap.Hand} hand 143 | * @return {Leap.HandMesh} 144 | */ 145 | HandMesh.prototype.scaleTo = function(hand) { 146 | var armLenScale, armWidthScale, baseScale, bone, boneXOffset, 147 | finger, fingerBoneLengthScale, halfArmLength, i, j, mesh, _i, _j; 148 | 149 | baseScale = hand.middleFinger.proximal.length 150 | / this.fingerMeshes[2][1].geometry.parameters.height; 151 | 152 | for (i = _i = 0; _i < 5; i = ++_i) { 153 | finger = hand.fingers[i]; 154 | j = 0; 155 | while (true) { 156 | if (j === this.fingerMeshes[i].length - 1) { 157 | mesh = this.fingerMeshes[i][j]; 158 | mesh.scale.set(baseScale, baseScale, baseScale); 159 | break; 160 | } 161 | bone = finger.bones[3 - (j / 2)]; 162 | mesh = this.fingerMeshes[i][j]; 163 | mesh.scale.set(baseScale, baseScale, baseScale); 164 | j++; 165 | mesh = this.fingerMeshes[i][j]; 166 | fingerBoneLengthScale = bone.length / mesh.geometry.parameters.height; 167 | mesh.scale.set(baseScale, fingerBoneLengthScale, baseScale); 168 | j++; 169 | } 170 | } 171 | if (this.options.showArm) { 172 | armLenScale = hand.arm.length 173 | / (this.armBones[0].geometry.parameters.height 174 | + this.armBones[0].geometry.parameters.radiusTop); 175 | armWidthScale = hand.arm.width 176 | / (this.armBones[2].geometry.parameters.height 177 | + this.armBones[2].geometry.parameters.radiusTop); 178 | for (i = _j = 0; _j <= 3; i = ++_j) { 179 | this.armBones[i].scale.set(baseScale, (i < 2 ? armLenScale : armWidthScale), baseScale); 180 | this.armSpheres[i].scale.set(baseScale, baseScale, baseScale); 181 | } 182 | boneXOffset = (hand.arm.width / 2) * 0.85; 183 | halfArmLength = hand.arm.length / 2; 184 | this.armBones[0].position.setX(boneXOffset); 185 | this.armBones[1].position.setX(-boneXOffset); 186 | this.armBones[2].position.setY(halfArmLength); 187 | this.armBones[3].position.setY(-halfArmLength); 188 | this.armSpheres[0].position.set(-boneXOffset, halfArmLength, 0); 189 | this.armSpheres[1].position.set(boneXOffset, halfArmLength, 0); 190 | this.armSpheres[2].position.set(boneXOffset, -halfArmLength, 0); 191 | this.armSpheres[3].position.set(-boneXOffset, -halfArmLength, 0); 192 | } 193 | return this; 194 | }; 195 | 196 | /** 197 | * @param {Leap.Hand} hand 198 | * @return {Leap.HandMesh} 199 | */ 200 | HandMesh.prototype.formTo = function(hand) { 201 | var bone, finger, i, j, mesh, _i; 202 | for (i = _i = 0; _i < 5; i = ++_i) { 203 | finger = hand.fingers[i]; 204 | j = 0; 205 | while (true) { 206 | if (j === this.fingerMeshes[i].length - 1) { 207 | mesh = this.fingerMeshes[i][j]; 208 | mesh.position.fromArray(bone.prevJoint); 209 | break; 210 | } 211 | bone = finger.bones[3 - (j / 2)]; 212 | mesh = this.fingerMeshes[i][j]; 213 | mesh.position.fromArray(bone.nextJoint); 214 | ++j; 215 | mesh = this.fingerMeshes[i][j]; 216 | mesh.position.fromArray(bone.center()); 217 | mesh.setRotationFromMatrix((new THREE.Matrix4()).fromArray(bone.matrix())); 218 | mesh.quaternion.multiply(BASE_BONE_ROTATION); 219 | ++j; 220 | } 221 | } 222 | if (this.armMesh) { 223 | this.armMesh.position.fromArray(hand.arm.center()); 224 | this.armMesh.setRotationFromMatrix((new THREE.Matrix4()).fromArray(hand.arm.matrix())); 225 | this.armMesh.quaternion.multiply(BASE_BONE_ROTATION); 226 | } 227 | return this; 228 | }; 229 | 230 | /** 231 | * @param {boolean} visible 232 | * @return {Leap.HandMesh} 233 | */ 234 | HandMesh.prototype.setVisibility = function(visible) { 235 | for (var j, i = 0; i < 5; i++) { 236 | j = 0; 237 | while (true) { 238 | this.fingerMeshes[i][j].visible = visible; 239 | if (++j === this.fingerMeshes[i].length) break; 240 | } 241 | } 242 | if (this.options.showArm) { 243 | for (var k = 0; k <= 3; k++) { 244 | this.armBones[k].visible = visible; 245 | this.armSpheres[k].visible = visible; 246 | } 247 | } 248 | return this; 249 | }; 250 | 251 | /** @return {Leap.HandMesh} */ 252 | HandMesh.prototype.show = function() { 253 | this.setVisibility(true); 254 | return this; 255 | }; 256 | 257 | /** @return {Leap.HandMesh} */ 258 | HandMesh.prototype.hide = function() { 259 | this.setVisibility(false); 260 | return this; 261 | }; 262 | 263 | /** @return {THREE.Object3D} */ 264 | HandMesh.prototype.getMesh = function() { 265 | return this.object3D; 266 | }; 267 | 268 | export { HandMesh }; 269 | -------------------------------------------------------------------------------- /lib/leap.transform.js: -------------------------------------------------------------------------------- 1 | import { Leap } from './leap'; 2 | 3 | const transform = function(scope) { 4 | var noop, transformDirections, transformMat4Implicit0, transformPositions, transformWithMatrices, _directionTransform; 5 | if (!scope) { 6 | scope = {}; 7 | } 8 | noop = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; 9 | _directionTransform = new THREE.Matrix4(); 10 | if (scope.vr === true) { 11 | this.setOptimizeHMD(true); 12 | scope.quaternion = (new THREE.Quaternion()).setFromRotationMatrix((new THREE.Matrix4()).set(-1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 1)); 13 | scope.scale = 0.001; 14 | scope.position = new THREE.Vector3(0, 0, -0.08); 15 | } 16 | if (scope.vr === 'desktop') { 17 | scope.scale = 0.001; 18 | } 19 | scope.getTransform = function(hand) { 20 | var matrix; 21 | if (scope.matrix) { 22 | matrix = typeof scope.matrix === 'function' ? scope.matrix(hand) : scope.matrix; 23 | if (window.THREE && matrix instanceof THREE.Matrix4) { 24 | return matrix.elements; 25 | } else { 26 | return matrix; 27 | } 28 | } else if (scope.position || scope.quaternion || scope.scale) { 29 | _directionTransform.set.apply(_directionTransform, noop); 30 | if (scope.quaternion) { 31 | _directionTransform.makeRotationFromQuaternion(typeof scope.quaternion === 'function' ? scope.quaternion(hand) : scope.quaternion); 32 | } 33 | if (scope.position) { 34 | _directionTransform.setPosition(typeof scope.position === 'function' ? scope.position(hand) : scope.position); 35 | } 36 | return _directionTransform.elements; 37 | } else { 38 | return noop; 39 | } 40 | }; 41 | scope.getScale = function(hand) { 42 | if (!isNaN(scope.scale)) { 43 | scope.scale = new THREE.Vector3(scope.scale, scope.scale, scope.scale); 44 | } 45 | if (typeof scope.scale === 'function') { 46 | return scope.scale(hand); 47 | } else { 48 | return scope.scale; 49 | } 50 | }; 51 | transformPositions = function(matrix, vec3s) { 52 | var vec3, _i, _len, _results; 53 | _results = []; 54 | for (_i = 0, _len = vec3s.length; _i < _len; _i++) { 55 | vec3 = vec3s[_i]; 56 | if (vec3) { 57 | _results.push(Leap.vec3.transformMat4(vec3, vec3, matrix)); 58 | } else { 59 | _results.push(void 0); 60 | } 61 | } 62 | return _results; 63 | }; 64 | transformMat4Implicit0 = function(out, a, m) { 65 | var x, y, z; 66 | x = a[0]; 67 | y = a[1]; 68 | z = a[2]; 69 | out[0] = m[0] * x + m[4] * y + m[8] * z; 70 | out[1] = m[1] * x + m[5] * y + m[9] * z; 71 | out[2] = m[2] * x + m[6] * y + m[10] * z; 72 | return out; 73 | }; 74 | transformDirections = function(matrix, vec3s) { 75 | var vec3, _i, _len, _results; 76 | _results = []; 77 | for (_i = 0, _len = vec3s.length; _i < _len; _i++) { 78 | vec3 = vec3s[_i]; 79 | if (vec3) { 80 | _results.push(transformMat4Implicit0(vec3, vec3, matrix)); 81 | } else { 82 | _results.push(void 0); 83 | } 84 | } 85 | return _results; 86 | }; 87 | transformWithMatrices = function(hand, transform, scale) { 88 | var finger, scalarScale, _i, _j, _len, _len1, _ref, _ref1; 89 | transformDirections(transform, [hand.direction, hand.palmNormal, hand.palmVelocity, hand.arm.basis[0], hand.arm.basis[1], hand.arm.basis[2]]); 90 | _ref = hand.fingers; 91 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 92 | finger = _ref[_i]; 93 | transformDirections(transform, [finger.direction, finger.metacarpal.basis[0], finger.metacarpal.basis[1], finger.metacarpal.basis[2], finger.proximal.basis[0], finger.proximal.basis[1], finger.proximal.basis[2], finger.medial.basis[0], finger.medial.basis[1], finger.medial.basis[2], finger.distal.basis[0], finger.distal.basis[1], finger.distal.basis[2]]); 94 | } 95 | Leap.glMatrix.mat4.scale(transform, transform, scale); 96 | transformPositions(transform, [hand.palmPosition, hand.stabilizedPalmPosition, hand.sphereCenter, hand.arm.nextJoint, hand.arm.prevJoint]); 97 | _ref1 = hand.fingers; 98 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 99 | finger = _ref1[_j]; 100 | transformPositions(transform, [finger.carpPosition, finger.mcpPosition, finger.pipPosition, finger.dipPosition, finger.distal.nextJoint, finger.tipPosition]); 101 | } 102 | scalarScale = (scale[0] + scale[1] + scale[2]) / 3; 103 | return hand.arm.width *= scalarScale; 104 | }; 105 | return { 106 | frame: function(frame) { 107 | var finger, hand, len, _i, _j, _len, _len1, _ref, _ref1, _results; 108 | if (!frame.valid || frame.data.transformed) { 109 | return; 110 | } 111 | frame.data.transformed = true; 112 | _ref = frame.hands; 113 | _results = []; 114 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 115 | hand = _ref[_i]; 116 | transformWithMatrices(hand, scope.getTransform(hand), (scope.getScale(hand) || new THREE.Vector3(1, 1, 1)).toArray()); 117 | if (scope.effectiveParent) { 118 | transformWithMatrices(hand, scope.effectiveParent.matrixWorld.elements, scope.effectiveParent.scale.toArray()); 119 | } 120 | len = null; 121 | _ref1 = hand.fingers; 122 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 123 | finger = _ref1[_j]; 124 | len = Leap.vec3.create(); 125 | Leap.vec3.sub(len, finger.mcpPosition, finger.carpPosition); 126 | finger.metacarpal.length = Leap.vec3.length(len); 127 | Leap.vec3.sub(len, finger.pipPosition, finger.mcpPosition); 128 | finger.proximal.length = Leap.vec3.length(len); 129 | Leap.vec3.sub(len, finger.dipPosition, finger.pipPosition); 130 | finger.medial.length = Leap.vec3.length(len); 131 | Leap.vec3.sub(len, finger.tipPosition, finger.dipPosition); 132 | finger.distal.length = Leap.vec3.length(len); 133 | } 134 | Leap.vec3.sub(len, hand.arm.prevJoint, hand.arm.nextJoint); 135 | _results.push(hand.arm.length = Leap.vec3.length(len)); 136 | } 137 | return _results; 138 | } 139 | }; 140 | }; 141 | 142 | export { transform }; 143 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-leap-hands", 3 | "version": "0.8.0", 4 | "description": "Leap Motion components for A-Frame VR", 5 | "source": "src/index.js", 6 | "main": "dist/aframe-leap-hands.js", 7 | "module": "dist/aframe-leap-hands.module.js", 8 | "scripts": { 9 | "dev": "concurrently \"microbundle watch --target browser\" \"serve --listen 5000\"", 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "dist": "microbundle --target browser", 12 | "version": "npm run dist && git add -A dist", 13 | "postversion": "git push && git push --tags && npm publish" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/donmccurdy/aframe-leap-hands.git" 18 | }, 19 | "keywords": [ 20 | "aframe", 21 | "aframevr", 22 | "leap", 23 | "leapmotion", 24 | "vr", 25 | "hand" 26 | ], 27 | "author": "Don McCurdy ", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/donmccurdy/aframe-leap-hands/issues" 31 | }, 32 | "homepage": "https://github.com/donmccurdy/aframe-leap-hands#readme", 33 | "dependencies": {}, 34 | "devDependencies": { 35 | "concurrently": "^4.0.1", 36 | "microbundle": "^0.6.0", 37 | "serve": "^10.0.1" 38 | }, 39 | "peerDependencies": { 40 | "aframe": "^0.8.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/helpers/hand-body.js: -------------------------------------------------------------------------------- 1 | /** 2 | * CANNON body controller for a single Leap Motion hand. 3 | */ 4 | function HandBody (el, handComponent) { 5 | this.el = el; 6 | this.handComponent = handComponent; 7 | this.system = this.el.sceneEl.systems.leap; 8 | this.physics = this.el.sceneEl.systems.physics; 9 | this.physics.addComponent(this); 10 | 11 | this.palmBody = /** @type {CANNON.Body} */ null; 12 | this.fingerBodies = /** @type {{string: CANNON.Body}} */ {}; 13 | } 14 | 15 | HandBody.prototype.remove = function () { 16 | this.system.removeComponent(this); 17 | for (var id in this.fingerBodies) { 18 | if (this.fingerBodies.hasOwnProperty(id)) { 19 | this.physics.removeBody(this.fingerBodies[id]); 20 | } 21 | } 22 | }; 23 | 24 | HandBody.prototype.beforeStep = function () { 25 | var finger, fingerBody, 26 | hand = this.handComponent.getHand(); 27 | 28 | if (!hand || !hand.valid) return; 29 | 30 | this.syncPalmBody(hand, this.palmBody || this.createPalmBody()); 31 | 32 | for (var i = 0; i < hand.fingers.length; i++) { 33 | finger = hand.fingers[i]; 34 | if (finger.valid) { 35 | fingerBody = this.fingerBodies[finger.type] || this.createFingerBody(finger); 36 | this.syncFingerBody(finger, fingerBody); 37 | } 38 | } 39 | }; 40 | 41 | HandBody.prototype.createFingerBody = function (finger) { 42 | var body = new CANNON.Body({ 43 | shape: new CANNON.Sphere(finger.distal.length / 2), 44 | material: this.physics.material, 45 | mass: 0, 46 | fixedRotation: true 47 | }); 48 | body.el = this.el; 49 | this.physics.addBody(body); 50 | this.fingerBodies[finger.type] = body; 51 | return body; 52 | }; 53 | 54 | HandBody.prototype.syncFingerBody = (function () { 55 | var position = new THREE.Vector3(); 56 | 57 | return function (finger, body) { 58 | this.el.object3D.localToWorld(position.fromArray(finger.distal.center())); 59 | body.position.copy(position); 60 | body.shapes[0].radius = finger.distal.length / 2; 61 | }; 62 | }()); 63 | 64 | HandBody.prototype.createPalmBody = function () { 65 | var body = new CANNON.Body({ 66 | shape: new CANNON.Sphere(0.01), 67 | material: this.physics.material, 68 | mass: 0 69 | }); 70 | body.el = this.el; 71 | this.physics.addBody(body); 72 | this.palmBody = body; 73 | return body; 74 | }; 75 | 76 | /** 77 | * Repositions and rotates the Body instance to match the Leap hand. 78 | * TODO: There are some residual rotation issues here. 79 | * @param {LEAP.Hand} hand 80 | * @param {CANNON.Body} body 81 | */ 82 | HandBody.prototype.syncPalmBody = (function () { 83 | var position = new THREE.Vector3(), 84 | rotation = new THREE.Quaternion(), 85 | hmdRotation = new THREE.Quaternion(), 86 | euler = new THREE.Euler(), 87 | _tmp1 = new THREE.Vector3(), 88 | _tmp2 = new THREE.Vector3(); 89 | 90 | return function (hand, body) { 91 | rotation.setFromEuler(euler.set(hand.pitch(), hand.yaw(), hand.roll())); 92 | this.el.object3D.matrixWorld.decompose(_tmp1, hmdRotation, _tmp2); 93 | body.quaternion.copy(hmdRotation.multiply(rotation)); 94 | 95 | this.el.object3D.localToWorld(position.fromArray(hand.palmPosition)); 96 | body.position.copy(position); 97 | }; 98 | }()); 99 | 100 | export { HandBody }; 101 | -------------------------------------------------------------------------------- /src/helpers/intersector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper for raycasting, which chooses a raycaster direction based on hand position. Also supports 3 | * a debugging mode, in which the ray is visible. 4 | */ 5 | function Intersector () { 6 | this.arrowHelper = this.createArrowHelper(); 7 | this.raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(), 0, 0.2); 8 | } 9 | 10 | Intersector.prototype.update = function (options, object3D, hand, isHolding) { 11 | // Update options. 12 | this.holdDistance = options.holdDistance; 13 | this.debug = options.debug; 14 | 15 | // Update raycaster. 16 | this.raycaster.far = this.holdDistance; 17 | this.raycaster.ray.direction.fromArray(hand.palmNormal); 18 | this.raycaster.ray.direction.x += hand.direction[0] / 2; 19 | this.raycaster.ray.direction.y += hand.direction[1] / 2; 20 | this.raycaster.ray.direction.z += hand.direction[2] / 2; 21 | this.raycaster.ray.direction.normalize(); 22 | this.raycaster.ray.origin.fromArray(hand.palmPosition); 23 | object3D.localToWorld(this.raycaster.ray.origin); 24 | 25 | // Update arrow helper. 26 | if (this.debug) { 27 | this.arrowHelper = this.arrowHelper || this.createArrowHelper(); 28 | this.arrowHelper.position.copy(this.raycaster.ray.origin); 29 | object3D.worldToLocal(this.arrowHelper.position); 30 | this.arrowHelper.setDirection(this.raycaster.ray.direction); 31 | this.arrowHelper.setLength(this.holdDistance); 32 | this.arrowHelper.setColor(isHolding ? 0xFF0000 : 0x00FF00); 33 | } else { 34 | delete this.arrowHelper; 35 | } 36 | }; 37 | 38 | Intersector.prototype.intersectObjects = function (objects, isRecursive) { 39 | return this.raycaster.intersectObjects(objects, isRecursive); 40 | }; 41 | 42 | /** @return {THREE.ArrowHelper} */ 43 | Intersector.prototype.createArrowHelper = function () { 44 | return new THREE.ArrowHelper( 45 | new THREE.Vector3(1, 0, 0), 46 | new THREE.Vector3(), 47 | this.holdDistance 48 | ); 49 | }; 50 | 51 | /** @return {THREE.Object3D} */ 52 | Intersector.prototype.getMesh = function () { 53 | return this.arrowHelper; 54 | }; 55 | 56 | /** @return {Intersector} */ 57 | Intersector.prototype.show = function () { 58 | if (this.arrowHelper) this.arrowHelper.visible = true; 59 | return this; 60 | }; 61 | 62 | /** @return {Intersector} */ 63 | Intersector.prototype.hide = function () { 64 | if (this.arrowHelper) this.arrowHelper.visible = false; 65 | return this; 66 | }; 67 | 68 | export { Intersector }; 69 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { System } from './leap-system'; 2 | import { Component } from './leap-hand'; 3 | 4 | export { System, Component }; 5 | -------------------------------------------------------------------------------- /src/leap-hand.js: -------------------------------------------------------------------------------- 1 | import { HandMesh } from '../lib/leap.hand-mesh'; 2 | import { HandBody } from './helpers/hand-body'; 3 | import { Intersector } from './helpers/intersector'; 4 | import { CircularArray } from '../lib/circular-array'; 5 | 6 | var nextID = 1; 7 | 8 | /** 9 | * A-Frame component for a single Leap Motion hand. 10 | */ 11 | const Component = AFRAME.registerComponent('leap-hand', { 12 | schema: { 13 | hand: {default: '', oneOf: ['left', 'right'], required: true}, 14 | enablePhysics: {default: false}, 15 | holdDistance: {default: 0.2}, // m 16 | holdDebounce: {default: 100}, // ms 17 | holdSelector: {default: '[holdable]'}, 18 | holdSensitivity: {default: 0.95}, // [0,1] 19 | releaseSensitivity: {default: 0.75}, // [0,1] 20 | debug: {default: false} 21 | }, 22 | 23 | init: function () { 24 | this.system = this.el.sceneEl.systems.leap; 25 | 26 | this.handID = nextID++; 27 | this.hand = /** @type {Leap.Hand} */ null; 28 | this.handBody = /** @type {HandBody} */ null; 29 | this.handMesh = new HandMesh(); 30 | 31 | this.isVisible = false; 32 | this.isHolding = false; 33 | 34 | var bufferLen = Math.floor(this.data.holdDebounce / (1000 / 120)); 35 | this.grabStrength = 0; 36 | this.pinchStrength = 0; 37 | this.grabStrengthBuffer = /** @type {CircularArray} */ new CircularArray(bufferLen); 38 | this.pinchStrengthBuffer = /** @type {CircularArray} */ new CircularArray(bufferLen); 39 | 40 | this.intersector = new Intersector(); 41 | this.holdTarget = /** @type {AFRAME.Element} */ null; 42 | 43 | this.el.setObject3D('mesh', this.handMesh.getMesh()); 44 | this.handMesh.hide(); 45 | 46 | if (this.data.debug) { 47 | this.el.object3D.add(this.intersector.getMesh()); 48 | } 49 | }, 50 | 51 | update: function () { 52 | var data = this.data; 53 | if (data.enablePhysics && !this.handBody) { 54 | this.handBody = new HandBody(this.el, this); 55 | } else if (!data.enablePhysics && this.handBody) { 56 | this.handBody.remove(); 57 | this.handBody = null; 58 | } 59 | }, 60 | 61 | remove: function () { 62 | if (this.handMesh) { 63 | this.el.removeObject3D('mesh'); 64 | this.handMesh = null; 65 | } 66 | if (this.handBody) { 67 | this.handBody.remove(); 68 | this.handBody = null; 69 | } 70 | if (this.intersector.getMesh()) { 71 | this.el.object3D.remove(this.intersector.getMesh()); 72 | this.intersector = null; 73 | } 74 | }, 75 | 76 | tick: function () { 77 | var hand = this.getHand(); 78 | 79 | if (hand && hand.valid) { 80 | this.handMesh.scaleTo(hand); 81 | this.handMesh.formTo(hand); 82 | this.grabStrengthBuffer.push(hand.grabStrength); 83 | this.pinchStrengthBuffer.push(hand.pinchStrength); 84 | this.grabStrength = circularArrayAvg(this.grabStrengthBuffer); 85 | this.pinchStrength = circularArrayAvg(this.pinchStrengthBuffer); 86 | var isHolding = Math.max(this.grabStrength, this.pinchStrength) 87 | > (this.isHolding ? this.data.releaseSensitivity : this.data.holdSensitivity); 88 | this.intersector.update(this.data, this.el.object3D, hand, isHolding); 89 | if ( isHolding && !this.isHolding) this.hold(hand); 90 | if (!isHolding && this.isHolding) this.release(hand); 91 | } else if (this.isHolding) { 92 | this.release(null); 93 | } 94 | 95 | if (hand && !this.isVisible) { 96 | this.handMesh.show(); 97 | this.intersector.show(); 98 | } 99 | 100 | if (!hand && this.isVisible) { 101 | this.handMesh.hide(); 102 | this.intersector.hide(); 103 | } 104 | this.isVisible = !!hand; 105 | }, 106 | 107 | getHand: function () { 108 | var data = this.data, 109 | frame = this.system.getFrame(); 110 | return frame.hands.length ? frame.hands[frame.hands[0].type === data.hand ? 0 : 1] : null; 111 | }, 112 | 113 | hold: function (hand) { 114 | var objects, results, 115 | eventDetail = this.getEventDetail(hand); 116 | 117 | this.el.emit('leap-holdstart', eventDetail); 118 | 119 | objects = [].slice.call(this.el.sceneEl.querySelectorAll(this.data.holdSelector)) 120 | .map(function (el) { return el.object3D; }); 121 | results = this.intersector.intersectObjects(objects, true); 122 | this.holdTarget = results[0] && results[0].object && results[0].object.el; 123 | if (this.holdTarget) { 124 | this.holdTarget.emit('leap-holdstart', eventDetail); 125 | } 126 | this.isHolding = true; 127 | }, 128 | 129 | release: function (hand) { 130 | var eventDetail = this.getEventDetail(hand); 131 | 132 | this.el.emit('leap-holdstop', eventDetail); 133 | 134 | if (this.holdTarget) { 135 | this.holdTarget.emit('leap-holdstop', eventDetail); 136 | this.holdTarget = null; 137 | } 138 | this.isHolding = false; 139 | }, 140 | 141 | getEventDetail: function (hand) { 142 | return { 143 | hand: hand, 144 | handID: this.handID, 145 | body: this.handBody ? this.handBody.palmBody : null 146 | }; 147 | } 148 | }); 149 | 150 | function circularArrayAvg (array) { 151 | var avg = 0; 152 | array = array.array(); 153 | for (var i = 0; i < array.length; i++) { 154 | avg += array[i]; 155 | } 156 | return avg / array.length; 157 | } 158 | 159 | export { Component }; 160 | -------------------------------------------------------------------------------- /src/leap-system.js: -------------------------------------------------------------------------------- 1 | import { Leap } from '../lib/leap'; 2 | import { transform } from '../lib/leap.transform.js'; 3 | 4 | // Defaults from leap.transform.js. 5 | var DEFAULT_SCALE = 0.001; 6 | var DEFAULT_POSITION = new THREE.Vector3(); 7 | var DEFAULT_QUATERNION = new THREE.Quaternion(); 8 | 9 | Leap.Controller.plugin('transform', transform); 10 | 11 | /** 12 | * Leap Motion system for A-Frame. 13 | */ 14 | export const System = AFRAME.registerSystem('leap', { 15 | schema: { 16 | vr: {default: true}, 17 | scale: {default: DEFAULT_SCALE}, 18 | position: { 19 | type: 'vec3', 20 | default: { 21 | x: DEFAULT_POSITION.x, 22 | y: DEFAULT_POSITION.y, 23 | z: DEFAULT_POSITION.z, 24 | } 25 | }, 26 | quaternion: { 27 | type: 'vec4', 28 | default: { 29 | x: DEFAULT_QUATERNION.x, 30 | y: DEFAULT_QUATERNION.y, 31 | z: DEFAULT_QUATERNION.z, 32 | w: DEFAULT_QUATERNION.w 33 | } 34 | } 35 | }, 36 | 37 | init: function () { 38 | this.controller = Leap.loop() 39 | .use('transform', this.data); 40 | }, 41 | 42 | getFrame: function () { 43 | return this.controller.frame(); 44 | } 45 | }); 46 | --------------------------------------------------------------------------------