├── Leap.js.erb ├── Library ├── Listener.js ├── HandList.js ├── ScreenList.js ├── AnimLoop.js ├── PointableList.js ├── Screen.js ├── Plane.js ├── Calibrate.js ├── Pointable.js ├── Controller.js ├── Vector.js ├── Matrix.js ├── Frame.js └── Hand.js ├── Rakefile ├── Examples ├── visualizer.html ├── liveDemo.html └── js │ └── utilities.js ├── README.md ├── Leap.min.js └── Leap.js /Leap.js.erb: -------------------------------------------------------------------------------- 1 | var Leap = { APIVersion : "0.7.3" }; 2 | 3 | <%= load_file("./Library/*.js") %> 4 | -------------------------------------------------------------------------------- /Library/Listener.js: -------------------------------------------------------------------------------- 1 | Leap.Listener = function(){ 2 | 3 | this.onConnect = function(controller){}; 4 | this.onDisconnect = function(controller){}; 5 | this.onExit = function(controller){}; 6 | this.onFrame = function(controller){}; 7 | this.onInit = function(controller){}; 8 | }; 9 | -------------------------------------------------------------------------------- /Library/HandList.js: -------------------------------------------------------------------------------- 1 | Leap.HandList = function(){}; 2 | 3 | Leap.HandList.prototype = new Array; 4 | 5 | Leap.HandList.prototype.append = function(other){ 6 | 7 | for(i = 0; i < other.length; i++) this.push(new Leap.Hand(other[i])); 8 | }; 9 | 10 | Leap.HandList.prototype.count = function(){ 11 | 12 | return this.length; 13 | }; 14 | 15 | Leap.HandList.prototype.empty = function(){ 16 | 17 | return this.length > 0; 18 | }; 19 | -------------------------------------------------------------------------------- /Library/ScreenList.js: -------------------------------------------------------------------------------- 1 | Leap.ScreenList = function(){}; 2 | 3 | Leap.ScreenList.prototype = new Array; 4 | 5 | Leap.ScreenList.prototype.count = function(){ 6 | 7 | return this.length; 8 | }; 9 | 10 | Leap.ScreenList.prototype.empty = function(){ 11 | 12 | return this.length > 0; 13 | }; 14 | 15 | Leap.ScreenList.prototype.closestScreenHit = function(pointable){ 16 | 17 | if(this.length < 1) return Leap.Screen.invalid(); 18 | 19 | var closest = this[0]; 20 | var min = closest.intersect(pointable).distance; 21 | 22 | for(var index = 1; index < this.length; index++){ 23 | var distance = this[index].intersect(pointable).distance; 24 | if(distance < min){ 25 | closest = this[index]; 26 | min = distance; 27 | } 28 | } 29 | 30 | return closest; 31 | }; -------------------------------------------------------------------------------- /Library/AnimLoop.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function( callback ){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | Leap.AnimLoop = function(controller, callback){ 13 | 14 | this._controller = controller; 15 | this._callback = callback; 16 | 17 | var me = this; 18 | 19 | this._loop = function(controller){ 20 | window.requestAnimFrame(function(){ me._loop(me._controller); }); 21 | me._callback(controller); 22 | }; 23 | 24 | if(controller.isConnected()) window.requestAnimFrame(function(){ me._loop(me._controller); }); 25 | else{ 26 | this._listener = new Leap.Listener(); 27 | this._listener.onConnect = function(controller){ 28 | me._controller.removeListener(me._listener); 29 | window.requestAnimFrame(function(){ me._loop(me._controller); }); 30 | }; 31 | controller.addListener(this._listener); 32 | } 33 | }; -------------------------------------------------------------------------------- /Library/PointableList.js: -------------------------------------------------------------------------------- 1 | Leap.PointableList = function(){}; 2 | 3 | Leap.PointableList.prototype = new Array; 4 | 5 | Leap.PointableList.prototype.append = function(other){ 6 | for(i=0; i0; 15 | }; 16 | 17 | Leap.FingerList = function(){}; 18 | 19 | Leap.FingerList.prototype = new Array; 20 | 21 | Leap.FingerList.prototype.append = function(other){ 22 | for(i = 0; i < other.length; i++) this.push(new Leap.Finger(other[i])); 23 | }; 24 | 25 | Leap.FingerList.prototype.count = function(){ 26 | return this.length; 27 | }; 28 | 29 | Leap.FingerList.prototype.empty = function(){ 30 | return this.length > 0; 31 | }; 32 | 33 | Leap.ToolList = function(){}; 34 | 35 | Leap.ToolList.prototype = new Array; 36 | 37 | Leap.ToolList.prototype.append = function(other){ 38 | for(i=0; i0; 47 | }; 48 | -------------------------------------------------------------------------------- /Library/Screen.js: -------------------------------------------------------------------------------- 1 | Leap.Screen = function(data){ 2 | 3 | if(data){ 4 | 5 | this._plane = new Leap.Plane(data[0],data[1],data[2]); 6 | this._center = data[0].plus(data[2]).dividedBy(2); 7 | this._origin = data[1].plus(data[1].minus(this._center)); 8 | 9 | var xv = data[2].minus(data[0]); 10 | var yv = data[0].minus(data[1]); 11 | var xscale = 2*xv.magnitude()/window.innerWidth; 12 | var yscale = 4*yv.magnitude()/window.innerHeight; 13 | this._xspan = xv.normalized().dividedBy(xscale); 14 | this._yspan = yv.normalized().dividedBy(yscale); 15 | 16 | this._valid = true; 17 | } 18 | else{ 19 | 20 | this._plane = null; 21 | this._valid = false; 22 | } 23 | }; 24 | 25 | Leap.Screen.prototype = { 26 | 27 | distanceToPoint : function(point){ 28 | return this._plane.pointDistance(point); 29 | }, 30 | 31 | intersect : function(pointable, normalize, clampRatio){ 32 | // TODO: Implement clampRatio 33 | var intersect = this._plane.rayIntersect(pointable.tipPosition(), pointable.direction()); 34 | 35 | if(normalize){ // Normalizes to 2D pixels 36 | var direction = intersect.position.minus(this._origin); 37 | var x = this._xspan.dot(direction); 38 | var y = this._yspan.dot(direction); 39 | intersect.position = new Leap.Vector([x, y, 0]); 40 | } 41 | 42 | return intersect; 43 | }, 44 | 45 | normal : function(){ 46 | return this._plane.normal(); 47 | }, 48 | 49 | isValid : function(){ 50 | return this._valid; 51 | } 52 | }; 53 | 54 | Leap.Screen.invalid = function(){ return new Leap.Screen(); } -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require 'uglifier' 3 | 4 | def load_file(path) 5 | raise "nothing at #{path}" if Dir[path].empty? 6 | files = Dir[path].to_a.sort.map do |f| 7 | File.read(f) 8 | end 9 | files.join("\n") 10 | end 11 | 12 | file 13 | 14 | task :build do 15 | File.open(File.expand_path("./Leap.js", Dir.pwd), "w") { |f| f << ERB.new(File.read("./Leap.js.erb")).result } 16 | File.open(File.expand_path("./Leap.min.js", Dir.pwd), "w") do |f| 17 | filedata = File.read("./Leap.js") 18 | puts "Leap.js length: #{filedata.length} (before uglifier)" 19 | filedata = Uglifier.new.compile(filedata) 20 | puts "Leap.min.js length: #{filedata.length} (after uglifier)" 21 | varhash = Hash.new 22 | filedata.scan(/(_[\w\d]+)/) { |n| varhash[n[0]] = (varhash[n[0]]?varhash[n[0]]:0)+n[0].length } 23 | privatevars = (varhash.sort_by { |n,c| c }).reverse 24 | alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw" 25 | counter = 0 26 | shortname = alphabet[0] 27 | privatevars.each do |e| 28 | if shortname.length <= e[0].length 29 | varhash[e[0]] = shortname 30 | counter += 1 31 | if counter < alphabet.length 32 | shortname = "#{alphabet[counter]}" 33 | else 34 | a = alphabet[counter/alphabet.length-1] 35 | b = alphabet[counter%alphabet.length] 36 | shortname = "#{a}#{b}" 37 | end 38 | else 39 | varhash.delete e[0] 40 | puts "#{e[0]} skipped" 41 | end 42 | end 43 | safevars = (varhash.sort_by { |n,c| n.length }).reverse 44 | safevars.each do |e| 45 | filedata.gsub! /#{e[0]}/,e[1] 46 | puts "#{e[0]} => #{e[1]}" 47 | end 48 | puts "Leap.min.js length: #{filedata.length} (after private variable replacement)" 49 | f << filedata 50 | end 51 | end -------------------------------------------------------------------------------- /Library/Plane.js: -------------------------------------------------------------------------------- 1 | Leap.Plane = function(point1, point2, point3){ 2 | 3 | this._point1 = new Leap.Vector(point1); 4 | this._point2 = new Leap.Vector(point2); 5 | this._point3 = new Leap.Vector(point3); 6 | }; 7 | 8 | Leap.Plane.prototype = { 9 | 10 | normal : function(){ 11 | 12 | var x21 = this._point2.x - this._point1.x; 13 | var y21 = this._point2.y - this._point1.y; 14 | var z21 = this._point2.z - this._point1.z; 15 | 16 | var x31 = this._point3.x - this._point1.x; 17 | var y31 = this._point3.y - this._point1.y; 18 | var z31 = this._point3.z - this._point1.z; 19 | 20 | var x = y21*z31 - y31*z21; 21 | var y = x21*z31 - x31*z21; 22 | var z = x21*y31 - x31*y21; 23 | 24 | if(x==0 && y==0 && z==0) this._normal = null; 25 | else this._normal = new Leap.Vector([x, y, z]); 26 | 27 | this.normal = function(){ return this._normal; }; 28 | return this._normal; 29 | }, 30 | 31 | unitnormal : function(){ 32 | 33 | var normal = this.normal(); 34 | if(n==null) return null; 35 | 36 | this._unitnormal = n.normalized(); 37 | 38 | this.unitnormal = function(){ return this._unitnormal; }; 39 | return this._unitnormal; 40 | }, 41 | 42 | pointIntersect : function(point){ 43 | 44 | var unitnormal = this.unitnormal(); 45 | var distance = unitnormal.dot(this._point1.minus(point)); 46 | var position = unitnormal.multiply(distance).plus(point); 47 | 48 | return {position: position, distance: distance}; 49 | }, 50 | 51 | pointDistance : function(point){ 52 | 53 | var unitnormal = this.unitnormal(); 54 | var distance = unitnormal.dot(this._point1.minus(point)); 55 | 56 | return distance; 57 | }, 58 | 59 | rayIntersect : function(rayPosition, rayDirection){ 60 | 61 | var d = rayDirection.dot(this.normal()); 62 | 63 | if(d == 0) return null; 64 | 65 | var n = this._point1.minus(rayPosition).dot(this.normal()); 66 | var t = n/d; 67 | 68 | //if(t < 0) return null; 69 | 70 | var intersect = rayPosition.plus(rayDirection.multiply(t)); 71 | var distance = t*rayDirection.magnitude(); 72 | 73 | return {position: intersect, distance: distance}; 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /Examples/visualizer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Visualizer - Leap 4 | 7 | 8 | 9 | 66 | 67 | 68 |
69 | 70 | 71 | -------------------------------------------------------------------------------- /Examples/liveDemo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | LeapJS Live Demo:
9 | 10 |
11 |
12 | 13 | 62 | 63 | -------------------------------------------------------------------------------- /Library/Calibrate.js: -------------------------------------------------------------------------------- 1 | Leap.Calibrate = function(controller){ 2 | 3 | this._controller = controller; 4 | this._points = []; 5 | var me = this; 6 | 7 | this._elem = document.createElement("div"); 8 | this._elem.style.cssText = this._pointCSS + this._point1CSS; 9 | this._elem.innerText = "1"; 10 | this._elem.title = "Place finger here, then click.\nMake sure only one finger is visible." 11 | 12 | this._elem.onclick = function(){ me._calibrate1(); }; 13 | 14 | document.body.appendChild(this._elem); 15 | 16 | var me = this; 17 | this._listener = new Leap.Listener(); 18 | this._listener.onFrame = function(controller){ me._fingerCount(controller); }; 19 | this._controller.addListener(this._listener); 20 | }; 21 | 22 | Leap.Calibrate.prototype = { 23 | 24 | _pointCSS : "width: 20px; height: 20px; padding: 10px; margin: -20px; position: fixed; text-align: center; background-color: #c3cccc; color: #ffffff; cursor: pointer; ", 25 | _point1CSS : "left: 25%; top: 50%;", 26 | _point2CSS : "left: 25%; top: 25%;", 27 | _point3CSS : "left: 75%; top: 50%;", 28 | 29 | _calibrate1 : function(){ 30 | var pointables = this._controller.frame().pointables(); 31 | if(pointables.count() == 1){ 32 | var me = this; 33 | this._points[0] = pointables[0].tipPosition(); 34 | this._elem.style.cssText = this._pointCSS + this._point2CSS; 35 | this._elem.innerText = "2"; 36 | this._elem.onclick = function(){ me._calibrate2(); }; 37 | } 38 | }, 39 | 40 | _calibrate2 : function(){ 41 | var pointables = this._controller.frame().pointables(); 42 | if(pointables.count() == 1){ 43 | var me = this; 44 | this._points[1] = pointables[0].tipPosition(); 45 | this._elem.style.cssText = this._pointCSS + this._point3CSS; 46 | this._elem.innerText = "3"; 47 | this._elem.onclick = function(){ me._calibrate3(); }; 48 | } 49 | }, 50 | 51 | _calibrate3 : function(){ 52 | var pointables = this._controller.frame().pointables(); 53 | if(pointables.count() == 1){ 54 | var me = this; 55 | this._points[2] = pointables[0].tipPosition(); 56 | document.body.removeChild(this._elem); 57 | delete this._elem; 58 | 59 | this._controller.removeListener(this._listener); 60 | this.onComplete(new Leap.Screen(this._points)); 61 | } 62 | }, 63 | 64 | _fingerCount : function(controller){ 65 | var count = controller.frame().pointables().count(); 66 | if(count == 0) this._elem.style.backgroundColor = "#c3cccc"; 67 | else if(count == 1) this._elem.style.backgroundColor = "#BCD63C"; 68 | else this._elem.style.backgroundColor = "#FF0000"; 69 | }, 70 | 71 | onComplete : function(screen){} 72 | } -------------------------------------------------------------------------------- /Library/Pointable.js: -------------------------------------------------------------------------------- 1 | Leap.Pointable = function(pointableData, parentHand, obj){ 2 | 3 | if(obj==null) obj = this; 4 | 5 | if(pointableData == null){ 6 | 7 | obj._frame = null; 8 | obj._hand = null; 9 | obj._id = null; 10 | obj._valid = false; 11 | 12 | obj._direction = new Leap.Vector(); 13 | obj._tipPosition = new Leap.Vector(); 14 | obj._tipVelocity = new Leap.Vector(); 15 | 16 | obj._length = null; 17 | obj._width = null; 18 | } 19 | else{ 20 | 21 | obj._frame = (parentHand)?parentHand._frame:null; 22 | obj._hand = parentHand; 23 | obj._id = pointableData.id; 24 | obj._valid = true; 25 | 26 | obj._direction = new Leap.Vector(pointableData.direction); 27 | obj._tipPosition = new Leap.Vector(pointableData.tipPosition); 28 | obj._tipVelocity = new Leap.Vector(pointableData.tipVelocity); 29 | 30 | obj._length = pointableData.length; 31 | obj._width = pointableData.width; 32 | } 33 | }; 34 | 35 | Leap.Pointable.prototype = { 36 | 37 | frame : function(){ 38 | return this._frame; 39 | }, 40 | 41 | hand : function(){ 42 | return this._hand; 43 | }, 44 | 45 | id : function(){ 46 | return this._id; 47 | }, 48 | 49 | direction : function(){ 50 | return this._direction; 51 | }, 52 | 53 | tipPosition : function(){ 54 | return this._tipPosition; 55 | }, 56 | 57 | tipVelocity : function(){ 58 | return this._tipVelocity; 59 | }, 60 | 61 | length : function(){ 62 | return this._length; 63 | }, 64 | 65 | width : function(){ 66 | return this._width; 67 | }, 68 | 69 | toString : function(){ 70 | var val = "{id:"+this._id+",direction:"+this._direction.toString()+","; 71 | val += "tipPosition:"+this._tipPosition.toString()+","; 72 | val += "tipVelocity:"+this._tipVelocity.toString()+","; 73 | val += "length:"+this._length+","; 74 | val += "width:"+this._width+"}"; 75 | return val; 76 | }, 77 | 78 | isValid : function(){ 79 | return this._valid; 80 | } 81 | }; 82 | 83 | Leap.Pointable.invalid = function(){ 84 | return new Leap.Pointable(); 85 | }; 86 | 87 | Leap.Finger = function(fingerData, parentHand){ 88 | 89 | Leap.Pointable(fingerData, parentHand, this); 90 | 91 | this._isFinger = true; 92 | this._isTool = false; 93 | }; 94 | 95 | Leap.Finger.prototype = Leap.Pointable.prototype; 96 | 97 | Leap.Finger.invalid = function(){ 98 | return new Leap.Finger(); 99 | }; 100 | 101 | Leap.Tool = function(toolData, parentHand){ 102 | 103 | Leap.Pointable(toolData, parentHand, this); 104 | 105 | this._isTool = true; 106 | this._isFinger = false; 107 | }; 108 | 109 | Leap.Tool.prototype = Leap.Pointable.prototype; 110 | 111 | Leap.Tool.invalid = function(){ 112 | return new Leap.Tool(); 113 | }; 114 | -------------------------------------------------------------------------------- /Library/Controller.js: -------------------------------------------------------------------------------- 1 | Leap.Controller = function(connection){ 2 | 3 | this._frames = []; 4 | this._listeners = {}; 5 | this._listenerId = 0; 6 | 7 | this._bufferSize = 1024; 8 | this._bufferBegin = 0; 9 | 10 | this._screens = new Leap.ScreenList(); 11 | 12 | for(var index = 0; index < this._bufferSize; index++) this._frames[index] = Leap.Frame.invalid(); 13 | 14 | if ((typeof(WebSocket) == 'undefined') && (typeof(MozWebSocket) != 'undefined')) WebSocket = MozWebSocket; 15 | 16 | if (typeof(WebSocket) != 'undefined'){ 17 | 18 | this._socket = new WebSocket(connection); 19 | this._socket._controller = this; 20 | 21 | this._socket.onmessage = function(event){ 22 | this._controller._versionFrame(event); 23 | }; 24 | 25 | this._socket.onopen = function(event){ 26 | for(index in this._controller._listeners) 27 | this._controller._listeners[index].onConnect(this._controller); 28 | }; 29 | 30 | this._socket.onclose = function(event){ 31 | for(index in this._controller._listeners) 32 | this._controller._listeners[index].onDisconnect(this._controller); 33 | }; 34 | 35 | this._socket.onerror = function(event){ 36 | this.onclose(event); 37 | }; 38 | } 39 | }; 40 | 41 | Leap.Controller.prototype = { 42 | 43 | isConnected : function(){ 44 | return this._socket.connected; 45 | }, 46 | 47 | frame : function(index){ 48 | if(index == null || index == 0) return this._frames[this._bufferBegin]; 49 | if(index > this._bufferSize - 1) return Leap.Frame.invalid(); 50 | 51 | index = this._bufferBegin-index; 52 | if(index < 0) index += this._bufferSize; 53 | return this._frames[index]; 54 | }, 55 | 56 | addListener : function(listener){ 57 | listener._id = this._listenerId++; 58 | this._listeners[listener._id] = listener; 59 | listener.onInit(this); 60 | }, 61 | 62 | removeListener : function(listener){ 63 | listener.onExit(this); 64 | this._listeners[listener._id].onExit(this); 65 | delete this._listeners[listener._id]; 66 | }, 67 | 68 | config : function(){ 69 | // Requires additional data form WebSocket server 70 | }, 71 | 72 | calibratedScreens : function(){ 73 | return this._screens; 74 | }, 75 | 76 | _onmessage : function(event){ 77 | 78 | var eventData = JSON.parse(event.data); 79 | var newFrame = new Leap.Frame(eventData); 80 | 81 | this._bufferBegin++; 82 | if(this._bufferBegin == this._bufferSize) this._bufferBegin = 0; 83 | 84 | delete this._frames[this._bufferBegin]; 85 | this._frames[this._bufferBegin] = newFrame; 86 | 87 | for(index in this._listeners) 88 | this._listeners[index].onFrame(this); 89 | }, 90 | 91 | _versionFrame : function(event){ 92 | Leap.serverVersion = JSON.parse(event.data).version; 93 | this._socket.onmessage = function(event){ this._controller._onmessage(event); }; 94 | } 95 | }; 96 | -------------------------------------------------------------------------------- /Library/Vector.js: -------------------------------------------------------------------------------- 1 | Leap.Vector = function(data){ 2 | 3 | if(data instanceof Leap.Vector){ 4 | this.x = data.x; 5 | this.y = data.y; 6 | this.z = data.z; 7 | } 8 | else if(data != null){ 9 | this.x = (typeof(data[0]) == "number")?data[0]:0; 10 | this.y = (typeof(data[1]) == "number")?data[1]:0; 11 | this.z = (typeof(data[2]) == "number")?data[2]:0; 12 | } 13 | else{ 14 | this.x = 0; 15 | this.y = 0; 16 | this.z = 0; 17 | } 18 | }; 19 | 20 | Leap.Vector.prototype = { 21 | 22 | angleTo : function(other){ 23 | var denom = this.magnitude()*other.magnitude(); 24 | if(denom > 0) return Math.acos(this.dot(other)/denom); 25 | else return 0; 26 | }, 27 | 28 | cross : function(other){ 29 | var x = this.y*other.z - other.y*this.z; 30 | var y = this.x*other.z - other.x*this.z; 31 | var z = this.x*other.y - other.x*this.y; 32 | return new Leap.Vector([x,y,z]); 33 | }, 34 | 35 | distanceTo : function(other){ 36 | return this.minus(other).magnitude(); 37 | }, 38 | 39 | dot : function(other){ 40 | return this.x*other.x + this.y*other.y + this.z*other.z; 41 | }, 42 | 43 | plus : function(other){ 44 | return new Leap.Vector([this.x + other.x,this.y + other.y,this.z + other.z]); 45 | }, 46 | 47 | minus : function(other){ 48 | return new Leap.Vector([this.x - other.x,this.y - other.y,this.z - other.z]); 49 | }, 50 | 51 | multiply : function(scalar){ 52 | return new Leap.Vector([this.x*scalar,this.y*scalar,this.z*scalar]); 53 | }, 54 | 55 | dividedBy : function(scalar){ 56 | return new Leap.Vector([this.x/scalar,this.y/scalar,this.z/scalar]); 57 | }, 58 | 59 | magnitude : function(){ 60 | return Math.sqrt(this.magnitudeSquared()); 61 | }, 62 | 63 | magnitudeSquared : function(){ 64 | return Math.pow(this.x,2) + Math.pow(this.y,2) + Math.pow(this.z,2); 65 | }, 66 | 67 | normalized : function(){ 68 | var magnitude = this.magnitude(); 69 | if(magnitude > 0) return this.dividedBy(magnitude); 70 | else return new Leap.Vector(); 71 | }, 72 | 73 | pitch : function(){ 74 | //var proj = new Leap.Vector([0,this.y,this.z]); 75 | //return Leap.vectors.forward().angleTo(proj); 76 | return Math.atan2(this.y, -this.z); 77 | }, 78 | 79 | roll : function(){ 80 | //var proj = new Leap.Vector([this.x,this.y,0]); 81 | //return Leap.vectors.down().angleTo(proj); 82 | return Math.atan2(this.x, -this.y); 83 | }, 84 | 85 | yaw : function(){ 86 | //var proj = new Leap.Vector([this.x,0,this.z]); 87 | //return Leap.vectors.forward().angleTo(proj); 88 | return Math.atan2(this.x, -this.z); 89 | }, 90 | 91 | toArray : function(){ 92 | return [this.x, this.y, this.z]; 93 | }, 94 | 95 | toString : function(){ 96 | return "{x:"+this.x+",y:"+this.y+",z:"+this.z+"}"; 97 | }, 98 | 99 | compare : function(other){ 100 | return this.x==other.x && this.y==other.y && this.z==other.z; 101 | }, 102 | 103 | isValid : function(){ 104 | return (this.x != NaN && this.x > -Infinity && this.x < Infinity) && 105 | (this.y != NaN && this.y > -Infinity && this.y < Infinity) && 106 | (this.z != NaN && this.z > -Infinity && this.z < Infinity); 107 | } 108 | }; 109 | 110 | Leap.Vector.backward = function(){ return new Leap.Vector([0,0,1]); }; 111 | Leap.Vector.down = function(){ return new Leap.Vector([0,-1,0]); }; 112 | Leap.Vector.forward = function(){ return new Leap.Vector([0,0,-1]); }; 113 | Leap.Vector.left = function(){ return new Leap.Vector([-1,0,0]); }; 114 | Leap.Vector.right = function(){ return new Leap.Vector([1,0,0]); }; 115 | Leap.Vector.up = function(){ return new Leap.Vector([0,1,0]); }; 116 | Leap.Vector.xAxis = function(){ return new Leap.Vector([1,0,0]); }; 117 | Leap.Vector.yAxis = function(){ return new Leap.Vector([0,1,0]); }; 118 | Leap.Vector.zAxis = function(){ return new Leap.Vector([0,0,1]); }; 119 | Leap.Vector.zero = function(){ return new Leap.Vector([0,0,0]); }; 120 | -------------------------------------------------------------------------------- /Library/Matrix.js: -------------------------------------------------------------------------------- 1 | Leap.Matrix = function(data){ 2 | 3 | if(data instanceof Leap.Matrix){ 4 | this.xBasis = new Leap.Vector(data.xBasis); 5 | this.yBasis = new Leap.Vector(data.yBasis); 6 | this.zBasis = new Leap.Vector(data.zBasis); 7 | this.origin = new Leap.Vector(data.origin); 8 | } 9 | else if(data instanceof Array){ 10 | if(data[0] instanceof Leap.Vector && typeof(data[1]) == "number"){ 11 | this.setRotation(data[0],data[1]); 12 | this.origin = new Leap.Vector(data[2]); 13 | } 14 | else{ 15 | this.xBasis = new Leap.Vector(data[0]); 16 | this.yBasis = new Leap.Vector(data[1]); 17 | this.zBasis = new Leap.Vector(data[2]); 18 | this.origin = new Leap.Vector(data[3]); 19 | } 20 | } 21 | else{ 22 | this.xBasis = new Leap.Vector([1,0,0]); 23 | this.yBasis = new Leap.Vector([0,1,0]); 24 | this.zBasis = new Leap.Vector([0,0,1]); 25 | this.origin = new Leap.Vector([0,0,0]); 26 | } 27 | }; 28 | 29 | Leap.Matrix.prototype = { 30 | 31 | setRotation : function(_axis, angle){ 32 | var axis = _axis.normalized(); 33 | var s = Math.sin(angle); 34 | var c = Math.cos(angle); 35 | var C = 1-c; 36 | 37 | this.xBasis = new Leap.Vector([axis.x*axis.x*C + c, axis.x*axis.y*C - axis.z*s, axis.x*axis.z*C + axis.y*s]); 38 | this.yBasis = new Leap.Vector([axis.y*axis.x*C + axis.z*s, axis.y*axis.y*C + c, axis.y*axis.z*C - axis.x*s]); 39 | this.zBasis = new Leap.Vector([axis.z*axis.x*C - axis.y*s, axis.z*axis.y*C + axis.x*s, axis.z*axis.z*C + c]); 40 | }, 41 | 42 | transformPoint : function(data){ 43 | return this.origin.plus(this.transformDirection(data)); 44 | }, 45 | 46 | transformDirection : function(data){ 47 | var x = this.xBasis.multiply(data.x); 48 | var y = this.yBasis.multiply(data.y); 49 | var z = this.zBasis.multiply(data.z); 50 | return x.plus(y).plus(z); 51 | }, 52 | 53 | times : function(other){ 54 | var x = this.transformDirection(other.xBasis); 55 | var y = this.transformDirection(other.yBasis); 56 | var z = this.transformDirection(other.zBasis); 57 | var o = this.transformPoint(other.origin); 58 | return new Leap.Matrix([x,y,z,o]); 59 | }, 60 | 61 | rigidInverse : function(){ 62 | var x = new Leap.Vector([this.xBasis.x, this.yBasis.x, this.zBasis.x]); 63 | var y = new Leap.Vector([this.xBasis.y, this.yBasis.y, this.zBasis.y]); 64 | var z = new Leap.Vector([this.xBasis.z, this.yBasis.z, this.zBasis.z]); 65 | var rotInverse = new Leap.Matrix([x,y,z]); 66 | rotInverse.origin = rotInverse.transformDirection(Leap.Vector.zero().minus(this.origin)); 67 | return rotInverse; 68 | }, 69 | 70 | toArray3x3 : function(output){ 71 | if(output == null) output = []; 72 | else output.length = 0; 73 | output[0] = this.xBasis.x; 74 | output[1] = this.xBasis.y; 75 | output[2] = this.xBasis.z; 76 | output[3] = this.yBasis.x; 77 | output[4] = this.yBasis.y; 78 | output[5] = this.yBasis.z; 79 | output[6] = this.zBasis.x; 80 | output[7] = this.zBasis.y; 81 | output[8] = this.zBasis.z; 82 | return output; 83 | }, 84 | 85 | toArray4x4 : function(output){ 86 | if(output == null) output = []; 87 | else output.length = 0; 88 | output[0] = this.xBasis.x; 89 | output[1] = this.xBasis.y; 90 | output[2] = this.xBasis.z; 91 | output[3] = 0; 92 | output[4] = this.yBasis.x; 93 | output[5] = this.yBasis.y; 94 | output[6] = this.yBasis.z; 95 | output[7] = 0; 96 | output[8] = this.zBasis.x; 97 | output[9] = this.zBasis.y; 98 | output[10] = this.zBasis.z; 99 | output[11] = 0; 100 | output[12] = this.origin.x; 101 | output[13] = this.origin.y; 102 | output[14] = this.origin.z; 103 | output[15] = 1; 104 | return output; 105 | }, 106 | 107 | toString : function(){ 108 | return "{xBasis:"+this.xBasis+",yBasis:"+this.yBasis+ 109 | ",zBasis:"+this.zBasis+",origin:"+this.origin+"}"; 110 | }, 111 | 112 | compare : function(other){ 113 | return this.xBasis.compare(other.xBasis) && 114 | this.yBasis.compare(other.yBasis) && 115 | this.zBasis.compare(other.zBasis) && 116 | this.origin.compare(other.origin); 117 | } 118 | }; 119 | 120 | Leap.Matrix.identity = function(){ return new Leap.Matrix(); }; 121 | -------------------------------------------------------------------------------- /Examples/js/utilities.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function( callback ){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function pad(val, min){ 13 | while(val.toString().lengththis.V-1?Leap.Frame.invalid():(e=this.H-e,e<0&&(e+=this.V),this.c[e])},addListener:function(e){e.I=this.r++,this.F[e.I]=e,e.onInit(this)},removeListener:function(e){e.onExit(this),this.F[e.I].onExit(this),delete this.F[e.I]},config:function(){},calibratedScreens:function(){return this.AF},AC:function(e){var t=JSON.parse(e.data),n=new Leap.Frame(t);this.H++,this.H==this.V&&(this.H=0),delete this.c[this.H],this.c[this.H]=n;for(index in this.F)this.F[index].onFrame(this)},o:function(e){Leap.serverVersion=JSON.parse(e.data).version,this.U.onmessage=function(e){this.B.AC(e)}}},Leap.Frame=function(e){this.N=new Leap.FingerList,this.b=new Leap.ToolList,this.G=new Leap.PointableList,this.j=new Leap.HandList,this.K={},this.L={},this.E={},this.X={};if(e==null)this.I=null,this.f=null,this.C=!1,this.A=new Leap.Matrix,this.Y=null,this.D=new Leap.Vector;else{this.I=e.id,this.f=e.timestamp,this.C=!0,this.A=new Leap.Matrix(e.r),this.Y=e.s,this.D=new Leap.Vector(e.t);for(index in e.hands){var t=new Leap.Hand(e.hands[index],this);this.X[t.I]=t,this.j.push(t)}for(index in e.pointables){var n=this.X[e.pointables[index].handId];if(e.pointables[index].tool){var r=new Leap.Tool(e.pointables[index],n);this.E[r.I]=this.L[r.I]=r,this.G.push(r),this.b.push(r),n&&(n.E[r.I]=n.L[r.I]=r,n.G.push(r),n.b.push(r))}else{var r=new Leap.Finger(e.pointables[index],n);this.E[r.I]=this.K[r.I]=r,this.G.push(r),this.N.push(r),n&&(n.E[r.I]=n.K[r.I]=r,n.G.push(r),n.N.push(r))}}}},Leap.Frame.prototype={id:function(){return this.I},timestamp:function(){return this.f},rotationAngle:function(e,t){if(!this.C||!e.C)return 0;var n=this.rotationMatrix(e),r=(n.xBasis.x+n.yBasis.y+n.zBasis.z-1)*.5,i=Math.acos(r);return i===NaN?0:i},rotationAxis:function(e){if(!this.C||!e.C)return Leap.Vector.zero();var t=this.A.zBasis.y-e.A.yBasis.z,n=this.A.xBasis.z-e.A.zBasis.x,r=this.A.yBasis.x-e.A.xBasis.y,i=new Leap.Vector([t,n,r]);return i.normalize()},rotationMatrix:function(e){if(!this.C||!e.C)return Leap.Matrix.identity();var t=new Leap.Vector([this.A.xBasis.x,this.A.yBasis.x,this.A.zBasis.x]),n=new Leap.Vector([this.A.xBasis.y,this.A.yBasis.y,this.A.zBasis.y]),r=new Leap.Vector([this.A.xBasis.z,this.A.yBasis.z,this.A.zBasis.z]),i=new Leap.Matrix([t,n,r]);return e.A.multiply(i)},scaleFactor:function(e){return!this.C||!e.C?1:Math.exp(this.Y-e.Y)},translation:function(e){if(!this.valid||!e.valid)return Leap.Vector.zero();var t=this.D.x-e.D.x,n=this.D.y-e.D.y,r=this.D.z-e.D.z;return new Leap.Vector([t,n,r])},finger:function(e){return this.K[e]==null?Leap.Finger.invalid():this.N[e]},fingers:function(){return this.N},hand:function(e){return this.X[e]==null?Leap.Hand.invalid():this.X[e]},hands:function(){return this.j},pointable:function(e){return this.E[e]==null?Leap.Pointable.invalid():this.E[e]},pointables:function(){return this.G},tool:function(e){return this.L[e]==null?Leap.Tool.invalid():this.L[e]},tools:function(){return this.b},pointables:function(){return this.G},compare:function(e){return this.I==e.id},toString:function(){var e="{timestamp:"+this.f+",id:"+this.I+",hands:[";for(var t=0;t0},Leap.Listener=function(){this.onConnect=function(e){},this.onDisconnect=function(e){},this.onExit=function(e){},this.onFrame=function(e){},this.onInit=function(e){}},Leap.Matrix=function(e){e instanceof Leap.Matrix?(this.xBasis=new Leap.Vector(e.xBasis),this.yBasis=new Leap.Vector(e.yBasis),this.zBasis=new Leap.Vector(e.zBasis),this.origin=new Leap.Vector(e.origin)):e instanceof Array?e[0]instanceof Leap.Vector&&typeof e[1]=="number"?(this.setRotation(e[0],e[1]),this.origin=new Leap.Vector(e[2])):(this.xBasis=new Leap.Vector(e[0]),this.yBasis=new Leap.Vector(e[1]),this.zBasis=new Leap.Vector(e[2]),this.origin=new Leap.Vector(e[3])):(this.xBasis=new Leap.Vector([1,0,0]),this.yBasis=new Leap.Vector([0,1,0]),this.zBasis=new Leap.Vector([0,0,1]),this.origin=new Leap.Vector([0,0,0]))},Leap.Matrix.prototype={setRotation:function(e,t){var n=e.normalized(),r=Math.sin(t),i=Math.cos(t),s=1-i;this.xBasis=new Leap.Vector([n.x*n.x*s+i,n.x*n.y*s-n.z*r,n.x*n.z*s+n.y*r]),this.yBasis=new Leap.Vector([n.y*n.x*s+n.z*r,n.y*n.y*s+i,n.y*n.z*s-n.x*r]),this.zBasis=new Leap.Vector([n.z*n.x*s-n.y*r,n.z*n.y*s+n.x*r,n.z*n.z*s+i])},transformPoint:function(e){return this.origin.plus(this.transformDirection(e))},transformDirection:function(e){var t=this.xBasis.multiply(e.x),n=this.yBasis.multiply(e.y),r=this.zBasis.multiply(e.z);return t.plus(n).plus(r)},times:function(e){var t=this.transformDirection(e.xBasis),n=this.transformDirection(e.yBasis),r=this.transformDirection(e.zBasis),i=this.transformPoint(e.origin);return new Leap.Matrix([t,n,r,i])},rigidInverse:function(){var e=new Leap.Vector([this.xBasis.x,this.yBasis.x,this.zBasis.x]),t=new Leap.Vector([this.xBasis.y,this.yBasis.y,this.zBasis.y]),n=new Leap.Vector([this.xBasis.z,this.yBasis.z,this.zBasis.z]),r=new Leap.Matrix([e,t,n]);return r.origin=r.transformDirection(Leap.Vector.zero().minus(this.origin)),r},toArray3x3:function(e){return e==null?e=[]:e.length=0,e[0]=this.xBasis.x,e[1]=this.xBasis.y,e[2]=this.xBasis.z,e[3]=this.yBasis.x,e[4]=this.yBasis.y,e[5]=this.yBasis.z,e[6]=this.zBasis.x,e[7]=this.zBasis.y,e[8]=this.zBasis.z,e},toArray4x4:function(e){return e==null?e=[]:e.length=0,e[0]=this.xBasis.x,e[1]=this.xBasis.y,e[2]=this.xBasis.z,e[3]=0,e[4]=this.yBasis.x,e[5]=this.yBasis.y,e[6]=this.yBasis.z,e[7]=0,e[8]=this.zBasis.x,e[9]=this.zBasis.y,e[10]=this.zBasis.z,e[11]=0,e[12]=this.origin.x,e[13]=this.origin.y,e[14]=this.origin.z,e[15]=1,e},toString:function(){return"{xBasis:"+this.xBasis+",yBasis:"+this.yBasis+",zBasis:"+this.zBasis+",origin:"+this.origin+"}"},compare:function(e){return this.xBasis.compare(e.xBasis)&&this.yBasis.compare(e.yBasis)&&this.zBasis.compare(e.zBasis)&&this.origin.compare(e.origin)}},Leap.Matrix.identity=function(){return new Leap.Matrix},Leap.Plane=function(e,t,n){this.P=new Leap.Vector(e),this.m=new Leap.Vector(t),this.n=new Leap.Vector(n)},Leap.Plane.prototype={normal:function(){var e=this.m.x-this.P.x,t=this.m.y-this.P.y,n=this.m.z-this.P.z,r=this.n.x-this.P.x,i=this.n.y-this.P.y,s=this.n.z-this.P.z,o=t*s-i*n,u=e*s-r*n,a=e*i-r*t;return o==0&&u==0&&a==0?this.d=null:this.d=new Leap.Vector([o,u,a]),this.normal=function(){return this.d},this.d},unitnormal:function(){var e=this.normal();return n==null?null:(this.i=n.normalized(),this.unitnormal=function(){return this.i},this.i)},pointIntersect:function(e){var t=this.unitnormal(),n=t.dot(this.P.minus(e)),r=t.multiply(n).plus(e);return{position:r,distance:n}},pointDistance:function(e){var t=this.unitnormal(),n=t.dot(this.P.minus(e));return n},rayIntersect:function(e,t){var n=t.dot(this.normal());if(n==0)return null;var r=this.P.minus(e).dot(this.normal()),i=r/n,s=e.plus(t.multiply(i)),o=i*t.magnitude();return{position:s,distance:o}}},Leap.Pointable=function(e,t,n){n==null&&(n=this),e==null?(n.e=null,n.AG=null,n.I=null,n.C=!1,n.O=new Leap.Vector,n.a=new Leap.Vector,n.Z=new Leap.Vector,n.l=null,n.p=null):(n.e=t?t.e:null,n.AG=t,n.I=e.id,n.C=!0,n.O=new Leap.Vector(e.direction),n.a=new Leap.Vector(e.tipPosition),n.Z=new Leap.Vector(e.tipVelocity),n.l=e.length,n.p=e.width)},Leap.Pointable.prototype={frame:function(){return this.e},hand:function(){return this.AG},id:function(){return this.I},direction:function(){return this.O},tipPosition:function(){return this.a},tipVelocity:function(){return this.Z},length:function(){return this.l},width:function(){return this.p},toString:function(){var e="{id:"+this.I+",direction:"+this.O.toString()+",";return e+="tipPosition:"+this.a.toString()+",",e+="tipVelocity:"+this.Z.toString()+",",e+="length:"+this.l+",",e+="width:"+this.p+"}",e},isValid:function(){return this.C}},Leap.Pointable.invalid=function(){return new Leap.Pointable},Leap.Finger=function(e,t){Leap.Pointable(e,t,this),this.AD=!0,this.AJ=!1},Leap.Finger.prototype=Leap.Pointable.prototype,Leap.Finger.invalid=function(){return new Leap.Finger},Leap.Tool=function(e,t){Leap.Pointable(e,t,this),this.AJ=!0,this.AD=!1},Leap.Tool.prototype=Leap.Pointable.prototype,Leap.Tool.invalid=function(){return new Leap.Tool},Leap.PointableList=function(){},Leap.PointableList.prototype=new Array,Leap.PointableList.prototype.append=function(e){for(i=0;i0},Leap.FingerList=function(){},Leap.FingerList.prototype=new Array,Leap.FingerList.prototype.append=function(e){for(i=0;i0},Leap.ToolList=function(){},Leap.ToolList.prototype=new Array,Leap.ToolList.prototype.append=function(e){for(i=0;i0},Leap.Screen=function(e){if(e){this.k=new Leap.Plane(e[0],e[1],e[2]),this.AI=e[0].plus(e[2]).dividedBy(2),this.AH=e[1].plus(e[1].minus(this.AI));var t=e[2].minus(e[0]),n=e[0].minus(e[1]),r=2*t.magnitude()/window.innerWidth,i=4*n.magnitude()/window.innerHeight;this.AK=t.normalized().dividedBy(r),this.AL=n.normalized().dividedBy(i),this.C=!0}else this.k=null,this.C=!1},Leap.Screen.prototype={distanceToPoint:function(e){return this.k.pointDistance(e)},intersect:function(e,t,n){var r=this.k.rayIntersect(e.tipPosition(),e.direction());if(t){var i=r.position.minus(this.AH),s=this.AK.dot(i),o=this.AL.dot(i);r.position=new Leap.Vector([s,o,0])}return r},normal:function(){return this.k.normal()},isValid:function(){return this.C}},Leap.Screen.invalid=function(){return new Leap.Screen},Leap.ScreenList=function(){},Leap.ScreenList.prototype=new Array,Leap.ScreenList.prototype.count=function(){return this.length},Leap.ScreenList.prototype.empty=function(){return this.length>0},Leap.ScreenList.prototype.closestScreenHit=function(e){if(this.length<1)return Leap.Screen.invalid();var t=this[0],n=t.intersect(e).distance;for(var r=1;r0?Math.acos(this.dot(e)/t):0},cross:function(e){var t=this.y*e.z-e.y*this.z,n=this.x*e.z-e.x*this.z,r=this.x*e.y-e.x*this.y;return new Leap.Vector([t,n,r])},distanceTo:function(e){return this.minus(e).magnitude()},dot:function(e){return this.x*e.x+this.y*e.y+this.z*e.z},plus:function(e){return new Leap.Vector([this.x+e.x,this.y+e.y,this.z+e.z])},minus:function(e){return new Leap.Vector([this.x-e.x,this.y-e.y,this.z-e.z])},multiply:function(e){return new Leap.Vector([this.x*e,this.y*e,this.z*e])},dividedBy:function(e){return new Leap.Vector([this.x/e,this.y/e,this.z/e])},magnitude:function(){return Math.sqrt(this.magnitudeSquared())},magnitudeSquared:function(){return Math.pow(this.x,2)+Math.pow(this.y,2)+Math.pow(this.z,2)},normalized:function(){var e=this.magnitude();return e>0?this.dividedBy(e):new Leap.Vector},pitch:function(){return Math.atan2(this.y,-this.z)},roll:function(){return Math.atan2(this.x,-this.y)},yaw:function(){return Math.atan2(this.x,-this.z)},toArray:function(){return[this.x,this.y,this.z]},toString:function(){return"{x:"+this.x+",y:"+this.y+",z:"+this.z+"}"},compare:function(e){return this.x==e.x&&this.y==e.y&&this.z==e.z},isValid:function(){return this.x!=NaN&&this.x>-Infinity&&this.x-Infinity&&this.y-Infinity&&this.z this._bufferSize - 1) return Leap.Frame.invalid(); 157 | 158 | index = this._bufferBegin-index; 159 | if(index < 0) index += this._bufferSize; 160 | return this._frames[index]; 161 | }, 162 | 163 | addListener : function(listener){ 164 | listener._id = this._listenerId++; 165 | this._listeners[listener._id] = listener; 166 | listener.onInit(this); 167 | }, 168 | 169 | removeListener : function(listener){ 170 | listener.onExit(this); 171 | this._listeners[listener._id].onExit(this); 172 | delete this._listeners[listener._id]; 173 | }, 174 | 175 | config : function(){ 176 | // Requires additional data form WebSocket server 177 | }, 178 | 179 | calibratedScreens : function(){ 180 | return this._screens; 181 | }, 182 | 183 | _onmessage : function(event){ 184 | 185 | var eventData = JSON.parse(event.data); 186 | var newFrame = new Leap.Frame(eventData); 187 | 188 | this._bufferBegin++; 189 | if(this._bufferBegin == this._bufferSize) this._bufferBegin = 0; 190 | 191 | delete this._frames[this._bufferBegin]; 192 | this._frames[this._bufferBegin] = newFrame; 193 | 194 | for(index in this._listeners) 195 | this._listeners[index].onFrame(this); 196 | }, 197 | 198 | _versionFrame : function(event){ 199 | Leap.serverVersion = JSON.parse(event.data).version; 200 | this._socket.onmessage = function(event){ this._controller._onmessage(event); }; 201 | } 202 | }; 203 | 204 | Leap.Frame = function(frameData){ 205 | 206 | this._fingers = new Leap.FingerList(); 207 | this._tools = new Leap.ToolList(); 208 | this._pointables = new Leap.PointableList(); 209 | this._hands = new Leap.HandList(); 210 | 211 | this._fingerTable = {}; 212 | this._toolTable = {}; 213 | this._pointableTable = {}; 214 | this._handTable = {}; 215 | 216 | if(frameData == null){ 217 | this._id = null; 218 | this._timestamp = null; 219 | this._valid = false; 220 | 221 | this._rotation = new Leap.Matrix(); 222 | this._scale = null; 223 | this._translation = new Leap.Vector(); 224 | } 225 | else{ 226 | this._id = frameData.id; 227 | this._timestamp = frameData.timestamp; 228 | this._valid = true; 229 | 230 | this._rotation = new Leap.Matrix(frameData.r); 231 | this._scale = frameData.s; 232 | this._translation = new Leap.Vector(frameData.t); 233 | 234 | for(index in frameData.hands){ 235 | 236 | var newHand = new Leap.Hand(frameData.hands[index],this) 237 | this._handTable[newHand._id] = newHand; 238 | this._hands.push(newHand); 239 | } 240 | 241 | for(index in frameData.pointables){ 242 | var hand = this._handTable[frameData.pointables[index].handId]; 243 | if(frameData.pointables[index].tool){ 244 | var pointable = new Leap.Tool(frameData.pointables[index],hand); 245 | this._pointableTable[pointable._id] = this._toolTable[pointable._id] = pointable; 246 | this._pointables.push(pointable); 247 | this._tools.push(pointable); 248 | if(hand){ 249 | hand._pointableTable[pointable._id] = hand._toolTable[pointable._id] = pointable; 250 | hand._pointables.push(pointable); 251 | hand._tools.push(pointable); 252 | } 253 | } 254 | else{ 255 | var pointable = new Leap.Finger(frameData.pointables[index],hand); 256 | this._pointableTable[pointable._id] = this._fingerTable[pointable._id] = pointable; 257 | this._pointables.push(pointable); 258 | this._fingers.push(pointable); 259 | if(hand){ 260 | hand._pointableTable[pointable._id] = hand._fingerTable[pointable._id] = pointable; 261 | hand._pointables.push(pointable); 262 | hand._fingers.push(pointable); 263 | } 264 | } 265 | } 266 | } 267 | }; 268 | 269 | Leap.Frame.prototype = { 270 | 271 | id : function(){ 272 | return this._id; 273 | }, 274 | 275 | timestamp : function(){ 276 | return this._timestamp; 277 | }, 278 | 279 | rotationAngle : function(sinceFrame, axis){ 280 | // TODO: implement axis parameter 281 | if (!this._valid || !sinceFrame._valid) return 0.0; 282 | var rot = this.rotationMatrix(sinceFrame); 283 | var cs = (rot.xBasis.x + rot.yBasis.y + rot.zBasis.z - 1.0)*0.5 284 | var angle = Math.acos(cs); 285 | return angle === NaN ? 0.0 : angle; 286 | }, 287 | 288 | rotationAxis : function(sinceFrame){ 289 | if (!this._valid || !sinceFrame._valid) return Leap.Vector.zero(); 290 | var x = this._rotation.zBasis.y - sinceFrame._rotation.yBasis.z; 291 | var y = this._rotation.xBasis.z - sinceFrame._rotation.zBasis.x; 292 | var z = this._rotation.yBasis.x - sinceFrame._rotation.xBasis.y; 293 | var vec = new Leap.Vector([x, y, z]); 294 | return vec.normalize(); 295 | }, 296 | 297 | rotationMatrix : function(sinceFrame){ 298 | if (!this._valid || !sinceFrame._valid) return Leap.Matrix.identity(); 299 | var xBasis = new Leap.Vector([this._rotation.xBasis.x, this._rotation.yBasis.x, this._rotation.zBasis.x]); 300 | var yBasis = new Leap.Vector([this._rotation.xBasis.y, this._rotation.yBasis.y, this._rotation.zBasis.y]); 301 | var zBasis = new Leap.Vector([this._rotation.xBasis.z, this._rotation.yBasis.z, this._rotation.zBasis.z]); 302 | var transpose = new Leap.Matrix([xBasis, yBasis, zBasis]); 303 | return sinceFrame._rotation.multiply(transpose); 304 | }, 305 | 306 | scaleFactor : function(sinceFrame){ 307 | if (!this._valid || !sinceFrame._valid) return 1.0; 308 | return Math.exp(this._scale - sinceFrame._scale); 309 | }, 310 | 311 | translation : function(sinceFrame){ 312 | if (!this.valid || !sinceFrame.valid) return Leap.Vector.zero(); 313 | var x = this._translation.x - sinceFrame._translation.x; 314 | var y = this._translation.y - sinceFrame._translation.y; 315 | var z = this._translation.z - sinceFrame._translation.z; 316 | return new Leap.Vector([x, y, z]); 317 | }, 318 | 319 | finger : function(id){ 320 | if(this._fingerTable[id]==null) return Leap.Finger.invalid(); 321 | return this._fingers[id]; 322 | }, 323 | 324 | fingers : function(){ 325 | return this._fingers; 326 | }, 327 | 328 | hand : function(id){ 329 | if(this._handTable[id]==null) return Leap.Hand.invalid(); 330 | return this._handTable[id]; 331 | }, 332 | 333 | hands : function(){ 334 | return this._hands; 335 | }, 336 | 337 | pointable : function(id){ 338 | if(this._pointableTable[id]==null) return Leap.Pointable.invalid(); 339 | return this._pointableTable[id]; 340 | }, 341 | 342 | pointables : function(){ 343 | return this._pointables; 344 | }, 345 | 346 | tool : function(id){ 347 | if(this._toolTable[id]==null) return Leap.Tool.invalid(); 348 | return this._toolTable[id]; 349 | }, 350 | 351 | tools : function(){ 352 | return this._tools; 353 | }, 354 | 355 | pointables : function(){ 356 | return this._pointables; 357 | }, 358 | 359 | compare : function(other){ 360 | return this._id==other.id; 361 | }, 362 | 363 | toString : function(){ 364 | var val = "{timestamp:"+this._timestamp+",id:"+this._id+",hands:["; 365 | for(var i=0; i < this._hands.length; i++) val += this._hands[i].toString(); 366 | val += "]}"; 367 | return val; 368 | }, 369 | 370 | isValid : function(){ return this._valid; } 371 | }; 372 | 373 | Leap.Frame.invalid = function(){ 374 | return new Leap.Frame(); 375 | }; 376 | 377 | Leap.Hand = function(handData, parentFrame){ 378 | 379 | this._fingers = new Leap.FingerList(); 380 | this._tools = new Leap.ToolList(); 381 | this._pointables = new Leap.PointableList(); 382 | 383 | this._fingerTable = {}; 384 | this._toolTable = {}; 385 | this._pointableTable = {}; 386 | 387 | if(handData == null){ 388 | 389 | this._frame = null; 390 | this._id = null; 391 | this._valid = false; 392 | 393 | this._rotation = new Leap.Matrix(); 394 | this._scale = null; 395 | this._translation = new Leap.Vector(); 396 | 397 | this._direction = new Leap.Vector(); 398 | this._palmNormal = new Leap.Vector(); 399 | this._palmPosition = new Leap.Vector(); 400 | this._palmVelocity = new Leap.Vector(); 401 | this._sphereCenter = new Leap.Vector(); 402 | this._sphereRadius = null; 403 | } 404 | else{ 405 | 406 | this._frame = parentFrame; 407 | this._id = handData.id; 408 | this._valid = true; 409 | 410 | this._rotation = new Leap.Matrix(handData.r); 411 | this._scale = handData.s; 412 | this._translation = new Leap.Vector(handData.t); 413 | 414 | this._direction = new Leap.Vector(handData.direction); 415 | this._palmNormal = new Leap.Vector(handData.palmNormal); 416 | this._palmPosition = new Leap.Vector(handData.palmPosition); 417 | this._palmVelocity = new Leap.Vector(handData.palmVelocity); 418 | this._sphereCenter = new Leap.Vector(handData.sphereCenter); 419 | this._sphereRadius = handData.sphereRadius; 420 | } 421 | }; 422 | 423 | Leap.Hand.prototype = { 424 | 425 | frame : function(){ 426 | return this._frame; 427 | }, 428 | 429 | id : function(){ 430 | return this._id; 431 | }, 432 | 433 | direction : function(){ 434 | return this._direction; 435 | }, 436 | 437 | palmNormal : function(){ 438 | return this._palmNormal; 439 | }, 440 | 441 | palmPosition : function(){ 442 | return this._palmPosition; 443 | }, 444 | 445 | palmVelocity : function(){ 446 | return this._palmVelocity; 447 | }, 448 | 449 | sphereCenter : function(){ 450 | return this._sphereCenter; 451 | }, 452 | 453 | sphereRadius : function(){ 454 | return this._sphereRadius; 455 | }, 456 | 457 | rotationAngle : function(sinceFrame, axis){ 458 | // TODO: implement axis parameter 459 | if (!this._valid || !sinceFrame._valid) return 0.0; 460 | var sinceHand = sinceFrame.hand(this._id); 461 | if(!sinceHand._valid) return 0.0; 462 | 463 | var rot = this.rotationMatrix(sinceFrame); 464 | var cs = (rot.xBasis.x + rot.yBasis.y + rot.zBasis.z - 1.0)*0.5 465 | var angle = Math.acos(cs); 466 | return angle === NaN ? 0.0 : angle; 467 | }, 468 | 469 | rotationAxis : function(sinceFrame){ 470 | if (!this._valid || !sinceFrame._valid) return Leap.Vector.zero(); 471 | var sinceHand = sinceFrame.hand(this._id); 472 | if(!sinceHand._valid) return Leap.Vector.zero(); 473 | 474 | var x = this._rotation.zBasis.y - sinceHand._rotation.yBasis.z; 475 | var y = this._rotation.xBasis.z - sinceHand._rotation.zBasis.x; 476 | var z = this._rotation.yBasis.x - sinceHand._rotation.xBasis.y; 477 | var vec = new Leap.Vector([x, y, z]); 478 | return vec.normalize(); 479 | }, 480 | 481 | rotationMatrix : function(sinceFrame){ 482 | if (!this._valid || !sinceFrame._valid) return Leap.Matrix.identity(); 483 | var sinceHand = sinceFrame.hand(this._id); 484 | if(!sinceHand._valid) return Leap.Matrix.identity(); 485 | 486 | var xBasis = new Leap.Vector([this._rotation.xBasis.x, this._rotation.yBasis.x, this._rotation.zBasis.x]); 487 | var yBasis = new Leap.Vector([this._rotation.xBasis.y, this._rotation.yBasis.y, this._rotation.zBasis.y]); 488 | var zBasis = new Leap.Vector([this._rotation.xBasis.z, this._rotation.yBasis.z, this._rotation.zBasis.z]); 489 | var transpose = new Leap.Matrix([xBasis, yBasis, zBasis]); 490 | return sinceHand._rotation.multiply(transpose); 491 | }, 492 | 493 | scaleFactor : function(sinceFrame){ 494 | if (!this._valid || !sinceFrame._valid) return 1.0; 495 | var sinceHand = sinceFrame.hand(this._id); 496 | if(!sinceHand._valid) return 1.0; 497 | 498 | return Math.exp(this._scale - sinceHand._scale); 499 | }, 500 | 501 | translation : function(sinceFrame){ 502 | if (!this.valid || !sinceFrame.valid) return Leap.Vector.zero(); 503 | var sinceHand = sinceFrame.hand(this._id); 504 | if(!sinceHand._valid) return Leap.Vector.zero(); 505 | 506 | var x = this._translation.x - sinceHand._translation.x; 507 | var y = this._translation.y - sinceHand._translation.y; 508 | var z = this._translation.z - sinceHand._translation.z; 509 | return new Leap.Vector([x, y, z]); 510 | }, 511 | 512 | finger : function(id){ 513 | if(this._fingerTable[id]==null) return Leap.Finger.invalid(); 514 | return this._fingerTable[id]; 515 | }, 516 | 517 | fingers : function(){ 518 | return this._fingers; 519 | }, 520 | 521 | pointable : function(id){ 522 | if(this._pointableTable[id]==null) return Leap.Pointable.invalid(); 523 | return this._pointableTable[id]; 524 | }, 525 | 526 | pointables : function(){ 527 | return this._pointables; 528 | }, 529 | 530 | tool : function(id){ 531 | if(this._toolTable[id]==null) return {isValid:false}; 532 | return this._toolTable[id]; 533 | }, 534 | 535 | tools : function(){ 536 | return this._tools; 537 | }, 538 | 539 | toString : function(){ 540 | var val = "{id:"+obj._id+",sphereCenter:"+(obj._sphereCenter==null?"null":obj._sphereCenter)+","; 541 | val += "sphereRadius:"+(obj._sphereRadius==null?"null":obj._sphereRadius)+","; 542 | val += "normal:"+(obj._normal==undefined?"null":obj._normal.toString())+",fingers:["; 543 | for(var i=0; i < this._fingers.length; i++) val += this._fingers[i].toString(); 544 | val += "],tools:["; 545 | for(var i=0; i < this._tools.length; i++) val += this._tools[i].toString(); 546 | val += "],palmNormal:"+(obj._palmNormal==undefined?"null":obj._palmNormal.toString())+","; 547 | val += "palmPosition:"+(obj._palmPosition==undefined?"null":obj._palmPosition.toString())+","; 548 | val += "palmVelocity:"+(obj._palmVelocity==undefined?"null":obj._palmVelocity.toString())+"}"; 549 | return val; 550 | }, 551 | 552 | isValid : function(){ 553 | return this._valid; 554 | } 555 | }; 556 | 557 | Leap.Hand.invalid = function(){ 558 | return new Leap.Hand(); 559 | }; 560 | 561 | Leap.HandList = function(){}; 562 | 563 | Leap.HandList.prototype = new Array; 564 | 565 | Leap.HandList.prototype.append = function(other){ 566 | 567 | for(i = 0; i < other.length; i++) this.push(new Leap.Hand(other[i])); 568 | }; 569 | 570 | Leap.HandList.prototype.count = function(){ 571 | 572 | return this.length; 573 | }; 574 | 575 | Leap.HandList.prototype.empty = function(){ 576 | 577 | return this.length > 0; 578 | }; 579 | 580 | Leap.Listener = function(){ 581 | 582 | this.onConnect = function(controller){}; 583 | this.onDisconnect = function(controller){}; 584 | this.onExit = function(controller){}; 585 | this.onFrame = function(controller){}; 586 | this.onInit = function(controller){}; 587 | }; 588 | 589 | Leap.Matrix = function(data){ 590 | 591 | if(data instanceof Leap.Matrix){ 592 | this.xBasis = new Leap.Vector(data.xBasis); 593 | this.yBasis = new Leap.Vector(data.yBasis); 594 | this.zBasis = new Leap.Vector(data.zBasis); 595 | this.origin = new Leap.Vector(data.origin); 596 | } 597 | else if(data instanceof Array){ 598 | if(data[0] instanceof Leap.Vector && typeof(data[1]) == "number"){ 599 | this.setRotation(data[0],data[1]); 600 | this.origin = new Leap.Vector(data[2]); 601 | } 602 | else{ 603 | this.xBasis = new Leap.Vector(data[0]); 604 | this.yBasis = new Leap.Vector(data[1]); 605 | this.zBasis = new Leap.Vector(data[2]); 606 | this.origin = new Leap.Vector(data[3]); 607 | } 608 | } 609 | else{ 610 | this.xBasis = new Leap.Vector([1,0,0]); 611 | this.yBasis = new Leap.Vector([0,1,0]); 612 | this.zBasis = new Leap.Vector([0,0,1]); 613 | this.origin = new Leap.Vector([0,0,0]); 614 | } 615 | }; 616 | 617 | Leap.Matrix.prototype = { 618 | 619 | setRotation : function(_axis, angle){ 620 | var axis = _axis.normalized(); 621 | var s = Math.sin(angle); 622 | var c = Math.cos(angle); 623 | var C = 1-c; 624 | 625 | this.xBasis = new Leap.Vector([axis.x*axis.x*C + c, axis.x*axis.y*C - axis.z*s, axis.x*axis.z*C + axis.y*s]); 626 | this.yBasis = new Leap.Vector([axis.y*axis.x*C + axis.z*s, axis.y*axis.y*C + c, axis.y*axis.z*C - axis.x*s]); 627 | this.zBasis = new Leap.Vector([axis.z*axis.x*C - axis.y*s, axis.z*axis.y*C + axis.x*s, axis.z*axis.z*C + c]); 628 | }, 629 | 630 | transformPoint : function(data){ 631 | return this.origin.plus(this.transformDirection(data)); 632 | }, 633 | 634 | transformDirection : function(data){ 635 | var x = this.xBasis.multiply(data.x); 636 | var y = this.yBasis.multiply(data.y); 637 | var z = this.zBasis.multiply(data.z); 638 | return x.plus(y).plus(z); 639 | }, 640 | 641 | times : function(other){ 642 | var x = this.transformDirection(other.xBasis); 643 | var y = this.transformDirection(other.yBasis); 644 | var z = this.transformDirection(other.zBasis); 645 | var o = this.transformPoint(other.origin); 646 | return new Leap.Matrix([x,y,z,o]); 647 | }, 648 | 649 | rigidInverse : function(){ 650 | var x = new Leap.Vector([this.xBasis.x, this.yBasis.x, this.zBasis.x]); 651 | var y = new Leap.Vector([this.xBasis.y, this.yBasis.y, this.zBasis.y]); 652 | var z = new Leap.Vector([this.xBasis.z, this.yBasis.z, this.zBasis.z]); 653 | var rotInverse = new Leap.Matrix([x,y,z]); 654 | rotInverse.origin = rotInverse.transformDirection(Leap.Vector.zero().minus(this.origin)); 655 | return rotInverse; 656 | }, 657 | 658 | toArray3x3 : function(output){ 659 | if(output == null) output = []; 660 | else output.length = 0; 661 | output[0] = this.xBasis.x; 662 | output[1] = this.xBasis.y; 663 | output[2] = this.xBasis.z; 664 | output[3] = this.yBasis.x; 665 | output[4] = this.yBasis.y; 666 | output[5] = this.yBasis.z; 667 | output[6] = this.zBasis.x; 668 | output[7] = this.zBasis.y; 669 | output[8] = this.zBasis.z; 670 | return output; 671 | }, 672 | 673 | toArray4x4 : function(output){ 674 | if(output == null) output = []; 675 | else output.length = 0; 676 | output[0] = this.xBasis.x; 677 | output[1] = this.xBasis.y; 678 | output[2] = this.xBasis.z; 679 | output[3] = 0; 680 | output[4] = this.yBasis.x; 681 | output[5] = this.yBasis.y; 682 | output[6] = this.yBasis.z; 683 | output[7] = 0; 684 | output[8] = this.zBasis.x; 685 | output[9] = this.zBasis.y; 686 | output[10] = this.zBasis.z; 687 | output[11] = 0; 688 | output[12] = this.origin.x; 689 | output[13] = this.origin.y; 690 | output[14] = this.origin.z; 691 | output[15] = 1; 692 | return output; 693 | }, 694 | 695 | toString : function(){ 696 | return "{xBasis:"+this.xBasis+",yBasis:"+this.yBasis+ 697 | ",zBasis:"+this.zBasis+",origin:"+this.origin+"}"; 698 | }, 699 | 700 | compare : function(other){ 701 | return this.xBasis.compare(other.xBasis) && 702 | this.yBasis.compare(other.yBasis) && 703 | this.zBasis.compare(other.zBasis) && 704 | this.origin.compare(other.origin); 705 | } 706 | }; 707 | 708 | Leap.Matrix.identity = function(){ return new Leap.Matrix(); }; 709 | 710 | Leap.Plane = function(point1, point2, point3){ 711 | 712 | this._point1 = new Leap.Vector(point1); 713 | this._point2 = new Leap.Vector(point2); 714 | this._point3 = new Leap.Vector(point3); 715 | }; 716 | 717 | Leap.Plane.prototype = { 718 | 719 | normal : function(){ 720 | 721 | var x21 = this._point2.x - this._point1.x; 722 | var y21 = this._point2.y - this._point1.y; 723 | var z21 = this._point2.z - this._point1.z; 724 | 725 | var x31 = this._point3.x - this._point1.x; 726 | var y31 = this._point3.y - this._point1.y; 727 | var z31 = this._point3.z - this._point1.z; 728 | 729 | var x = y21*z31 - y31*z21; 730 | var y = x21*z31 - x31*z21; 731 | var z = x21*y31 - x31*y21; 732 | 733 | if(x==0 && y==0 && z==0) this._normal = null; 734 | else this._normal = new Leap.Vector([x, y, z]); 735 | 736 | this.normal = function(){ return this._normal; }; 737 | return this._normal; 738 | }, 739 | 740 | unitnormal : function(){ 741 | 742 | var normal = this.normal(); 743 | if(n==null) return null; 744 | 745 | this._unitnormal = n.normalized(); 746 | 747 | this.unitnormal = function(){ return this._unitnormal; }; 748 | return this._unitnormal; 749 | }, 750 | 751 | pointIntersect : function(point){ 752 | 753 | var unitnormal = this.unitnormal(); 754 | var distance = unitnormal.dot(this._point1.minus(point)); 755 | var position = unitnormal.multiply(distance).plus(point); 756 | 757 | return {position: position, distance: distance}; 758 | }, 759 | 760 | pointDistance : function(point){ 761 | 762 | var unitnormal = this.unitnormal(); 763 | var distance = unitnormal.dot(this._point1.minus(point)); 764 | 765 | return distance; 766 | }, 767 | 768 | rayIntersect : function(rayPosition, rayDirection){ 769 | 770 | var d = rayDirection.dot(this.normal()); 771 | 772 | if(d == 0) return null; 773 | 774 | var n = this._point1.minus(rayPosition).dot(this.normal()); 775 | var t = n/d; 776 | 777 | //if(t < 0) return null; 778 | 779 | var intersect = rayPosition.plus(rayDirection.multiply(t)); 780 | var distance = t*rayDirection.magnitude(); 781 | 782 | return {position: intersect, distance: distance}; 783 | } 784 | }; 785 | 786 | Leap.Pointable = function(pointableData, parentHand, obj){ 787 | 788 | if(obj==null) obj = this; 789 | 790 | if(pointableData == null){ 791 | 792 | obj._frame = null; 793 | obj._hand = null; 794 | obj._id = null; 795 | obj._valid = false; 796 | 797 | obj._direction = new Leap.Vector(); 798 | obj._tipPosition = new Leap.Vector(); 799 | obj._tipVelocity = new Leap.Vector(); 800 | 801 | obj._length = null; 802 | obj._width = null; 803 | } 804 | else{ 805 | 806 | obj._frame = (parentHand)?parentHand._frame:null; 807 | obj._hand = parentHand; 808 | obj._id = pointableData.id; 809 | obj._valid = true; 810 | 811 | obj._direction = new Leap.Vector(pointableData.direction); 812 | obj._tipPosition = new Leap.Vector(pointableData.tipPosition); 813 | obj._tipVelocity = new Leap.Vector(pointableData.tipVelocity); 814 | 815 | obj._length = pointableData.length; 816 | obj._width = pointableData.width; 817 | } 818 | }; 819 | 820 | Leap.Pointable.prototype = { 821 | 822 | frame : function(){ 823 | return this._frame; 824 | }, 825 | 826 | hand : function(){ 827 | return this._hand; 828 | }, 829 | 830 | id : function(){ 831 | return this._id; 832 | }, 833 | 834 | direction : function(){ 835 | return this._direction; 836 | }, 837 | 838 | tipPosition : function(){ 839 | return this._tipPosition; 840 | }, 841 | 842 | tipVelocity : function(){ 843 | return this._tipVelocity; 844 | }, 845 | 846 | length : function(){ 847 | return this._length; 848 | }, 849 | 850 | width : function(){ 851 | return this._width; 852 | }, 853 | 854 | toString : function(){ 855 | var val = "{id:"+this._id+",direction:"+this._direction.toString()+","; 856 | val += "tipPosition:"+this._tipPosition.toString()+","; 857 | val += "tipVelocity:"+this._tipVelocity.toString()+","; 858 | val += "length:"+this._length+","; 859 | val += "width:"+this._width+"}"; 860 | return val; 861 | }, 862 | 863 | isValid : function(){ 864 | return this._valid; 865 | } 866 | }; 867 | 868 | Leap.Pointable.invalid = function(){ 869 | return new Leap.Pointable(); 870 | }; 871 | 872 | Leap.Finger = function(fingerData, parentHand){ 873 | 874 | Leap.Pointable(fingerData, parentHand, this); 875 | 876 | this._isFinger = true; 877 | this._isTool = false; 878 | }; 879 | 880 | Leap.Finger.prototype = Leap.Pointable.prototype; 881 | 882 | Leap.Finger.invalid = function(){ 883 | return new Leap.Finger(); 884 | }; 885 | 886 | Leap.Tool = function(toolData, parentHand){ 887 | 888 | Leap.Pointable(toolData, parentHand, this); 889 | 890 | this._isTool = true; 891 | this._isFinger = false; 892 | }; 893 | 894 | Leap.Tool.prototype = Leap.Pointable.prototype; 895 | 896 | Leap.Tool.invalid = function(){ 897 | return new Leap.Tool(); 898 | }; 899 | 900 | Leap.PointableList = function(){}; 901 | 902 | Leap.PointableList.prototype = new Array; 903 | 904 | Leap.PointableList.prototype.append = function(other){ 905 | for(i=0; i0; 914 | }; 915 | 916 | Leap.FingerList = function(){}; 917 | 918 | Leap.FingerList.prototype = new Array; 919 | 920 | Leap.FingerList.prototype.append = function(other){ 921 | for(i = 0; i < other.length; i++) this.push(new Leap.Finger(other[i])); 922 | }; 923 | 924 | Leap.FingerList.prototype.count = function(){ 925 | return this.length; 926 | }; 927 | 928 | Leap.FingerList.prototype.empty = function(){ 929 | return this.length > 0; 930 | }; 931 | 932 | Leap.ToolList = function(){}; 933 | 934 | Leap.ToolList.prototype = new Array; 935 | 936 | Leap.ToolList.prototype.append = function(other){ 937 | for(i=0; i0; 946 | }; 947 | 948 | Leap.Screen = function(data){ 949 | 950 | if(data){ 951 | 952 | this._plane = new Leap.Plane(data[0],data[1],data[2]); 953 | this._center = data[0].plus(data[2]).dividedBy(2); 954 | this._origin = data[1].plus(data[1].minus(this._center)); 955 | 956 | var xv = data[2].minus(data[0]); 957 | var yv = data[0].minus(data[1]); 958 | var xscale = 2*xv.magnitude()/window.innerWidth; 959 | var yscale = 4*yv.magnitude()/window.innerHeight; 960 | this._xspan = xv.normalized().dividedBy(xscale); 961 | this._yspan = yv.normalized().dividedBy(yscale); 962 | 963 | this._valid = true; 964 | } 965 | else{ 966 | 967 | this._plane = null; 968 | this._valid = false; 969 | } 970 | }; 971 | 972 | Leap.Screen.prototype = { 973 | 974 | distanceToPoint : function(point){ 975 | return this._plane.pointDistance(point); 976 | }, 977 | 978 | intersect : function(pointable, normalize, clampRatio){ 979 | // TODO: Implement clampRatio 980 | var intersect = this._plane.rayIntersect(pointable.tipPosition(), pointable.direction()); 981 | 982 | if(normalize){ // Normalizes to 2D pixels 983 | var direction = intersect.position.minus(this._origin); 984 | var x = this._xspan.dot(direction); 985 | var y = this._yspan.dot(direction); 986 | intersect.position = new Leap.Vector([x, y, 0]); 987 | } 988 | 989 | return intersect; 990 | }, 991 | 992 | normal : function(){ 993 | return this._plane.normal(); 994 | }, 995 | 996 | isValid : function(){ 997 | return this._valid; 998 | } 999 | }; 1000 | 1001 | Leap.Screen.invalid = function(){ return new Leap.Screen(); } 1002 | Leap.ScreenList = function(){}; 1003 | 1004 | Leap.ScreenList.prototype = new Array; 1005 | 1006 | Leap.ScreenList.prototype.count = function(){ 1007 | 1008 | return this.length; 1009 | }; 1010 | 1011 | Leap.ScreenList.prototype.empty = function(){ 1012 | 1013 | return this.length > 0; 1014 | }; 1015 | 1016 | Leap.ScreenList.prototype.closestScreenHit = function(pointable){ 1017 | 1018 | if(this.length < 1) return Leap.Screen.invalid(); 1019 | 1020 | var closest = this[0]; 1021 | var min = closest.intersect(pointable).distance; 1022 | 1023 | for(var index = 1; index < this.length; index++){ 1024 | var distance = this[index].intersect(pointable).distance; 1025 | if(distance < min){ 1026 | closest = this[index]; 1027 | min = distance; 1028 | } 1029 | } 1030 | 1031 | return closest; 1032 | }; 1033 | Leap.Vector = function(data){ 1034 | 1035 | if(data instanceof Leap.Vector){ 1036 | this.x = data.x; 1037 | this.y = data.y; 1038 | this.z = data.z; 1039 | } 1040 | else if(data != null){ 1041 | this.x = (typeof(data[0]) == "number")?data[0]:0; 1042 | this.y = (typeof(data[1]) == "number")?data[1]:0; 1043 | this.z = (typeof(data[2]) == "number")?data[2]:0; 1044 | } 1045 | else{ 1046 | this.x = 0; 1047 | this.y = 0; 1048 | this.z = 0; 1049 | } 1050 | }; 1051 | 1052 | Leap.Vector.prototype = { 1053 | 1054 | angleTo : function(other){ 1055 | var denom = this.magnitude()*other.magnitude(); 1056 | if(denom > 0) return Math.acos(this.dot(other)/denom); 1057 | else return 0; 1058 | }, 1059 | 1060 | cross : function(other){ 1061 | var x = this.y*other.z - other.y*this.z; 1062 | var y = this.x*other.z - other.x*this.z; 1063 | var z = this.x*other.y - other.x*this.y; 1064 | return new Leap.Vector([x,y,z]); 1065 | }, 1066 | 1067 | distanceTo : function(other){ 1068 | return this.minus(other).magnitude(); 1069 | }, 1070 | 1071 | dot : function(other){ 1072 | return this.x*other.x + this.y*other.y + this.z*other.z; 1073 | }, 1074 | 1075 | plus : function(other){ 1076 | return new Leap.Vector([this.x + other.x,this.y + other.y,this.z + other.z]); 1077 | }, 1078 | 1079 | minus : function(other){ 1080 | return new Leap.Vector([this.x - other.x,this.y - other.y,this.z - other.z]); 1081 | }, 1082 | 1083 | multiply : function(scalar){ 1084 | return new Leap.Vector([this.x*scalar,this.y*scalar,this.z*scalar]); 1085 | }, 1086 | 1087 | dividedBy : function(scalar){ 1088 | return new Leap.Vector([this.x/scalar,this.y/scalar,this.z/scalar]); 1089 | }, 1090 | 1091 | magnitude : function(){ 1092 | return Math.sqrt(this.magnitudeSquared()); 1093 | }, 1094 | 1095 | magnitudeSquared : function(){ 1096 | return Math.pow(this.x,2) + Math.pow(this.y,2) + Math.pow(this.z,2); 1097 | }, 1098 | 1099 | normalized : function(){ 1100 | var magnitude = this.magnitude(); 1101 | if(magnitude > 0) return this.dividedBy(magnitude); 1102 | else return new Leap.Vector(); 1103 | }, 1104 | 1105 | pitch : function(){ 1106 | //var proj = new Leap.Vector([0,this.y,this.z]); 1107 | //return Leap.vectors.forward().angleTo(proj); 1108 | return Math.atan2(this.y, -this.z); 1109 | }, 1110 | 1111 | roll : function(){ 1112 | //var proj = new Leap.Vector([this.x,this.y,0]); 1113 | //return Leap.vectors.down().angleTo(proj); 1114 | return Math.atan2(this.x, -this.y); 1115 | }, 1116 | 1117 | yaw : function(){ 1118 | //var proj = new Leap.Vector([this.x,0,this.z]); 1119 | //return Leap.vectors.forward().angleTo(proj); 1120 | return Math.atan2(this.x, -this.z); 1121 | }, 1122 | 1123 | toArray : function(){ 1124 | return [this.x, this.y, this.z]; 1125 | }, 1126 | 1127 | toString : function(){ 1128 | return "{x:"+this.x+",y:"+this.y+",z:"+this.z+"}"; 1129 | }, 1130 | 1131 | compare : function(other){ 1132 | return this.x==other.x && this.y==other.y && this.z==other.z; 1133 | }, 1134 | 1135 | isValid : function(){ 1136 | return (this.x != NaN && this.x > -Infinity && this.x < Infinity) && 1137 | (this.y != NaN && this.y > -Infinity && this.y < Infinity) && 1138 | (this.z != NaN && this.z > -Infinity && this.z < Infinity); 1139 | } 1140 | }; 1141 | 1142 | Leap.Vector.backward = function(){ return new Leap.Vector([0,0,1]); }; 1143 | Leap.Vector.down = function(){ return new Leap.Vector([0,-1,0]); }; 1144 | Leap.Vector.forward = function(){ return new Leap.Vector([0,0,-1]); }; 1145 | Leap.Vector.left = function(){ return new Leap.Vector([-1,0,0]); }; 1146 | Leap.Vector.right = function(){ return new Leap.Vector([1,0,0]); }; 1147 | Leap.Vector.up = function(){ return new Leap.Vector([0,1,0]); }; 1148 | Leap.Vector.xAxis = function(){ return new Leap.Vector([1,0,0]); }; 1149 | Leap.Vector.yAxis = function(){ return new Leap.Vector([0,1,0]); }; 1150 | Leap.Vector.zAxis = function(){ return new Leap.Vector([0,0,1]); }; 1151 | Leap.Vector.zero = function(){ return new Leap.Vector([0,0,0]); }; 1152 | 1153 | --------------------------------------------------------------------------------