From 342245d30971459f91267dad19af3bef3f1b8db4 Mon Sep 17 00:00:00 2001 From: Changbai Li Date: Sun, 3 Nov 2013 19:57:47 -0500 Subject: [PATCH 1/3] Reorganized file; added different music scales I'm using Mr. Yu Jiang Tham's Theremin as the base of my Leap electronic Instrument project. First step is to implement scales to the theremin, since the original only maps position to frequency. --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 6 + README.md | 3 + assets/leap.js | 6882 ++++++++++++++++++++++++++++++++++++++++++++ assets/leap.min.js | 3 - index.html | 125 +- js/arpeggio.js | 129 + js/scaleManager.js | 75 + 8 files changed, 7099 insertions(+), 124 deletions(-) delete mode 100644 .DS_Store create mode 100644 .gitignore create mode 100644 assets/leap.js delete mode 100644 assets/leap.min.js create mode 100644 js/arpeggio.js create mode 100644 js/scaleManager.js diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bb607cfa057d5cc07509fce42ff84637f41658d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOH0E*5Z-NTji``|(BtB{NJ}gA;30$x9)%Qou%ZbmHW0Fx#}ZhX|PF`K`eus zj{c$v=iX*MOW~Wd>ij>0WgMkhulL0p&DKHt&;mJ`O`{}~8&|HAFunH{a^pqeJn&Z! zO3(akox1)?dG7pd0Uo7Kq>MqIndoU0W{XF=v)x_}`du*^4D+tojz+z%7!I!T{0PMU zu+Gjeu5Ty)YyP$?G=ChZR&r{vgm+LXOL_5DNi36Ru$MVyEFm#K3=jjvz#cPT4h8FY zk2#>F69dFRJp;Hu2q=h-!9t_jI-tVqGx}SID4^q80#O)r3>F%}1HyGGpibrHiNSR` z*oBF63>F%7I^$|&=*Ns)K3=#Q9qdAdGwx`lniwDksth!B)5i1v27Z~^M}9ShTEqY` z@Xr|Ft+6+DVNvF6{joeeYXy`iC@2_LpaKGV?Vh;s}U8nG9&t8_rR N2q;3RA_jhefp4%= this.size) return undefined; + if (i >= this._buf.length) return undefined; + return this._buf[(this.pos - i - 1) % this.size]; +} + +CircularBuffer.prototype.push = function(o) { + this._buf[this.pos % this.size] = o; + return this.pos++; +} + +},{}],3:[function(require,module,exports){ +var Connection = module.exports = require('./base_connection') + +Connection.prototype.setupSocket = function() { + var connection = this; + var socket = new WebSocket(this.getUrl()); + socket.onopen = function() { connection.handleOpen() }; + socket.onmessage = function(message) { connection.handleData(message.data) }; + socket.onclose = function() { connection.handleClose() }; + return socket; +} + +Connection.prototype.startHeartbeat = function() { + if (!this.protocol.sendHeartbeat || this.heartbeatTimer) return; + var connection = this; + var propertyName = null; + if (typeof document.hidden !== "undefined") { + propertyName = "hidden"; + } else if (typeof document.mozHidden !== "undefined") { + propertyName = "mozHidden"; + } else if (typeof document.msHidden !== "undefined") { + propertyName = "msHidden"; + } else if (typeof document.webkitHidden !== "undefined") { + propertyName = "webkitHidden"; + } else { + propertyName = undefined; + } + + var windowVisible = true; + + var focusBlurHandler = function(e) { + windowVisible = e.type === 'focus'; + }; + + window.addEventListener('focus', focusBlurHandler); + window.addEventListener('blur', focusBlurHandler); + + this.on('disconnect', function() { + if (connection.heartbeatTimer) { + clearTimeout(connection.heartbeatTimer); + delete connection.heartbeatTimer; + } + window.removeEventListener('focus', focusBlurHandler); + window.removeEventListener('blur', focusBlurHandler); + }); + + this.heartbeatTimer = setInterval(function() { + var isVisible = propertyName === undefined ? true : document[propertyName] === false; + if (isVisible && windowVisible) { + connection.sendHeartbeat(); + } else { + connection.setHeartbeatState(false); + } + }, this.opts.heartbeatInterval); +} + +},{"./base_connection":1}],4:[function(require,module,exports){ +(function(process){var Frame = require('./frame') + , CircularBuffer = require("./circular_buffer") + , Pipeline = require("./pipeline") + , EventEmitter = require('events').EventEmitter + , gestureListener = require('./gesture').gestureListener + , _ = require('underscore'); + +/** + * Constructs a Controller object. + * + * When creating a Controller object, you may optionally pass in options + * to set the host , set the port, enable gestures, or select the frame event type. + * + * ```javascript + * var controller = new Leap.Controller({ + * host: '127.0.0.1', + * port: 6437, + * enableGestures: true, + * frameEventName: 'animationFrame' + * }); + * ``` + * + * @class Controller + * @memberof Leap + * @classdesc + * The Controller class is your main interface to the Leap Motion Controller. + * + * Create an instance of this Controller class to access frames of tracking data + * and configuration information. Frame data can be polled at any time using the + * [Controller.frame]{@link Leap.Controller#frame}() function. Call frame() or frame(0) to get the most recent + * frame. Set the history parameter to a positive integer to access previous frames. + * A controller stores up to 60 frames in its frame history. + * + * Polling is an appropriate strategy for applications which already have an + * intrinsic update loop, such as a game. + */ +var Controller = module.exports = function(opts) { + var inNode = typeof(process) !== 'undefined' && process.title === 'node'; + + opts = _.defaults(opts || {}, { + inNode: inNode + }); + + this.inNode = opts.inNode; + + opts = _.defaults(opts || {}, { + frameEventName: this.useAnimationLoop() ? 'animationFrame' : 'deviceFrame', + suppressAnimationLoop: false + }); + + this.suppressAnimationLoop = opts.suppressAnimationLoop; + this.frameEventName = opts.frameEventName; + this.history = new CircularBuffer(200); + this.lastFrame = Frame.Invalid; + this.lastValidFrame = Frame.Invalid; + this.lastConnectionFrame = Frame.Invalid; + this.accumulatedGestures = []; + if (opts.connectionType === undefined) { + this.connectionType = (this.inBrowser() ? require('./connection') : require('./node_connection')); + } else { + this.connectionType = opts.connectionType; + } + this.connection = new this.connectionType(opts); + this.setupConnectionEvents(); +} + +Controller.prototype.gesture = function(type, cb) { + var creator = gestureListener(this, type); + if (cb !== undefined) { + creator.stop(cb); + } + return creator; +} + +Controller.prototype.inBrowser = function() { + return !this.inNode; +} + +Controller.prototype.useAnimationLoop = function() { + return this.inBrowser() && typeof(chrome) === "undefined"; +} + +Controller.prototype.connect = function() { + var controller = this; + if (this.connection.connect() && this.inBrowser() && !controller.suppressAnimationLoop) { + var callback = function() { + controller.emit('animationFrame', controller.lastConnectionFrame); + window.requestAnimationFrame(callback); + } + window.requestAnimationFrame(callback); + } +} + +Controller.prototype.disconnect = function() { + this.connection.disconnect(); +} + +/** + * Returns a frame of tracking data from the Leap. + * + * Use the optional history parameter to specify which frame to retrieve. + * Call frame() or frame(0) to access the most recent frame; call frame(1) to + * access the previous frame, and so on. If you use a history value greater + * than the number of stored frames, then the controller returns an invalid frame. + * + * @method frame + * @memberof Leap.Controller.prototype + * @param {number} history The age of the frame to return, counting backwards from + * the most recent frame (0) into the past and up to the maximum age (59). + * @returns {Leap.Frame} The specified frame; or, if no history + * parameter is specified, the newest frame. If a frame is not available at + * the specified history position, an invalid Frame is returned. + */ +Controller.prototype.frame = function(num) { + return this.history.get(num) || Frame.Invalid; +} + +Controller.prototype.loop = function(callback) { + switch (callback.length) { + case 1: + this.on(this.frameEventName, callback); + break; + case 2: + var controller = this; + var scheduler = null; + var immediateRunnerCallback = function(frame) { + callback(frame, function() { + if (controller.lastFrame != frame) { + immediateRunnerCallback(controller.lastFrame); + } else { + controller.once(controller.frameEventName, immediateRunnerCallback); + } + }); + } + this.once(this.frameEventName, immediateRunnerCallback); + break; + } + this.connect(); +} + +Controller.prototype.addStep = function(step) { + if (!this.pipeline) this.pipeline = new Pipeline(this); + this.pipeline.addStep(step); +} + +Controller.prototype.processFrame = function(frame) { + if (frame.gestures) { + this.accumulatedGestures = this.accumulatedGestures.concat(frame.gestures); + } + if (this.pipeline) { + frame = this.pipeline.run(frame); + if (!frame) frame = Frame.Invalid; + } + this.lastConnectionFrame = frame; + this.emit('deviceFrame', frame); +} + +Controller.prototype.processFinishedFrame = function(frame) { + this.lastFrame = frame; + if (frame.valid) { + this.lastValidFrame = frame; + } + frame.controller = this; + frame.historyIdx = this.history.push(frame); + if (frame.gestures) { + frame.gestures = this.accumulatedGestures; + this.accumulatedGestures = []; + for (var gestureIdx = 0; gestureIdx != frame.gestures.length; gestureIdx++) { + this.emit("gesture", frame.gestures[gestureIdx], frame); + } + } + this.emit('frame', frame); +} + +Controller.prototype.setupConnectionEvents = function() { + var controller = this; + this.connection.on('frame', function(frame) { + controller.processFrame(frame); + }); + this.on(this.frameEventName, function(frame) { + controller.processFinishedFrame(frame); + }); + + // Delegate connection events + this.connection.on('disconnect', function() { controller.emit('disconnect'); }); + this.connection.on('ready', function() { controller.emit('ready'); }); + this.connection.on('connect', function() { controller.emit('connect'); }); + this.connection.on('focus', function() { controller.emit('focus') }); + this.connection.on('blur', function() { controller.emit('blur') }); + this.connection.on('protocol', function(protocol) { controller.emit('protocol', protocol); }); + this.connection.on('deviceConnect', function(evt) { controller.emit(evt.state ? 'deviceConnected' : 'deviceDisconnected'); }); +} + +_.extend(Controller.prototype, EventEmitter.prototype); + +})(require("__browserify_process")) +},{"./circular_buffer":2,"./connection":3,"./frame":5,"./gesture":6,"./node_connection":16,"./pipeline":10,"__browserify_process":18,"events":17,"underscore":20}],5:[function(require,module,exports){ +var Hand = require("./hand") + , Pointable = require("./pointable") + , createGesture = require("./gesture").createGesture + , glMatrix = require("gl-matrix") + , mat3 = glMatrix.mat3 + , vec3 = glMatrix.vec3 + , InteractionBox = require("./interaction_box") + , _ = require("underscore"); + +/** + * Constructs a Frame object. + * + * Frame instances created with this constructor are invalid. + * Get valid Frame objects by calling the + * [Controller.frame]{@link Leap.Controller#frame}() function. + * + * @class Frame + * @memberof Leap + * @classdesc + * The Frame class represents a set of hand and finger tracking data detected + * in a single frame. + * + * The Leap detects hands, fingers and tools within the tracking area, reporting + * their positions, orientations and motions in frames at the Leap frame rate. + * + * Access Frame objects using the [Controller.frame]{@link Leap.Controller#frame}() function. + */ +var Frame = module.exports = function(data) { + /** + * Reports whether this Frame instance is valid. + * + * A valid Frame is one generated by the Controller object that contains + * tracking data for all detected entities. An invalid Frame contains no + * actual tracking data, but you can call its functions without risk of a + * undefined object exception. The invalid Frame mechanism makes it more + * convenient to track individual data across the frame history. For example, + * you can invoke: + * + * ```javascript + * var finger = controller.frame(n).finger(fingerID); + * ``` + * + * for an arbitrary Frame history value, "n", without first checking whether + * frame(n) returned a null object. (You should still check that the + * returned Finger instance is valid.) + * + * @member valid + * @memberof Leap.Frame.prototype + * @type {Boolean} + */ + this.valid = true; + /** + * A unique ID for this Frame. Consecutive frames processed by the Leap + * have consecutive increasing values. + * @member id + * @memberof Leap.Frame.prototype + * @type {String} + */ + this.id = data.id; + /** + * The frame capture time in microseconds elapsed since the Leap started. + * @member timestamp + * @memberof Leap.Frame.prototype + * @type {number} + */ + this.timestamp = data.timestamp; + /** + * The list of Hand objects detected in this frame, given in arbitrary order. + * The list can be empty if no hands are detected. + * + * @member hands[] + * @memberof Leap.Frame.prototype + * @type {Leap.Hand} + */ + this.hands = []; + this.handsMap = {}; + /** + * The list of Pointable objects (fingers and tools) detected in this frame, + * given in arbitrary order. The list can be empty if no fingers or tools are + * detected. + * + * @member pointables[] + * @memberof Leap.Frame.prototype + * @type {Leap.Pointable} + */ + this.pointables = []; + /** + * The list of Tool objects detected in this frame, given in arbitrary order. + * The list can be empty if no tools are detected. + * + * @member tools[] + * @memberof Leap.Frame.prototype + * @type {Leap.Pointable} + */ + this.tools = []; + /** + * The list of Finger objects detected in this frame, given in arbitrary order. + * The list can be empty if no fingers are detected. + * @member fingers[] + * @memberof Leap.Frame.prototype + * @type {Leap.Pointable} + */ + this.fingers = []; + + if (data.interactionBox) { + this.interactionBox = new InteractionBox(data.interactionBox); + } + this.gestures = []; + this.pointablesMap = {}; + this._translation = data.t; + this._rotation = _.flatten(data.r); + this._scaleFactor = data.s; + this.data = data; + this.type = 'frame'; // used by event emitting + this.currentFrameRate = data.currentFrameRate; + var handMap = {}; + for (var handIdx = 0, handCount = data.hands.length; handIdx != handCount; handIdx++) { + var hand = new Hand(data.hands[handIdx]); + hand.frame = this; + this.hands.push(hand); + this.handsMap[hand.id] = hand; + handMap[hand.id] = handIdx; + } + for (var pointableIdx = 0, pointableCount = data.pointables.length; pointableIdx != pointableCount; pointableIdx++) { + var pointable = new Pointable(data.pointables[pointableIdx]); + pointable.frame = this; + this.pointables.push(pointable); + this.pointablesMap[pointable.id] = pointable; + (pointable.tool ? this.tools : this.fingers).push(pointable); + if (pointable.handId !== undefined && handMap.hasOwnProperty(pointable.handId)) { + var hand = this.hands[handMap[pointable.handId]]; + hand.pointables.push(pointable); + (pointable.tool ? hand.tools : hand.fingers).push(pointable); + } + } + + if (data.gestures) { + /** + * The list of Gesture objects detected in this frame, given in arbitrary order. + * The list can be empty if no gestures are detected. + * + * Circle and swipe gestures are updated every frame. Tap gestures + * only appear in the list for a single frame. + * @member gestures[] + * @memberof Leap.Frame.prototype + * @type {Leap.Gesture} + */ + for (var gestureIdx = 0, gestureCount = data.gestures.length; gestureIdx != gestureCount; gestureIdx++) { + this.gestures.push(createGesture(data.gestures[gestureIdx])); + } + } +} + +/** + * The tool with the specified ID in this frame. + * + * Use the Frame tool() function to retrieve a tool from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Pointable object, but if no tool + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a tool is lost and subsequently + * regained, the new Pointable object representing that tool may have a + * different ID than that representing the tool in an earlier frame. + * + * @method tool + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a Tool object from a previous frame. + * @returns {Leap.Pointable} The tool with the + * matching ID if one exists in this frame; otherwise, an invalid Pointable object + * is returned. + */ +Frame.prototype.tool = function(id) { + var pointable = this.pointable(id); + return pointable.tool ? pointable : Pointable.Invalid; +} + +/** + * The Pointable object with the specified ID in this frame. + * + * Use the Frame pointable() function to retrieve the Pointable object from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Pointable object, but if no finger or tool + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a finger or tool is lost and subsequently + * regained, the new Pointable object representing that finger or tool may have + * a different ID than that representing the finger or tool in an earlier frame. + * + * @method pointable + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a Pointable object from a previous frame. + * @returns {Leap.Pointable} The Pointable object with + * the matching ID if one exists in this frame; + * otherwise, an invalid Pointable object is returned. + */ +Frame.prototype.pointable = function(id) { + return this.pointablesMap[id] || Pointable.Invalid; +} + +/** + * The finger with the specified ID in this frame. + * + * Use the Frame finger() function to retrieve the finger from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Finger object, but if no finger + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a finger is lost and subsequently + * regained, the new Pointable object representing that physical finger may have + * a different ID than that representing the finger in an earlier frame. + * + * @method finger + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a finger from a previous frame. + * @returns {Leap.Pointable} The finger with the + * matching ID if one exists in this frame; otherwise, an invalid Pointable + * object is returned. + */ +Frame.prototype.finger = function(id) { + var pointable = this.pointable(id); + return !pointable.tool ? pointable : Pointable.Invalid; +} + +/** + * The Hand object with the specified ID in this frame. + * + * Use the Frame hand() function to retrieve the Hand object from + * this frame using an ID value obtained from a previous frame. + * This function always returns a Hand object, but if no hand + * with the specified ID is present, an invalid Hand object is returned. + * + * Note that ID values persist across frames, but only until tracking of a + * particular object is lost. If tracking of a hand is lost and subsequently + * regained, the new Hand object representing that physical hand may have + * a different ID than that representing the physical hand in an earlier frame. + * + * @method hand + * @memberof Leap.Frame.prototype + * @param {String} id The ID value of a Hand object from a previous frame. + * @returns {Leap.Hand} The Hand object with the matching + * ID if one exists in this frame; otherwise, an invalid Hand object is returned. + */ +Frame.prototype.hand = function(id) { + return this.handsMap[id] || Hand.Invalid; +} + +/** + * The angle of rotation around the rotation axis derived from the overall + * rotational motion between the current frame and the specified frame. + * + * The returned angle is expressed in radians measured clockwise around + * the rotation axis (using the right-hand rule) between the start and end frames. + * The value is always between 0 and pi radians (0 and 180 degrees). + * + * The Leap derives frame rotation from the relative change in position and + * orientation of all objects detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, then the + * angle of rotation is zero. + * + * @method rotationAngle + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @param {number[]} [axis] The axis to measure rotation around. + * @returns {number} A positive value containing the heuristically determined + * rotational change between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.rotationAngle = function(sinceFrame, axis) { + if (!this.valid || !sinceFrame.valid) return 0.0; + + var rot = this.rotationMatrix(sinceFrame); + var cs = (rot[0] + rot[4] + rot[8] - 1.0)*0.5 + var angle = Math.acos(cs); + angle = isNaN(angle) ? 0.0 : angle; + + if (axis !== undefined) { + var rotAxis = this.rotationAxis(sinceFrame); + angle *= vec3.dot(rotAxis, vec3.normalize(vec3.create(), axis)); + } + + return angle; +} + +/** + * The axis of rotation derived from the overall rotational motion between + * the current frame and the specified frame. + * + * The returned direction vector is normalized. + * + * The Leap derives frame rotation from the relative change in position and + * orientation of all objects detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, or if no + * rotation is detected between the two frames, a zero vector is returned. + * + * @method rotationAxis + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A normalized direction vector representing the axis of the heuristically determined + * rotational change between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.rotationAxis = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + return vec3.normalize(vec3.create(), [ + this._rotation[7] - sinceFrame._rotation[5], + this._rotation[2] - sinceFrame._rotation[6], + this._rotation[3] - sinceFrame._rotation[1] + ]); +} + +/** + * The transform matrix expressing the rotation derived from the overall + * rotational motion between the current frame and the specified frame. + * + * The Leap derives frame rotation from the relative change in position and + * orientation of all objects detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, then + * this method returns an identity matrix. + * + * @method rotationMatrix + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A transformation matrix containing the heuristically determined + * rotational change between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.rotationMatrix = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return mat3.create(); + var transpose = mat3.transpose(mat3.create(), this._rotation) + return mat3.multiply(mat3.create(), sinceFrame._rotation, transpose); +} + +/** + * The scale factor derived from the overall motion between the current frame and the specified frame. + * + * The scale factor is always positive. A value of 1.0 indicates no scaling took place. + * Values between 0.0 and 1.0 indicate contraction and values greater than 1.0 indicate expansion. + * + * The Leap derives scaling from the relative inward or outward motion of all + * objects detected in the field of view (independent of translation and rotation). + * + * If either this frame or sinceFrame is an invalid Frame object, then this method returns 1.0. + * + * @method scaleFactor + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative scaling. + * @returns {number} A positive value representing the heuristically determined + * scaling change ratio between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.scaleFactor = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return 1.0; + return Math.exp(this._scaleFactor - sinceFrame._scaleFactor); +} + +/** + * The change of position derived from the overall linear motion between the + * current frame and the specified frame. + * + * The returned translation vector provides the magnitude and direction of the + * movement in millimeters. + * + * The Leap derives frame translation from the linear motion of all objects + * detected in the field of view. + * + * If either this frame or sinceFrame is an invalid Frame object, then this + * method returns a zero vector. + * + * @method translation + * @memberof Leap.Frame.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative translation. + * @returns {number[]} A vector representing the heuristically determined change in + * position of all objects between the current frame and that specified in the sinceFrame parameter. + */ +Frame.prototype.translation = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + return vec3.subtract(vec3.create(), this._translation, sinceFrame._translation); +} + +/** + * A string containing a brief, human readable description of the Frame object. + * + * @method toString + * @memberof Leap.Frame.prototype + * @returns {String} A brief description of this frame. + */ +Frame.prototype.toString = function() { + var str = "Frame [ id:"+this.id+" | timestamp:"+this.timestamp+" | Hand count:("+this.hands.length+") | Pointable count:("+this.pointables.length+")"; + if (this.gestures) str += " | Gesture count:("+this.gestures.length+")"; + str += " ]"; + return str; +} + +/** + * Returns a JSON-formatted string containing the hands, pointables and gestures + * in this frame. + * + * @method dump + * @memberof Leap.Frame.prototype + * @returns {String} A JSON-formatted string. + */ +Frame.prototype.dump = function() { + var out = ''; + out += "Frame Info:
"; + out += this.toString(); + out += "

Hands:
" + for (var handIdx = 0, handCount = this.hands.length; handIdx != handCount; handIdx++) { + out += " "+ this.hands[handIdx].toString() + "
"; + } + out += "

Pointables:
"; + for (var pointableIdx = 0, pointableCount = this.pointables.length; pointableIdx != pointableCount; pointableIdx++) { + out += " "+ this.pointables[pointableIdx].toString() + "
"; + } + if (this.gestures) { + out += "

Gestures:
"; + for (var gestureIdx = 0, gestureCount = this.gestures.length; gestureIdx != gestureCount; gestureIdx++) { + out += " "+ this.gestures[gestureIdx].toString() + "
"; + } + } + out += "

Raw JSON:
"; + out += JSON.stringify(this.data); + return out; +} + +/** + * An invalid Frame object. + * + * You can use this invalid Frame in comparisons testing + * whether a given Frame instance is valid or invalid. (You can also check the + * [Frame.valid]{@link Leap.Frame#valid} property.) + * + * @static + * @type {Leap.Frame} + * @name Invalid + * @memberof Leap.Frame + */ +Frame.Invalid = { + valid: false, + hands: [], + fingers: [], + tools: [], + gestures: [], + pointables: [], + pointable: function() { return Pointable.Invalid }, + finger: function() { return Pointable.Invalid }, + hand: function() { return Hand.Invalid }, + toString: function() { return "invalid frame" }, + dump: function() { return this.toString() }, + rotationAngle: function() { return 0.0; }, + rotationMatrix: function() { return mat3.create(); }, + rotationAxis: function() { return vec3.create(); }, + scaleFactor: function() { return 1.0; }, + translation: function() { return vec3.create(); } +}; + +},{"./gesture":6,"./hand":7,"./interaction_box":9,"./pointable":11,"gl-matrix":19,"underscore":20}],6:[function(require,module,exports){ +var glMatrix = require("gl-matrix") + , vec3 = glMatrix.vec3 + , EventEmitter = require('events').EventEmitter + , _ = require('underscore'); + +/** + * Constructs a new Gesture object. + * + * An uninitialized Gesture object is considered invalid. Get valid instances + * of the Gesture class, which will be one of the Gesture subclasses, from a + * Frame object. + * + * @class Gesture + * @abstract + * @memberof Leap + * @classdesc + * The Gesture class represents a recognized movement by the user. + * + * The Leap watches the activity within its field of view for certain movement + * patterns typical of a user gesture or command. For example, a movement from side to + * side with the hand can indicate a swipe gesture, while a finger poking forward + * can indicate a screen tap gesture. + * + * When the Leap recognizes a gesture, it assigns an ID and adds a + * Gesture object to the frame gesture list. For continuous gestures, which + * occur over many frames, the Leap updates the gesture by adding + * a Gesture object having the same ID and updated properties in each + * subsequent frame. + * + * **Important:** Recognition for each type of gesture must be enabled; + * otherwise **no gestures are recognized or reported**. + * + * Subclasses of Gesture define the properties for the specific movement patterns + * recognized by the Leap. + * + * The Gesture subclasses for include: + * + * * CircleGesture -- A circular movement by a finger. + * * SwipeGesture -- A straight line movement by the hand with fingers extended. + * * ScreenTapGesture -- A forward tapping movement by a finger. + * * KeyTapGesture -- A downward tapping movement by a finger. + * + * Circle and swipe gestures are continuous and these objects can have a + * state of start, update, and stop. + * + * The screen tap gesture is a discrete gesture. The Leap only creates a single + * ScreenTapGesture object appears for each tap and it always has a stop state. + * + * Get valid Gesture instances from a Frame object. You can get a list of gestures + * from the Frame gestures array. You can also use the Frame gesture() method + * to find a gesture in the current frame using an ID value obtained in a + * previous frame. + * + * Gesture objects can be invalid. For example, when you get a gesture by ID + * using Frame.gesture(), and there is no gesture with that ID in the current + * frame, then gesture() returns an Invalid Gesture object (rather than a null + * value). Always check object validity in situations where a gesture might be + * invalid. + */ +var createGesture = exports.createGesture = function(data) { + var gesture; + switch (data.type) { + case 'circle': + gesture = new CircleGesture(data); + break; + case 'swipe': + gesture = new SwipeGesture(data); + break; + case 'screenTap': + gesture = new ScreenTapGesture(data); + break; + case 'keyTap': + gesture = new KeyTapGesture(data); + break; + default: + throw "unkown gesture type"; + } + /** + * The gesture ID. + * + * All Gesture objects belonging to the same recognized movement share the + * same ID value. Use the ID value with the Frame::gesture() method to + * find updates related to this Gesture object in subsequent frames. + * + * @member id + * @memberof Leap.Gesture.prototype + * @type {number} + */ + gesture.id = data.id; + /** + * The list of hands associated with this Gesture, if any. + * + * If no hands are related to this gesture, the list is empty. + * + * @member handIds + * @memberof Leap.Gesture.prototype + * @type {Array} + */ + gesture.handIds = data.handIds; + /** + * The list of fingers and tools associated with this Gesture, if any. + * + * If no Pointable objects are related to this gesture, the list is empty. + * + * @member pointableIds + * @memberof Leap.Gesture.prototype + * @type {Array} + */ + gesture.pointableIds = data.pointableIds; + /** + * The elapsed duration of the recognized movement up to the + * frame containing this Gesture object, in microseconds. + * + * The duration reported for the first Gesture in the sequence (with the + * start state) will typically be a small positive number since + * the movement must progress far enough for the Leap to recognize it as + * an intentional gesture. + * + * @member duration + * @memberof Leap.Gesture.prototype + * @type {number} + */ + gesture.duration = data.duration; + /** + * The gesture ID. + * + * Recognized movements occur over time and have a beginning, a middle, + * and an end. The 'state()' attribute reports where in that sequence this + * Gesture object falls. + * + * Possible values for the state field are: + * + * * start + * * update + * * stop + * + * @member state + * @memberof Leap.Gesture.prototype + * @type {String} + */ + gesture.state = data.state; + /** + * The gesture type. + * + * Possible values for the type field are: + * + * * circle + * * swipe + * * screenTap + * * keyTap + * + * @member type + * @memberof Leap.Gesture.prototype + * @type {String} + */ + gesture.type = data.type; + return gesture; +} + +var gestureListener = exports.gestureListener = function(controller, type) { + var handlers = {}; + var gestureMap = {}; + + var gestureCreator = function() { + var candidateGesture = gestureMap[gesture.id]; + if (candidateGesture !== undefined) gesture.update(gesture, frame); + if (gesture.state == "start" || gesture.state == "stop") { + if (type == gesture.type && gestureMap[gesture.id] === undefined) { + gestureMap[gesture.id] = new Gesture(gesture, frame); + gesture.update(gesture, frame); + } + if (gesture.state == "stop") { + delete gestureMap[gesture.id]; + } + } + }; + + controller.on('gesture', function(gesture, frame) { + if (gesture.type == type) { + if (gesture.state == "start" || gesture.state == "stop") { + if (gestureMap[gesture.id] === undefined) { + var gestureTracker = new Gesture(gesture, frame); + gestureMap[gesture.id] = gestureTracker; + _.each(handlers, function(cb, name) { + gestureTracker.on(name, cb); + }); + } + } + gestureMap[gesture.id].update(gesture, frame); + if (gesture.state == "stop") { + delete gestureMap[gesture.id]; + } + } + }); + var builder = { + start: function(cb) { + handlers['start'] = cb; + return builder; + }, + stop: function(cb) { + handlers['stop'] = cb; + return builder; + }, + complete: function(cb) { + handlers['stop'] = cb; + return builder; + }, + update: function(cb) { + handlers['update'] = cb; + return builder; + } + } + return builder; +} + +var Gesture = exports.Gesture = function(gesture, frame) { + this.gestures = [gesture]; + this.frames = [frame]; +} + +Gesture.prototype.update = function(gesture, frame) { + this.lastGesture = gesture; + this.lastFrame = frame; + this.gestures.push(gesture); + this.frames.push(frame); + this.emit(gesture.state, this); +} + +Gesture.prototype.translation = function() { + return vec3.subtract(vec3.create(), this.lastGesture.startPosition, this.lastGesture.position); +} + +_.extend(Gesture.prototype, EventEmitter.prototype); + +/** + * Constructs a new CircleGesture object. + * + * An uninitialized CircleGesture object is considered invalid. Get valid instances + * of the CircleGesture class from a Frame object. + * + * @class CircleGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The CircleGesture classes represents a circular finger movement. + * + * A circle movement is recognized when the tip of a finger draws a circle + * within the Leap field of view. + * + * ![CircleGesture](images/Leap_Gesture_Circle.png) + * + * Circle gestures are continuous. The CircleGesture objects for the gesture have + * three possible states: + * + * * start -- The circle gesture has just started. The movement has + * progressed far enough for the recognizer to classify it as a circle. + * * update -- The circle gesture is continuing. + * * stop -- The circle gesture is finished. + */ +var CircleGesture = function(data) { + /** + * The center point of the circle within the Leap frame of reference. + * + * @member center + * @memberof Leap.CircleGesture.prototype + * @type {number[]} + */ + this.center = data.center; + /** + * The normal vector for the circle being traced. + * + * If you draw the circle clockwise, the normal vector points in the same + * general direction as the pointable object drawing the circle. If you draw + * the circle counterclockwise, the normal points back toward the + * pointable. If the angle between the normal and the pointable object + * drawing the circle is less than 90 degrees, then the circle is clockwise. + * + * ```javascript + * var clockwiseness; + * if (circle.pointable.direction.angleTo(circle.normal) <= PI/4) { + * clockwiseness = "clockwise"; + * } + * else + * { + * clockwiseness = "counterclockwise"; + * } + * ``` + * + * @member normal + * @memberof Leap.CircleGesture.prototype + * @type {number[]} + */ + this.normal = data.normal; + /** + * The number of times the finger tip has traversed the circle. + * + * Progress is reported as a positive number of the number. For example, + * a progress value of .5 indicates that the finger has gone halfway + * around, while a value of 3 indicates that the finger has gone around + * the the circle three times. + * + * Progress starts where the circle gesture began. Since the circle + * must be partially formed before the Leap can recognize it, progress + * will be greater than zero when a circle gesture first appears in the + * frame. + * + * @member progress + * @memberof Leap.CircleGesture.prototype + * @type {number} + */ + this.progress = data.progress; + /** + * The radius of the circle in mm. + * + * @member radius + * @memberof Leap.CircleGesture.prototype + * @type {number} + */ + this.radius = data.radius; +} + +CircleGesture.prototype.toString = function() { + return "CircleGesture ["+JSON.stringify(this)+"]"; +} + +/** + * Constructs a new SwipeGesture object. + * + * An uninitialized SwipeGesture object is considered invalid. Get valid instances + * of the SwipeGesture class from a Frame object. + * + * @class SwipeGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The SwipeGesture class represents a swiping motion of a finger or tool. + * + * ![SwipeGesture](images/Leap_Gesture_Swipe.png) + * + * Swipe gestures are continuous. + */ +var SwipeGesture = function(data) { + /** + * The starting position within the Leap frame of + * reference, in mm. + * + * @member startPosition + * @memberof Leap.SwipeGesture.prototype + * @type {number[]} + */ + this.startPosition = data.startPosition; + /** + * The current swipe position within the Leap frame of + * reference, in mm. + * + * @member position + * @memberof Leap.SwipeGesture.prototype + * @type {number[]} + */ + this.position = data.position; + /** + * The unit direction vector parallel to the swipe motion. + * + * You can compare the components of the vector to classify the swipe as + * appropriate for your application. For example, if you are using swipes + * for two dimensional scrolling, you can compare the x and y values to + * determine if the swipe is primarily horizontal or vertical. + * + * @member direction + * @memberof Leap.SwipeGesture.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The speed of the finger performing the swipe gesture in + * millimeters per second. + * + * @member speed + * @memberof Leap.SwipeGesture.prototype + * @type {number} + */ + this.speed = data.speed; +} + +SwipeGesture.prototype.toString = function() { + return "SwipeGesture ["+JSON.stringify(this)+"]"; +} + +/** + * Constructs a new ScreenTapGesture object. + * + * An uninitialized ScreenTapGesture object is considered invalid. Get valid instances + * of the ScreenTapGesture class from a Frame object. + * + * @class ScreenTapGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The ScreenTapGesture class represents a tapping gesture by a finger or tool. + * + * A screen tap gesture is recognized when the tip of a finger pokes forward + * and then springs back to approximately the original postion, as if + * tapping a vertical screen. The tapping finger must pause briefly before beginning the tap. + * + * ![ScreenTap](images/Leap_Gesture_Tap2.png) + * + * ScreenTap gestures are discrete. The ScreenTapGesture object representing a tap always + * has the state, STATE_STOP. Only one ScreenTapGesture object is created for each + * screen tap gesture recognized. + */ +var ScreenTapGesture = function(data) { + /** + * The position where the screen tap is registered. + * + * @member position + * @memberof Leap.ScreenTapGesture.prototype + * @type {number[]} + */ + this.position = data.position; + /** + * The direction of finger tip motion. + * + * @member direction + * @memberof Leap.ScreenTapGesture.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The progess value is always 1.0 for a screen tap gesture. + * + * @member progress + * @memberof Leap.ScreenTapGesture.prototype + * @type {number} + */ + this.progress = data.progress; +} + +ScreenTapGesture.prototype.toString = function() { + return "ScreenTapGesture ["+JSON.stringify(this)+"]"; +} + +/** + * Constructs a new KeyTapGesture object. + * + * An uninitialized KeyTapGesture object is considered invalid. Get valid instances + * of the KeyTapGesture class from a Frame object. + * + * @class KeyTapGesture + * @memberof Leap + * @augments Leap.Gesture + * @classdesc + * The KeyTapGesture class represents a tapping gesture by a finger or tool. + * + * A key tap gesture is recognized when the tip of a finger rotates down toward the + * palm and then springs back to approximately the original postion, as if + * tapping. The tapping finger must pause briefly before beginning the tap. + * + * ![KeyTap](images/Leap_Gesture_Tap.png) + * + * Key tap gestures are discrete. The KeyTapGesture object representing a tap always + * has the state, STATE_STOP. Only one KeyTapGesture object is created for each + * key tap gesture recognized. + */ +var KeyTapGesture = function(data) { + /** + * The position where the key tap is registered. + * + * @member position + * @memberof Leap.KeyTapGesture.prototype + * @type {number[]} + */ + this.position = data.position; + /** + * The direction of finger tip motion. + * + * @member direction + * @memberof Leap.KeyTapGesture.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The progess value is always 1.0 for a key tap gesture. + * + * @member progress + * @memberof Leap.KeyTapGesture.prototype + * @type {number} + */ + this.progress = data.progress; +} + +KeyTapGesture.prototype.toString = function() { + return "KeyTapGesture ["+JSON.stringify(this)+"]"; +} + +},{"events":17,"gl-matrix":19,"underscore":20}],7:[function(require,module,exports){ +var Pointable = require("./pointable") + , glMatrix = require("gl-matrix") + , mat3 = glMatrix.mat3 + , vec3 = glMatrix.vec3 + , _ = require("underscore"); + +/** + * Constructs a Hand object. + * + * An uninitialized hand is considered invalid. + * Get valid Hand objects from a Frame object. + * @class Hand + * @memberof Leap + * @classdesc + * The Hand class reports the physical characteristics of a detected hand. + * + * Hand tracking data includes a palm position and velocity; vectors for + * the palm normal and direction to the fingers; properties of a sphere fit + * to the hand; and lists of the attached fingers and tools. + * + * Note that Hand objects can be invalid, which means that they do not contain + * valid tracking data and do not correspond to a physical entity. Invalid Hand + * objects can be the result of asking for a Hand object using an ID from an + * earlier frame when no Hand objects with that ID exist in the current frame. + * A Hand object created from the Hand constructor is also invalid. + * Test for validity with the [Hand.valid]{@link Leap.Hand#valid} property. + */ +var Hand = module.exports = function(data) { + /** + * A unique ID assigned to this Hand object, whose value remains the same + * across consecutive frames while the tracked hand remains visible. If + * tracking is lost (for example, when a hand is occluded by another hand + * or when it is withdrawn from or reaches the edge of the Leap field of view), + * the Leap may assign a new ID when it detects the hand in a future frame. + * + * Use the ID value with the {@link Frame.hand}() function to find this + * Hand object in future frames. + * + * @member id + * @memberof Leap.Hand.prototype + * @type {String} + */ + this.id = data.id; + /** + * The center position of the palm in millimeters from the Leap origin. + * @member palmPosition + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.palmPosition = data.palmPosition; + /** + * The direction from the palm position toward the fingers. + * + * The direction is expressed as a unit vector pointing in the same + * direction as the directed line from the palm position to the fingers. + * + * @member direction + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.direction = data.direction; + /** + * The rate of change of the palm position in millimeters/second. + * + * @member palmVeclocity + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.palmVelocity = data.palmVelocity; + /** + * The normal vector to the palm. If your hand is flat, this vector will + * point downward, or "out" of the front surface of your palm. + * + * ![Palm Vectors](images/Leap_Palm_Vectors.png) + * + * The direction is expressed as a unit vector pointing in the same + * direction as the palm normal (that is, a vector orthogonal to the palm). + * @member palmNormal + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.palmNormal = data.palmNormal; + /** + * The center of a sphere fit to the curvature of this hand. + * + * This sphere is placed roughly as if the hand were holding a ball. + * + * ![Hand Ball](images/Leap_Hand_Ball.png) + * @member sphereCenter + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.sphereCenter = data.sphereCenter; + /** + * The radius of a sphere fit to the curvature of this hand, in millimeters. + * + * This sphere is placed roughly as if the hand were holding a ball. Thus the + * size of the sphere decreases as the fingers are curled into a fist. + * + * @member sphereRadius + * @memberof Leap.Hand.prototype + * @type {number} + */ + this.sphereRadius = data.sphereRadius; + /** + * Reports whether this is a valid Hand object. + * + * @member valid + * @memberof Leap.Hand.prototype + * @type {boolean} + */ + this.valid = true; + /** + * The list of Pointable objects (fingers and tools) detected in this frame + * that are associated with this hand, given in arbitrary order. The list + * can be empty if no fingers or tools associated with this hand are detected. + * + * Use the {@link Pointable} tool property to determine + * whether or not an item in the list represents a tool or finger. + * You can also get only the tools using the Hand.tools[] list or + * only the fingers using the Hand.fingers[] list. + * + * @member pointables[] + * @memberof Leap.Hand.prototype + * @type {Leap.Pointable[]} + */ + this.pointables = []; + /** + * The list of fingers detected in this frame that are attached to + * this hand, given in arbitrary order. + * + * The list can be empty if no fingers attached to this hand are detected. + * + * @member fingers[] + * @memberof Leap.Hand.prototype + * @type {Leap.Pointable[]} + */ + this.fingers = []; + /** + * The list of tools detected in this frame that are held by this + * hand, given in arbitrary order. + * + * The list can be empty if no tools held by this hand are detected. + * + * @member tools[] + * @memberof Leap.Hand.prototype + * @type {Leap.Pointable[]} + */ + this.tools = []; + this._translation = data.t; + this._rotation = _.flatten(data.r); + this._scaleFactor = data.s; + + /** + * Time the hand has been visible in seconds. + * + * @member Hand.prototype.timeVisible {float} + */ + this.timeVisible = data.timeVisible; + + /** + * The palm position with stabalization + * @member stabilizedPalmPosition + * @memberof Leap.Hand.prototype + * @type {number[]} + */ + this.stabilizedPalmPosition = data.stabilizedPalmPosition; +} + +/** + * The finger with the specified ID attached to this hand. + * + * Use this function to retrieve a Pointable object representing a finger + * attached to this hand using an ID value obtained from a previous frame. + * This function always returns a Pointable object, but if no finger + * with the specified ID is present, an invalid Pointable object is returned. + * + * Note that the ID values assigned to fingers persist across frames, but only + * until tracking of a particular finger is lost. If tracking of a finger is + * lost and subsequently regained, the new Finger object representing that + * finger may have a different ID than that representing the finger in an + * earlier frame. + * + * @method finger + * @memberof Leap.Hand.prototype + * @param {String} id The ID value of a finger from a previous frame. + * @returns {Leap.Pointable} The Finger object with + * the matching ID if one exists for this hand in this frame; otherwise, an + * invalid Finger object is returned. + */ +Hand.prototype.finger = function(id) { + var finger = this.frame.finger(id); + return (finger && finger.handId == this.id) ? finger : Pointable.Invalid; +} + +/** + * The angle of rotation around the rotation axis derived from the change in + * orientation of this hand, and any associated fingers and tools, between the + * current frame and the specified frame. + * + * The returned angle is expressed in radians measured clockwise around the + * rotation axis (using the right-hand rule) between the start and end frames. + * The value is always between 0 and pi radians (0 and 180 degrees). + * + * If a corresponding Hand object is not found in sinceFrame, or if either + * this frame or sinceFrame are invalid Frame objects, then the angle of rotation is zero. + * + * @method rotationAngle + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @param {numnber[]} [axis] The axis to measure rotation around. + * @returns {number} A positive value representing the heuristically determined + * rotational change of the hand between the current frame and that specified in + * the sinceFrame parameter. + */ +Hand.prototype.rotationAngle = function(sinceFrame, axis) { + if (!this.valid || !sinceFrame.valid) return 0.0; + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return 0.0; + var rot = this.rotationMatrix(sinceFrame); + var cs = (rot[0] + rot[4] + rot[8] - 1.0)*0.5 + var angle = Math.acos(cs); + angle = isNaN(angle) ? 0.0 : angle; + if (axis !== undefined) { + var rotAxis = this.rotationAxis(sinceFrame); + angle *= vec3.dot(rotAxis, vec3.normalize(vec3.create(), axis)); + } + return angle; +} + +/** + * The axis of rotation derived from the change in orientation of this hand, and + * any associated fingers and tools, between the current frame and the specified frame. + * + * The returned direction vector is normalized. + * + * If a corresponding Hand object is not found in sinceFrame, or if either + * this frame or sinceFrame are invalid Frame objects, then this method returns a zero vector. + * + * @method rotationAxis + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A normalized direction Vector representing the axis of the heuristically determined + * rotational change of the hand between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.rotationAxis = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + var sinceHand = sinceFrame.hand(this.id); + if (!sinceHand.valid) return vec3.create(); + return vec3.normalize(vec3.create(), [ + this._rotation[7] - sinceHand._rotation[5], + this._rotation[2] - sinceHand._rotation[6], + this._rotation[3] - sinceHand._rotation[1] + ]); +} + +/** + * The transform matrix expressing the rotation derived from the change in + * orientation of this hand, and any associated fingers and tools, between + * the current frame and the specified frame. + * + * If a corresponding Hand object is not found in sinceFrame, or if either + * this frame or sinceFrame are invalid Frame objects, then this method returns + * an identity matrix. + * + * @method rotationMatrix + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative rotation. + * @returns {number[]} A transformation Matrix containing the heuristically determined + * rotational change of the hand between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.rotationMatrix = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return mat3.create(); + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return mat3.create(); + var transpose = mat3.transpose(mat3.create(), this._rotation); + var m = mat3.multiply(mat3.create(), sinceHand._rotation, transpose); + return m; +} + +/** + * The scale factor derived from the hand's motion between the current frame and the specified frame. + * + * The scale factor is always positive. A value of 1.0 indicates no scaling took place. + * Values between 0.0 and 1.0 indicate contraction and values greater than 1.0 indicate expansion. + * + * The Leap derives scaling from the relative inward or outward motion of a hand + * and its associated fingers and tools (independent of translation and rotation). + * + * If a corresponding Hand object is not found in sinceFrame, or if either this frame or sinceFrame + * are invalid Frame objects, then this method returns 1.0. + * + * @method scaleFactor + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative scaling. + * @returns {number} A positive value representing the heuristically determined + * scaling change ratio of the hand between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.scaleFactor = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return 1.0; + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return 1.0; + + return Math.exp(this._scaleFactor - sinceHand._scaleFactor); +} + +/** + * The change of position of this hand between the current frame and the specified frame + * + * The returned translation vector provides the magnitude and direction of the + * movement in millimeters. + * + * If a corresponding Hand object is not found in sinceFrame, or if either this frame or + * sinceFrame are invalid Frame objects, then this method returns a zero vector. + * + * @method translation + * @memberof Leap.Hand.prototype + * @param {Leap.Frame} sinceFrame The starting frame for computing the relative translation. + * @returns {number[]} A Vector representing the heuristically determined change in hand + * position between the current frame and that specified in the sinceFrame parameter. + */ +Hand.prototype.translation = function(sinceFrame) { + if (!this.valid || !sinceFrame.valid) return vec3.create(); + var sinceHand = sinceFrame.hand(this.id); + if(!sinceHand.valid) return vec3.create(); + return [ + this._translation[0] - sinceHand._translation[0], + this._translation[1] - sinceHand._translation[1], + this._translation[2] - sinceHand._translation[2] + ]; +} + +/** + * A string containing a brief, human readable description of the Hand object. + * @method toString + * @memberof Leap.Hand.prototype + * @returns {String} A description of the Hand as a string. + */ +Hand.prototype.toString = function() { + return "Hand [ id: "+ this.id + " | palm velocity:"+this.palmVelocity+" | sphere center:"+this.sphereCenter+" ] "; +} + +/** + * The pitch angle in radians. + * + * Pitch is the angle between the negative z-axis and the projection of + * the vector onto the y-z plane. In other words, pitch represents rotation + * around the x-axis. + * If the vector points upward, the returned angle is between 0 and pi radians + * (180 degrees); if it points downward, the angle is between 0 and -pi radians. + * + * @method pitch + * @memberof Leap.Hand.prototype + * @returns {number} The angle of this vector above or below the horizon (x-z plane). + * + */ +Hand.prototype.pitch = function() { + return Math.atan2(this.direction[1], -this.direction[2]); +} + +/** + * The yaw angle in radians. + * + * Yaw is the angle between the negative z-axis and the projection of + * the vector onto the x-z plane. In other words, yaw represents rotation + * around the y-axis. If the vector points to the right of the negative z-axis, + * then the returned angle is between 0 and pi radians (180 degrees); + * if it points to the left, the angle is between 0 and -pi radians. + * + * @method yaw + * @memberof Leap.Hand.prototype + * @returns {number} The angle of this vector to the right or left of the y-axis. + * + */ +Hand.prototype.yaw = function() { + return Math.atan2(this.direction[0], -this.direction[2]); +} + +/** + * The roll angle in radians. + * + * Roll is the angle between the y-axis and the projection of + * the vector onto the x-y plane. In other words, roll represents rotation + * around the z-axis. If the vector points to the left of the y-axis, + * then the returned angle is between 0 and pi radians (180 degrees); + * if it points to the right, the angle is between 0 and -pi radians. + * + * @method roll + * @memberof Leap.Hand.prototype + * @returns {number} The angle of this vector to the right or left of the y-axis. + * + */ +Hand.prototype.roll = function() { + return Math.atan2(this.palmNormal[0], -this.palmNormal[1]); +} + +/** + * An invalid Hand object. + * + * You can use an invalid Hand object in comparisons testing + * whether a given Hand instance is valid or invalid. (You can also use the + * Hand valid property.) + * + * @static + * @type {Leap.Hand} + * @name Invalid + * @memberof Leap.Hand + */ +Hand.Invalid = { + valid: false, + fingers: [], + tools: [], + pointables: [], + pointable: function() { return Pointable.Invalid }, + finger: function() { return Pointable.Invalid }, + toString: function() { return "invalid frame" }, + dump: function() { return this.toString(); }, + rotationAngle: function() { return 0.0; }, + rotationMatrix: function() { return mat3.create(); }, + rotationAxis: function() { return vec3.create(); }, + scaleFactor: function() { return 1.0; }, + translation: function() { return vec3.create(); } +}; + +},{"./pointable":11,"gl-matrix":19,"underscore":20}],8:[function(require,module,exports){ +(function(){/** + * Leap is the global namespace of the Leap API. + * @namespace Leap + */ +module.exports = { + Controller: require("./controller"), + Frame: require("./frame"), + Gesture: require("./gesture"), + Hand: require("./hand"), + Pointable: require("./pointable"), + InteractionBox: require("./interaction_box"), + Connection: require("./connection"), + CircularBuffer: require("./circular_buffer"), + UI: require("./ui"), + glMatrix: require("gl-matrix"), + mat3: require("gl-matrix").mat3, + vec3: require("gl-matrix").vec3, + loopController: undefined, + /** + * The Leap.loop() function passes a frame of Leap data to your + * callback function and then calls window.requestAnimationFrame() after + * executing your callback function. + * + * Leap.loop() sets up the Leap controller and WebSocket connection for you. + * You do not need to create your own controller when using this method. + * + * Your callback function is called on an interval determined by the client + * browser. Typically, this is on an interval of 60 frames/second. The most + * recent frame of Leap data is passed to your callback function. If the Leap + * is producing frames at a slower rate than the browser frame rate, the same + * frame of Leap data can be passed to your function in successive animation + * updates. + * + * As an alternative, you can create your own Controller object and use a + * {@link Controller#onFrame onFrame} callback to process the data at + * the frame rate of the Leap device. See {@link Controller} for an + * example. + * + * @method Leap.loop + * @param {function} callback A function called when the browser is ready to + * draw to the screen. The most recent {@link Frame} object is passed to + * your callback function. + * + * ```javascript + * Leap.loop( function( frame ) { + * // ... your code here + * }) + * ``` + */ + loop: function(opts, callback) { + if (callback === undefined) { + callback = opts; + opts = {}; + } + if (!this.loopController) this.loopController = new this.Controller(opts); + this.loopController.loop(callback); + return this.loopController; + } +} + +})() +},{"./circular_buffer":2,"./connection":3,"./controller":4,"./frame":5,"./gesture":6,"./hand":7,"./interaction_box":9,"./pointable":11,"./ui":13,"gl-matrix":19}],9:[function(require,module,exports){ +var glMatrix = require("gl-matrix") + , vec3 = glMatrix.vec3; + +/** + * Constructs a InteractionBox object. + * + * @class InteractionBox + * @memberof Leap + * @classdesc + * The InteractionBox class represents a box-shaped region completely within + * the field of view of the Leap Motion controller. + * + * The interaction box is an axis-aligned rectangular prism and provides + * normalized coordinates for hands, fingers, and tools within this box. + * The InteractionBox class can make it easier to map positions in the + * Leap Motion coordinate system to 2D or 3D coordinate systems used + * for application drawing. + * + * The InteractionBox region is defined by a center and dimensions along the x, y, and z axes. + */ +var InteractionBox = module.exports = function(data) { + /** + * Indicates whether this is a valid InteractionBox object. + * + * @member valid + * @type {Boolean} + * @memberof Leap.InteractionBox.prototype + */ + this.valid = true; + /** + * The center of the InteractionBox in device coordinates (millimeters). + * This point is equidistant from all sides of the box. + * + * @member center + * @type {number[]} + * @memberof Leap.InteractionBox.prototype + */ + this.center = data.center; + + this.size = data.size; + /** + * The width of the InteractionBox in millimeters, measured along the x-axis. + * + * @member width + * @type {number} + * @memberof Leap.InteractionBox.prototype + */ + this.width = data.size[0]; + /** + * The height of the InteractionBox in millimeters, measured along the y-axis. + * + * @member height + * @type {number} + * @memberof Leap.InteractionBox.prototype + */ + this.height = data.size[1]; + /** + * The depth of the InteractionBox in millimeters, measured along the z-axis. + * + * @member depth + * @type {number} + * @memberof Leap.InteractionBox.prototype + */ + this.depth = data.size[2]; +} + +/** + * Converts a position defined by normalized InteractionBox coordinates + * into device coordinates in millimeters. + * + * This function performs the inverse of normalizePoint(). + * + * @method denormalizePoint + * @memberof Leap.InteractionBox.prototype + * @param {Leap.Vector} normalizedPosition The input position in InteractionBox coordinates. + * @returns {Leap.Vector} The corresponding denormalized position in device coordinates. + */ +InteractionBox.prototype.denormalizePoint = function(normalizedPosition) { + return vec3.fromValues( + (normalizedPosition[0] - 0.5) * this.size[0] + this.center[0], + (normalizedPosition[1] - 0.5) * this.size[1] + this.center[1], + (normalizedPosition[2] - 0.5) * this.size[2] + this.center[2] + ); +} + +/** + * Normalizes the coordinates of a point using the interaction box. + * + * Coordinates from the Leap Motion frame of reference (millimeters) are + * converted to a range of [0..1] such that the minimum value of the + * InteractionBox maps to 0 and the maximum value of the InteractionBox maps to 1. + * + * @method normalizePoint + * @memberof Leap.InteractionBox.prototype + * @param {Leap.Vector} position The input position in device coordinates. + * @param {Boolean} clamp Whether or not to limit the output value to the range [0,1] + * when the input position is outside the InteractionBox. Defaults to true. + * @returns {Leap.Vector} The normalized position. + */ +InteractionBox.prototype.normalizePoint = function(position, clamp) { + var vec = vec3.fromValues( + ((position[0] - this.center[0]) / this.size[0]) + 0.5, + ((position[1] - this.center[1]) / this.size[1]) + 0.5, + ((position[2] - this.center[2]) / this.size[2]) + 0.5 + ); + + if (clamp) { + vec[0] = Math.min(Math.max(vec[0], 0), 1); + vec[1] = Math.min(Math.max(vec[1], 0), 1); + vec[2] = Math.min(Math.max(vec[2], 0), 1); + } + return vec; +} + +/** + * Writes a brief, human readable description of the InteractionBox object. + * + * @method toString + * @memberof Leap.InteractionBox.prototype + * @returns {String} A description of the InteractionBox object as a string. + */ +InteractionBox.prototype.toString = function() { + return "InteractionBox [ width:" + this.width + " | height:" + this.height + " | depth:" + this.depth + " ]"; +} + +/** + * An invalid InteractionBox object. + * + * You can use this InteractionBox instance in comparisons testing + * whether a given InteractionBox instance is valid or invalid. (You can also use the + * InteractionBox.valid property.) + * + * @static + * @type {Leap.InteractionBox} + * @name Invalid + * @memberof Leap.InteractionBox + */ +InteractionBox.Invalid = { valid: false }; + +},{"gl-matrix":19}],10:[function(require,module,exports){ +var Pipeline = module.exports = function() { + this.steps = []; +} + +Pipeline.prototype.addStep = function(step) { + this.steps.push(step); +} + +Pipeline.prototype.run = function(frame) { + var stepsLength = this.steps.length; + for (var i = 0; i != stepsLength; i++) { + if (!frame) break; + frame = this.steps[i](frame); + } + return frame; +} + +},{}],11:[function(require,module,exports){ +var glMatrix = require("gl-matrix") + , vec3 = glMatrix.vec3; + +/** + * Constructs a Pointable object. + * + * An uninitialized pointable is considered invalid. + * Get valid Pointable objects from a Frame or a Hand object. + * + * @class Pointable + * @memberof Leap + * @classdesc + * The Pointable class reports the physical characteristics of a detected + * finger or tool. + * + * Both fingers and tools are classified as Pointable objects. Use the + * Pointable.tool property to determine whether a Pointable object represents a + * tool or finger. The Leap classifies a detected entity as a tool when it is + * thinner, straighter, and longer than a typical finger. + * + * Note that Pointable objects can be invalid, which means that they do not + * contain valid tracking data and do not correspond to a physical entity. + * Invalid Pointable objects can be the result of asking for a Pointable object + * using an ID from an earlier frame when no Pointable objects with that ID + * exist in the current frame. A Pointable object created from the Pointable + * constructor is also invalid. Test for validity with the Pointable.valid + * property. + */ +var Pointable = module.exports = function(data) { + /** + * Indicates whether this is a valid Pointable object. + * + * @member valid + * @type {Boolean} + * @memberof Leap.Pointable.prototype + */ + this.valid = true; + /** + * A unique ID assigned to this Pointable object, whose value remains the + * same across consecutive frames while the tracked finger or tool remains + * visible. If tracking is lost (for example, when a finger is occluded by + * another finger or when it is withdrawn from the Leap field of view), the + * Leap may assign a new ID when it detects the entity in a future frame. + * + * Use the ID value with the pointable() functions defined for the + * {@link Frame} and {@link Frame.Hand} classes to find this + * Pointable object in future frames. + * + * @member id + * @type {String} + * @memberof Leap.Pointable.prototype + */ + this.id = data.id; + this.handId = data.handId; + /** + * The estimated length of the finger or tool in millimeters. + * + * The reported length is the visible length of the finger or tool from the + * hand to tip. If the length isn't known, then a value of 0 is returned. + * + * @member length + * @type {number} + * @memberof Leap.Pointable.prototype + */ + this.length = data.length; + /** + * Whether or not the Pointable is believed to be a tool. + * Tools are generally longer, thinner, and straighter than fingers. + * + * If tool is false, then this Pointable must be a finger. + * + * @member tool + * @type {Boolean} + * @memberof Leap.Pointable.prototype + */ + this.tool = data.tool; + /** + * The estimated width of the tool in millimeters. + * + * The reported width is the average width of the visible portion of the + * tool from the hand to the tip. If the width isn't known, + * then a value of 0 is returned. + * + * Pointable objects representing fingers do not have a width property. + * + * @member width + * @type {number} + * @memberof Leap.Pointable.prototype + */ + this.width = data.width; + /** + * The direction in which this finger or tool is pointing. + * + * The direction is expressed as a unit vector pointing in the same + * direction as the tip. + * + * ![Finger](images/Leap_Finger_Model.png) + * @member direction + * @type {number[]} + * @memberof Leap.Pointable.prototype + */ + this.direction = data.direction; + /** + * The tip position in millimeters from the Leap origin. + * Stabilized + * + * @member stabilizedTipPosition + * @type {Leap.Vector} + * @memberof Leap.Pointable.prototype + */ + this.stabilizedTipPosition = data.stabilizedTipPosition; + /** + * The tip position in millimeters from the Leap origin. + * + * @member tipPosition + * @type {number[]} + * @memberof Leap.Pointable.prototype + */ + this.tipPosition = data.tipPosition; + /** + * The rate of change of the tip position in millimeters/second. + * + * @member tipVelocity + * @type {number[]} + * @memberof Leap.Pointable.prototype + */ + this.tipVelocity = data.tipVelocity; + /** + * Human readable string describing the 'Touch Zone' of this pointable + * + * @member Pointable.prototype.touchZone {String} + */ + this.touchZone = data.touchZone; + /** + * Distance from 'Touch Plane' + * + * @member Pointable.prototype.touchDistance {number} + */ + this.touchDistance = data.touchDistance; + + /** + * Time the pointable has been visible in seconds. + * + * @member Pointable.prototype.timeVisible {float} + */ + this.timeVisible = data.timeVisible; +} + +/** + * A string containing a brief, human readable description of the Pointable + * object. + * + * @method toString + * @memberof Leap.Pointable.prototype + * @returns {String} A description of the Pointable object as a string. + */ +Pointable.prototype.toString = function() { + if(this.tool == true){ + return "Pointable [ id:" + this.id + " " + this.length + "mmx | with:" + this.width + "mm | direction:" + this.direction + ' ]'; + } else { + return "Pointable [ id:" + this.id + " " + this.length + "mmx | direction: " + this.direction + ' ]'; + } +} + +/** + * An invalid Pointable object. + * + * You can use this Pointable instance in comparisons testing + * whether a given Pointable instance is valid or invalid. (You can also use the + * Pointable.valid property.) + + * @static + * @type {Leap.Pointable} + * @name Invalid + * @memberof Leap.Pointable + */ +Pointable.Invalid = { valid: false }; + +},{"gl-matrix":19}],12:[function(require,module,exports){ +var Frame = require('./frame') + +var Event = function(data) { + this.type = data.type; + this.state = data.state; +}; + +var chooseProtocol = exports.chooseProtocol = function(header) { + var protocol; + switch(header.version) { + case 1: + protocol = JSONProtocol(1, function(data) { + return new Frame(data); + }); + break; + case 2: + protocol = JSONProtocol(2, function(data) { + return new Frame(data); + }); + protocol.sendHeartbeat = function(connection) { + connection.send(protocol.encode({heartbeat: true})); + } + break; + case 3: + protocol = JSONProtocol(3, function(data) { + return data.event ? new Event(data.event) : new Frame(data); + + }); + protocol.sendHeartbeat = function(connection) { + connection.send(protocol.encode({heartbeat: true})); + } + break; + default: + throw "unrecognized version"; + } + return protocol; +} + +var JSONProtocol = function(version, cb) { + var protocol = cb; + protocol.encode = function(message) { + return JSON.stringify(message); + } + protocol.version = version; + protocol.versionLong = 'Version ' + version; + protocol.type = 'protocol'; + return protocol; +}; + +},{"./frame":5}],13:[function(require,module,exports){ +exports.UI = { + Region: require("./ui/region"), + Cursor: require("./ui/cursor") +}; +},{"./ui/cursor":14,"./ui/region":15}],14:[function(require,module,exports){ +var Cursor = module.exports = function() { + return function(frame) { + var pointable = frame.pointables.sort(function(a, b) { return a.z - b.z })[0] + if (pointable && pointable.valid) { + frame.cursorPosition = pointable.tipPosition + } + return frame + } +} + +},{}],15:[function(require,module,exports){ +var EventEmitter = require('events').EventEmitter + , _ = require('underscore') + +var Region = module.exports = function(start, end) { + this.start = new Vector(start) + this.end = new Vector(end) + this.enteredFrame = null +} + +Region.prototype.hasPointables = function(frame) { + for (var i = 0; i != frame.pointables.length; i++) { + var position = frame.pointables[i].tipPosition + if (position.x >= this.start.x && position.x <= this.end.x && position.y >= this.start.y && position.y <= this.end.y && position.z >= this.start.z && position.z <= this.end.z) { + return true + } + } + return false +} + +Region.prototype.listener = function(opts) { + var region = this + if (opts && opts.nearThreshold) this.setupNearRegion(opts.nearThreshold) + return function(frame) { + return region.updatePosition(frame) + } +} + +Region.prototype.clipper = function() { + var region = this + return function(frame) { + region.updatePosition(frame) + return region.enteredFrame ? frame : null + } +} + +Region.prototype.setupNearRegion = function(distance) { + var nearRegion = this.nearRegion = new Region( + [this.start.x - distance, this.start.y - distance, this.start.z - distance], + [this.end.x + distance, this.end.y + distance, this.end.z + distance] + ) + var region = this + nearRegion.on("enter", function(frame) { + region.emit("near", frame) + }) + nearRegion.on("exit", function(frame) { + region.emit("far", frame) + }) + region.on('exit', function(frame) { + region.emit("near", frame) + }) +} + +Region.prototype.updatePosition = function(frame) { + if (this.nearRegion) this.nearRegion.updatePosition(frame) + if (this.hasPointables(frame) && this.enteredFrame == null) { + this.enteredFrame = frame + this.emit("enter", this.enteredFrame) + } else if (!this.hasPointables(frame) && this.enteredFrame != null) { + this.enteredFrame = null + this.emit("exit", this.enteredFrame) + } + return frame +} + +Region.prototype.normalize = function(position) { + return new Vector([ + (position.x - this.start.x) / (this.end.x - this.start.x), + (position.y - this.start.y) / (this.end.y - this.start.y), + (position.z - this.start.z) / (this.end.z - this.start.z) + ]) +} + +Region.prototype.mapToXY = function(position, width, height) { + var normalized = this.normalize(position) + var x = normalized.x, y = normalized.y + if (x > 1) x = 1 + else if (x < -1) x = -1 + if (y > 1) y = 1 + else if (y < -1) y = -1 + return [ + (x + 1) / 2 * width, + (1 - y) / 2 * height, + normalized.z + ] +} + +_.extend(Region.prototype, EventEmitter.prototype) +},{"events":17,"underscore":20}],16:[function(require,module,exports){ + +},{}],17:[function(require,module,exports){ +(function(process){if (!process.EventEmitter) process.EventEmitter = function () {}; + +var EventEmitter = exports.EventEmitter = process.EventEmitter; +var isArray = typeof Array.isArray === 'function' + ? Array.isArray + : function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]' + } +; +function indexOf (xs, x) { + if (xs.indexOf) return xs.indexOf(x); + for (var i = 0; i < xs.length; i++) { + if (x === xs[i]) return i; + } + return -1; +} + +// By default EventEmitters will print a warning if more than +// 10 listeners are added to it. This is a useful default which +// helps finding memory leaks. +// +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +var defaultMaxListeners = 10; +EventEmitter.prototype.setMaxListeners = function(n) { + if (!this._events) this._events = {}; + this._events.maxListeners = n; +}; + + +EventEmitter.prototype.emit = function(type) { + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events || !this._events.error || + (isArray(this._events.error) && !this._events.error.length)) + { + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } + } + + if (!this._events) return false; + var handler = this._events[type]; + if (!handler) return false; + + if (typeof handler == 'function') { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + var args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); + } + return true; + + } else if (isArray(handler)) { + var args = Array.prototype.slice.call(arguments, 1); + + var listeners = handler.slice(); + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + return true; + + } else { + return false; + } +}; + +// EventEmitter is defined in src/node_events.cc +// EventEmitter.prototype.emit() is also defined there. +EventEmitter.prototype.addListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('addListener only takes instances of Function'); + } + + if (!this._events) this._events = {}; + + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + this.emit('newListener', type, listener); + + if (!this._events[type]) { + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + } else if (isArray(this._events[type])) { + + // Check for listener leak + if (!this._events[type].warned) { + var m; + if (this._events.maxListeners !== undefined) { + m = this._events.maxListeners; + } else { + m = defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + console.trace(); + } + } + + // If we've already got an array, just append. + this._events[type].push(listener); + } else { + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + var self = this; + self.on(type, function g() { + self.removeListener(type, g); + listener.apply(this, arguments); + }); + + return this; +}; + +EventEmitter.prototype.removeListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('removeListener only takes instances of Function'); + } + + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events || !this._events[type]) return this; + + var list = this._events[type]; + + if (isArray(list)) { + var i = indexOf(list, listener); + if (i < 0) return this; + list.splice(i, 1); + if (list.length == 0) + delete this._events[type]; + } else if (this._events[type] === listener) { + delete this._events[type]; + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + if (arguments.length === 0) { + this._events = {}; + return this; + } + + // does not use listeners(), so no side effect of creating _events[type] + if (type && this._events && this._events[type]) this._events[type] = null; + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + if (!this._events) this._events = {}; + if (!this._events[type]) this._events[type] = []; + if (!isArray(this._events[type])) { + this._events[type] = [this._events[type]]; + } + return this._events[type]; +}; + +})(require("__browserify_process")) +},{"__browserify_process":18}],18:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; + +process.nextTick = (function () { + var canSetImmediate = typeof window !== 'undefined' + && window.setImmediate; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canSetImmediate) { + return function (f) { return window.setImmediate(f) }; + } + + if (canPost) { + var queue = []; + window.addEventListener('message', function (ev) { + if (ev.source === window && ev.data === 'process-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +} + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +},{}],19:[function(require,module,exports){ +(function(){/** + * @fileoverview gl-matrix - High performance matrix and vector operations + * @author Brandon Jones + * @author Colin MacKenzie IV + * @version 2.0.0 + */ + +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +(function() { + "use strict"; + + var shim = {}; + if (typeof(exports) === 'undefined') { + if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + shim.exports = {}; + define(function() { + return shim.exports; + }); + } else { + // gl-matrix lives in a browser, define its namespaces in global + shim.exports = window; + } + } + else { + // gl-matrix lives in commonjs, define its namespaces in exports + shim.exports = exports; + } + + (function(exports) { + /* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 2 Dimensional Vector + * @name vec2 + */ + +var vec2 = {}; + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new, empty vec2 + * + * @returns {vec2} a new 2D vector + */ +vec2.create = function() { + return new Float32Array(2); +}; + +/** + * Creates a new vec2 initialized with values from an existing vector + * + * @param {vec2} a vector to clone + * @returns {vec2} a new 2D vector + */ +vec2.clone = function(a) { + var out = new Float32Array(2); + out[0] = a[0]; + out[1] = a[1]; + return out; +}; + +/** + * Creates a new vec2 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} a new 2D vector + */ +vec2.fromValues = function(x, y) { + var out = new Float32Array(2); + out[0] = x; + out[1] = y; + return out; +}; + +/** + * Copy the values from one vec2 to another + * + * @param {vec2} out the receiving vector + * @param {vec2} a the source vector + * @returns {vec2} out + */ +vec2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + return out; +}; + +/** + * Set the components of a vec2 to the given values + * + * @param {vec2} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} out + */ +vec2.set = function(out, x, y) { + out[0] = x; + out[1] = y; + return out; +}; + +/** + * Adds two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + return out; +}; + +/** + * Subtracts two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.sub = vec2.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + return out; +}; + +/** + * Multiplies two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.mul = vec2.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + return out; +}; + +/** + * Divides two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.div = vec2.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + return out; +}; + +/** + * Returns the minimum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + return out; +}; + +/** + * Returns the maximum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ +vec2.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + return out; +}; + +/** + * Scales a vec2 by a scalar number + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to scale + * @param {vec2} b amount to scale the vector by + * @returns {vec2} out + */ +vec2.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + return out; +}; + +/** + * Calculates the euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} distance between a and b + */ +vec2.dist = vec2.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return Math.sqrt(x*x + y*y); +}; + +/** + * Calculates the squared euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} squared distance between a and b + */ +vec2.sqrDist = vec2.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return x*x + y*y; +}; + +/** + * Caclulates the length of a vec2 + * + * @param {vec2} a vector to calculate length of + * @returns {Number} length of a + */ +vec2.len = vec2.length = function (a) { + var x = a[0], + y = a[1]; + return Math.sqrt(x*x + y*y); +}; + +/** + * Caclulates the squared length of a vec2 + * + * @param {vec2} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec2.sqrLen = vec2.squaredLength = function (a) { + var x = a[0], + y = a[1]; + return x*x + y*y; +}; + +/** + * Negates the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to negate + * @returns {vec2} out + */ +vec2.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + return out; +}; + +/** + * Normalize a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to normalize + * @returns {vec2} out + */ +vec2.normalize = function(out, a) { + var x = a[0], + y = a[1]; + var len = x*x + y*y; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + } + return out; +}; + +/** + * Caclulates the dot product of two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} dot product of a and b + */ +vec2.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1]; +}; + +/** + * Computes the cross product of two vec2's + * Note that the cross product must by definition produce a 3D vector + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec3} out + */ +vec2.cross = function(out, a, b) { + var z = a[0] * b[1] - a[1] * b[0]; + out[0] = out[1] = 0; + out[2] = z; + return out; +}; + +/** + * Performs a linear interpolation between two vec2's + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec2} out + */ +vec2.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + return out; +}; + +/** + * Transforms the vec2 with a mat2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat2} m matrix to transform with + * @returns {vec2} out + */ +vec2.transformMat2 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = x * m[0] + y * m[1]; + out[1] = x * m[2] + y * m[3]; + return out; +}; + +/** + * Perform some operation over an array of vec2s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + */ +vec2.forEach = (function() { + var vec = new Float32Array(2); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 2; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec2} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec2.str = function (a) { + return 'vec2(' + a[0] + ', ' + a[1] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec2 = vec2; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 3 Dimensional Vector + * @name vec3 + */ + +var vec3 = {}; + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new, empty vec3 + * + * @returns {vec3} a new 3D vector + */ +vec3.create = function() { + return new Float32Array(3); +}; + +/** + * Creates a new vec3 initialized with values from an existing vector + * + * @param {vec3} a vector to clone + * @returns {vec3} a new 3D vector + */ +vec3.clone = function(a) { + var out = new Float32Array(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Creates a new vec3 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} a new 3D vector + */ +vec3.fromValues = function(x, y, z) { + var out = new Float32Array(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Copy the values from one vec3 to another + * + * @param {vec3} out the receiving vector + * @param {vec3} a the source vector + * @returns {vec3} out + */ +vec3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Set the components of a vec3 to the given values + * + * @param {vec3} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} out + */ +vec3.set = function(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Adds two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; +}; + +/** + * Subtracts two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.sub = vec3.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; +}; + +/** + * Multiplies two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.mul = vec3.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; +}; + +/** + * Divides two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.div = vec3.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; +}; + +/** + * Returns the minimum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; +}; + +/** + * Returns the maximum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; +}; + +/** + * Scales a vec3 by a scalar number + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to scale + * @param {vec3} b amount to scale the vector by + * @returns {vec3} out + */ +vec3.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; +}; + +/** + * Calculates the euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} distance between a and b + */ +vec3.dist = vec3.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Calculates the squared euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} squared distance between a and b + */ +vec3.sqrDist = vec3.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return x*x + y*y + z*z; +}; + +/** + * Caclulates the length of a vec3 + * + * @param {vec3} a vector to calculate length of + * @returns {Number} length of a + */ +vec3.len = vec3.length = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Caclulates the squared length of a vec3 + * + * @param {vec3} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec3.sqrLen = vec3.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return x*x + y*y + z*z; +}; + +/** + * Negates the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to negate + * @returns {vec3} out + */ +vec3.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; +}; + +/** + * Normalize a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to normalize + * @returns {vec3} out + */ +vec3.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2]; + var len = x*x + y*y + z*z; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +}; + +/** + * Caclulates the dot product of two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} dot product of a and b + */ +vec3.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +}; + +/** + * Computes the cross product of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.cross = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], + bx = b[0], by = b[1], bz = b[2]; + + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; +}; + +/** + * Performs a linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ +vec3.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; +}; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +vec3.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; + return out; +}; + +/** + * Transforms the vec3 with a quat + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec3} out + */ +vec3.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec3s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + */ +vec3.forEach = (function() { + var vec = new Float32Array(3); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 3; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec3} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec3.str = function (a) { + return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec3 = vec3; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 4 Dimensional Vector + * @name vec4 + */ + +var vec4 = {}; + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new, empty vec4 + * + * @returns {vec4} a new 4D vector + */ +vec4.create = function() { + return new Float32Array(4); +}; + +/** + * Creates a new vec4 initialized with values from an existing vector + * + * @param {vec4} a vector to clone + * @returns {vec4} a new 4D vector + */ +vec4.clone = function(a) { + var out = new Float32Array(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Creates a new vec4 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} a new 4D vector + */ +vec4.fromValues = function(x, y, z, w) { + var out = new Float32Array(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Copy the values from one vec4 to another + * + * @param {vec4} out the receiving vector + * @param {vec4} a the source vector + * @returns {vec4} out + */ +vec4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Set the components of a vec4 to the given values + * + * @param {vec4} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} out + */ +vec4.set = function(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Adds two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; +}; + +/** + * Subtracts two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.sub = vec4.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; +}; + +/** + * Multiplies two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.mul = vec4.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + out[3] = a[3] * b[3]; + return out; +}; + +/** + * Divides two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.div = vec4.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + out[3] = a[3] / b[3]; + return out; +}; + +/** + * Returns the minimum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + out[3] = Math.min(a[3], b[3]); + return out; +}; + +/** + * Returns the maximum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + out[3] = Math.max(a[3], b[3]); + return out; +}; + +/** + * Scales a vec4 by a scalar number + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to scale + * @param {vec4} b amount to scale the vector by + * @returns {vec4} out + */ +vec4.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; +}; + +/** + * Calculates the euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} distance between a and b + */ +vec4.dist = vec4.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Calculates the squared euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} squared distance between a and b + */ +vec4.sqrDist = vec4.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Caclulates the length of a vec4 + * + * @param {vec4} a vector to calculate length of + * @returns {Number} length of a + */ +vec4.len = vec4.length = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Caclulates the squared length of a vec4 + * + * @param {vec4} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec4.sqrLen = vec4.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Negates the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to negate + * @returns {vec4} out + */ +vec4.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = -a[3]; + return out; +}; + +/** + * Normalize a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to normalize + * @returns {vec4} out + */ +vec4.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var len = x*x + y*y + z*z + w*w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + out[3] = a[3] * len; + } + return out; +}; + +/** + * Caclulates the dot product of two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} dot product of a and b + */ +vec4.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +}; + +/** + * Performs a linear interpolation between two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec4} out + */ +vec4.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; +}; + +/** + * Transforms the vec4 with a mat4. + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec4} out + */ +vec4.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; +}; + +/** + * Transforms the vec4 with a quat + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec4} out + */ +vec4.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec4s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + */ +vec4.forEach = (function() { + var vec = new Float32Array(4); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 4; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec4} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec4.str = function (a) { + return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec4 = vec4; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 2x2 Matrix + * @name mat2 + */ + +var mat2 = {}; + +var mat2Identity = new Float32Array([ + 1, 0, + 0, 1 +]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity mat2 + * + * @returns {mat2} a new 2x2 matrix + */ +mat2.create = function() { + return new Float32Array(mat2Identity); +}; + +/** + * Creates a new mat2 initialized with values from an existing matrix + * + * @param {mat2} a matrix to clone + * @returns {mat2} a new 2x2 matrix + */ +mat2.clone = function(a) { + var out = new Float32Array(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Copy the values from one mat2 to another + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Set a mat2 to the identity matrix + * + * @param {mat2} out the receiving matrix + * @returns {mat2} out + */ +mat2.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Transpose the values of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a1 = a[1]; + out[1] = a[2]; + out[2] = a1; + } else { + out[0] = a[0]; + out[1] = a[2]; + out[2] = a[1]; + out[3] = a[3]; + } + + return out; +}; + +/** + * Inverts a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + + // Calculate the determinant + det = a0 * a3 - a2 * a1; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = a3 * det; + out[1] = -a1 * det; + out[2] = -a2 * det; + out[3] = a0 * det; + + return out; +}; + +/** + * Caclulates the adjugate of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ +mat2.adjoint = function(out, a) { + // Caching this value is nessecary if out == a + var a0 = a[0]; + out[0] = a[3]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a0; + + return out; +}; + +/** + * Calculates the determinant of a mat2 + * + * @param {mat2} a the source matrix + * @returns {Number} determinant of a + */ +mat2.determinant = function (a) { + return a[0] * a[3] - a[2] * a[1]; +}; + +/** + * Multiplies two mat2's + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the first operand + * @param {mat2} b the second operand + * @returns {mat2} out + */ +mat2.mul = mat2.multiply = function (out, a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = a0 * b0 + a1 * b2; + out[1] = a0 * b1 + a1 * b3; + out[2] = a2 * b0 + a3 * b2; + out[3] = a2 * b1 + a3 * b3; + return out; +}; + +/** + * Rotates a mat2 by the given angle + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {mat2} rad the angle to rotate the matrix by + * @returns {mat2} out + */ +mat2.rotate = function (out, a, rad) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + s = Math.sin(rad), + c = Math.cos(rad); + out[0] = a0 * c + a1 * s; + out[1] = a0 * -s + a1 * c; + out[2] = a2 * c + a3 * s; + out[3] = a2 * -s + a3 * c; + return out; +}; + +/** + * Scales the mat2 by the dimensions in the given vec2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {mat2} v the vec2 to scale the matrix by + * @returns {mat2} out + **/ +mat2.scale = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + v0 = v[0], v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v1; + out[2] = a2 * v0; + out[3] = a3 * v1; + return out; +}; + +/** + * Returns a string representation of a mat2 + * + * @param {mat2} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat2.str = function (a) { + return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat2 = mat2; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 3x3 Matrix + * @name mat3 + */ + +var mat3 = {}; + +var mat3Identity = new Float32Array([ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 +]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity mat3 + * + * @returns {mat3} a new 3x3 matrix + */ +mat3.create = function() { + return new Float32Array(mat3Identity); +}; + +/** + * Creates a new mat3 initialized with values from an existing matrix + * + * @param {mat3} a matrix to clone + * @returns {mat3} a new 3x3 matrix + */ +mat3.clone = function(a) { + var out = new Float32Array(9); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Copy the values from one mat3 to another + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Set a mat3 to the identity matrix + * + * @param {mat3} out the receiving matrix + * @returns {mat3} out + */ +mat3.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; +}; + +/** + * Transpose the values of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; + out[5] = a[7]; + out[6] = a02; + out[7] = a12; + } else { + out[0] = a[0]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a[1]; + out[4] = a[4]; + out[5] = a[7]; + out[6] = a[2]; + out[7] = a[5]; + out[8] = a[8]; + } + + return out; +}; + +/** + * Inverts a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = b01 * det; + out[1] = (-a22 * a01 + a02 * a21) * det; + out[2] = (a12 * a01 - a02 * a11) * det; + out[3] = b11 * det; + out[4] = (a22 * a00 - a02 * a20) * det; + out[5] = (-a12 * a00 + a02 * a10) * det; + out[6] = b21 * det; + out[7] = (-a21 * a00 + a01 * a20) * det; + out[8] = (a11 * a00 - a01 * a10) * det; + return out; +}; + +/** + * Caclulates the adjugate of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + out[0] = (a11 * a22 - a12 * a21); + out[1] = (a02 * a21 - a01 * a22); + out[2] = (a01 * a12 - a02 * a11); + out[3] = (a12 * a20 - a10 * a22); + out[4] = (a00 * a22 - a02 * a20); + out[5] = (a02 * a10 - a00 * a12); + out[6] = (a10 * a21 - a11 * a20); + out[7] = (a01 * a20 - a00 * a21); + out[8] = (a00 * a11 - a01 * a10); + return out; +}; + +/** + * Calculates the determinant of a mat3 + * + * @param {mat3} a the source matrix + * @returns {Number} determinant of a + */ +mat3.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); +}; + +/** + * Multiplies two mat3's + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the first operand + * @param {mat3} b the second operand + * @returns {mat3} out + */ +mat3.mul = mat3.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b00 = b[0], b01 = b[1], b02 = b[2], + b10 = b[3], b11 = b[4], b12 = b[5], + b20 = b[6], b21 = b[7], b22 = b[8]; + + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; +}; + +/** + * Returns a string representation of a mat3 + * + * @param {mat3} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat3.str = function (a) { + return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + + a[6] + ', ' + a[7] + ', ' + a[8] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat3 = mat3; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 4x4 Matrix + * @name mat4 + */ + +var mat4 = {}; + +var mat4Identity = new Float32Array([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ +mat4.create = function() { + return new Float32Array(mat4Identity); +}; + +/** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {mat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ +mat4.clone = function(a) { + var out = new Float32Array(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +mat4.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; + +/** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3], + a12 = a[6], a13 = a[7], + a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; +}; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; + +/** + * Caclulates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); + out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); + return out; +}; + +/** + * Calculates the determinant of a mat4 + * + * @param {mat4} a the source matrix + * @returns {Number} determinant of a + */ +mat4.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; +}; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +mat4.mul = mat4.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; + +/** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to translate + * @param {vec3} v vector to translate by + * @returns {mat4} out + */ +mat4.translate = function (out, a, v) { + var x = v[0], y = v[1], z = v[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; +}; + +/** + * Scales the mat4 by the dimensions in the given vec3 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to scale + * @param {vec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ +mat4.scale = function(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +mat4.rotate = function (out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < GLMAT_EPSILON) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; + +/** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateX = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateY = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateZ = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; +}; + +/** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @returns {mat4} out + */ +mat4.fromRotationTranslation = function (out, q, v) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; +}; + +/** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.frustum = function (out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left), + tb = 1 / (top - bottom), + nf = 1 / (near - far); + out[0] = (near * 2) * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = (near * 2) * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (far * near * 2) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.perspective = function (out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.ortho = function (out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; +}; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +mat4.lookAt = function (out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < GLMAT_EPSILON && + Math.abs(eyey - centery) < GLMAT_EPSILON && + Math.abs(eyez - centerz) < GLMAT_EPSILON) { + return mat4.identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; + +/** + * Returns a string representation of a mat4 + * + * @param {mat4} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat4.str = function (a) { + return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + + a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + + a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + + a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat4 = mat4; +} +; +/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class Quaternion + * @name quat + */ + +var quat = {}; + +var quatIdentity = new Float32Array([0, 0, 0, 1]); + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +/** + * Creates a new identity quat + * + * @returns {quat} a new quaternion + */ +quat.create = function() { + return new Float32Array(quatIdentity); +}; + +/** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {quat} a quaternion to clone + * @returns {quat} a new quaternion + */ +quat.clone = vec4.clone; + +/** + * Creates a new quat initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} a new quaternion + */ +quat.fromValues = vec4.fromValues; + +/** + * Copy the values from one quat to another + * + * @param {quat} out the receiving quaternion + * @param {quat} a the source quaternion + * @returns {quat} out + */ +quat.copy = vec4.copy; + +/** + * Set the components of a quat to the given values + * + * @param {quat} out the receiving quaternion + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} out + */ +quat.set = vec4.set; + +/** + * Set a quat to the identity quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ +quat.identity = function(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Sets a quat from the given angle and rotation axis, + * then returns it. + * + * @param {quat} out the receiving quaternion + * @param {vec3} axis the axis around which to rotate + * @param {Number} rad the angle in radians + * @returns {quat} out + **/ +quat.setAxisAngle = function(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +}; + +/** + * Adds two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ +quat.add = vec4.add; + +/** + * Multiplies two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ +quat.mul = quat.multiply = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; +}; + +/** + * Scales a quat by a scalar number + * + * @param {quat} out the receiving vector + * @param {quat} a the vector to scale + * @param {quat} b amount to scale the vector by + * @returns {quat} out + */ +quat.scale = vec4.scale; + +/** + * Rotates a quaternion by the given angle around the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateX = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; +}; + +/** + * Rotates a quaternion by the given angle around the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateY = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + by = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; +}; + +/** + * Rotates a quaternion by the given angle around the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateZ = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bz = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; +}; + +/** + * Calculates the W component of a quat from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate W component of + * @returns {quat} out + */ +quat.calculateW = function (out, a) { + var x = a[0], y = a[1], z = a[2]; + + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return out; +}; + +/** + * Caclulates the dot product of two quat's + * + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {Number} dot product of a and b + */ +quat.dot = vec4.dot; + +/** + * Performs a linear interpolation between two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ +quat.lerp = vec4.lerp; + +/** + * Performs a spherical linear interpolation between two quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ +quat.slerp = function (out, a, b, t) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = a[3]; + + var cosHalfTheta = ax * bx + ay * by + az * bz + aw * bw, + halfTheta, + sinHalfTheta, + ratioA, + ratioB; + + if (Math.abs(cosHalfTheta) >= 1.0) { + if (out !== a) { + out[0] = ax; + out[1] = ay; + out[2] = az; + out[3] = aw; + } + return out; + } + + halfTheta = Math.acos(cosHalfTheta); + sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + + if (Math.abs(sinHalfTheta) < 0.001) { + out[0] = (ax * 0.5 + bx * 0.5); + out[1] = (ay * 0.5 + by * 0.5); + out[2] = (az * 0.5 + bz * 0.5); + out[3] = (aw * 0.5 + bw * 0.5); + return out; + } + + ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta; + ratioB = Math.sin(t * halfTheta) / sinHalfTheta; + + out[0] = (ax * ratioA + bx * ratioB); + out[1] = (ay * ratioA + by * ratioB); + out[2] = (az * ratioA + bz * ratioB); + out[3] = (aw * ratioA + bw * ratioB); + + return out; +}; + +/** + * Calculates the inverse of a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate inverse of + * @returns {quat} out + */ +quat.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, + invDot = dot ? 1.0/dot : 0; + + // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; +}; + +/** + * Calculates the conjugate of a quat + * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate conjugate of + * @returns {quat} out + */ +quat.conjugate = function (out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Caclulates the length of a quat + * + * @param {quat} a vector to calculate length of + * @returns {Number} length of a + */ +quat.len = quat.length = vec4.length; + +/** + * Caclulates the squared length of a quat + * + * @param {quat} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +quat.sqrLen = quat.squaredLength = vec4.squaredLength; + +/** + * Normalize a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quaternion to normalize + * @returns {quat} out + */ +quat.normalize = vec4.normalize; + +/** + * Returns a string representation of a quatenion + * + * @param {quat} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +quat.str = function (a) { + return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.quat = quat; +} +; + + + + + + + + + + + })(shim.exports); +})(); + +})() +},{}],20:[function(require,module,exports){ +(function(){// Underscore.js 1.4.4 +// http://underscorejs.org +// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.4.4'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + return _.filter(obj, function(value, index, list) { + return !iterator.call(context, value, index, list); + }, context); + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs, first) { + if (_.isEmpty(attrs)) return first ? null : []; + return _[first ? 'find' : 'filter'](obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.where(obj, attrs, true); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity, value: -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity, value: Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value || _.identity); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(args, "" + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + var args = slice.call(arguments, 2); + return function() { + return func.apply(context, args.concat(slice.call(arguments))); + }; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. + _.partial = function(func) { + var args = slice.call(arguments, 1); + return function() { + return func.apply(this, args.concat(slice.call(arguments))); + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, result; + var previous = 0; + var later = function() { + previous = new Date; + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func]; + push.apply(args, arguments); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + var accum = Array(n); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + +}).call(this); + +})() +},{}],21:[function(require,module,exports){ +if (typeof(window.requestAnimationFrame) !== 'function') { + window.requestAnimationFrame = ( + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { setTimeout(callback, 1000 / 60); } + ); +} + +Leap = require("../lib/index"); + +},{"../lib/index":8}]},{},[21]) +; \ No newline at end of file diff --git a/assets/leap.min.js b/assets/leap.min.js deleted file mode 100644 index cc3ec62..0000000 --- a/assets/leap.min.js +++ /dev/null @@ -1,3 +0,0 @@ -(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return Y[0]>X[0]||Y[0]==X[0]&&Y[1]>X[1]||Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=ad&&typeof ad=="string"?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab= 10.0.0 is required.");return}if(location.protocol=="file:"){logger.error("WARNING: web-socket-js doesn't work in file:///... URL "+"unless you set Flash Security Settings properly. "+"Open the page via Web server i.e. http://...")}window.WebSocket=function(url,protocols,proxyHost,proxyPort,headers){var self=this;self.__id=WebSocket.__nextId++;WebSocket.__instances[self.__id]=self;self.readyState=WebSocket.CONNECTING;self.bufferedAmount=0;self.__events={};if(!protocols){protocols=[]}else if(typeof protocols=="string"){protocols=[protocols]}self.__createTask=setTimeout(function(){WebSocket.__addTask(function(){self.__createTask=null;WebSocket.__flash.create(self.__id,url,protocols,proxyHost||null,proxyPort||0,headers||null)})},0)};WebSocket.prototype.send=function(data){if(this.readyState==WebSocket.CONNECTING){throw"INVALID_STATE_ERR: Web Socket connection has not been established"}var result=WebSocket.__flash.send(this.__id,encodeURIComponent(data));if(result<0){return true}else{this.bufferedAmount+=result;return false}};WebSocket.prototype.close=function(){if(this.__createTask){clearTimeout(this.__createTask);this.__createTask=null;this.readyState=WebSocket.CLOSED;return}if(this.readyState==WebSocket.CLOSED||this.readyState==WebSocket.CLOSING){return}this.readyState=WebSocket.CLOSING;WebSocket.__flash.close(this.__id)};WebSocket.prototype.addEventListener=function(type,listener,useCapture){if(!(type in this.__events)){this.__events[type]=[]}this.__events[type].push(listener)};WebSocket.prototype.removeEventListener=function(type,listener,useCapture){if(!(type in this.__events))return;var events=this.__events[type];for(var i=events.length-1;i>=0;--i){if(events[i]===listener){events.splice(i,1);break}}};WebSocket.prototype.dispatchEvent=function(event){var events=this.__events[event.type]||[];for(var i=0;i0)return Math.acos(this.dot(other)/denom);else return 0},cross:function(other){var x=this.y*other.z-other.y*this.z;var y=this.x*other.z-other.x*this.z;var z=this.x*other.y-other.x*this.y;return new Vector([x,y,z])},distanceTo:function(other){return this.minus(other).magnitude()},dot:function(other){return this.x*other.x+this.y*other.y+this.z*other.z},plus:function(other){return new Vector([this.x+other.x,this.y+other.y,this.z+other.z])},minus:function(other){return new Vector([this.x-other.x,this.y-other.y,this.z-other.z])},multiply:function(scalar){return new Vector([this.x*scalar,this.y*scalar,this.z*scalar])},dividedBy:function(scalar){return new Vector([this.x/scalar,this.y/scalar,this.z/scalar])},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 magnitude=this.magnitude();if(magnitude>0)return this.dividedBy(magnitude);else return new 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(other){return this.x==other.x&&this.y==other.y&&this.z==other.z},isValid:function(){return this.x!=NaN&&this.x>-Infinity&&this.x-Infinity&&this.y-Infinity&&this.z=this.size)return undefined;if(i>=this._buf.length)return undefined;return this._buf[(this.pos-i-1)%this.size]};CircularBuffer.prototype.push=function(o){this._buf[this.pos%this.size]=o;return this.pos++}},{}],15:[function(require,module,exports){var process=module.exports={};process.nextTick=function(){var canSetImmediate=typeof window!=="undefined"&&window.setImmediate;var canPost=typeof window!=="undefined"&&window.postMessage&&window.addEventListener;if(canSetImmediate){return function(f){return window.setImmediate(f)}}if(canPost){var queue=[];window.addEventListener("message",function(ev){if(ev.source===window&&ev.data==="process-tick"){ev.stopPropagation();if(queue.length>0){var fn=queue.shift();fn()}}},true);return function nextTick(fn){queue.push(fn);window.postMessage("process-tick","*")}}return function nextTick(fn){setTimeout(fn,0)}}();process.title="browser";process.browser=true;process.env={};process.argv=[];process.binding=function(name){throw new Error("process.binding is not supported")};process.cwd=function(){return"/"};process.chdir=function(dir){throw new Error("process.chdir is not supported")}},{}],16:[function(require,module,exports){(function(process){if(!process.EventEmitter)process.EventEmitter=function(){};var EventEmitter=exports.EventEmitter=process.EventEmitter;var isArray=typeof Array.isArray==="function"?Array.isArray:function(xs){return Object.prototype.toString.call(xs)==="[object Array]"};function indexOf(xs,x){if(xs.indexOf)return xs.indexOf(x);for(var i=0;i0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);console.trace()}}this._events[type].push(listener)}else{this._events[type]=[this._events[type],listener]}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){var self=this;self.on(type,function g(){self.removeListener(type,g);listener.apply(this,arguments)});return this};EventEmitter.prototype.removeListener=function(type,listener){if("function"!==typeof listener){throw new Error("removeListener only takes instances of Function")}if(!this._events||!this._events[type])return this;var list=this._events[type];if(isArray(list)){var i=indexOf(list,listener);if(i<0)return this;list.splice(i,1);if(list.length==0)delete this._events[type]}else if(this._events[type]===listener){delete this._events[type]}return this};EventEmitter.prototype.removeAllListeners=function(type){if(arguments.length===0){this._events={};return this}if(type&&this._events&&this._events[type])this._events[type]=null;return this};EventEmitter.prototype.listeners=function(type){if(!this._events)this._events={};if(!this._events[type])this._events[type]=[];if(!isArray(this._events[type])){this._events[type]=[this._events[type]]}return this._events[type]}})(require("__browserify_process"))},{__browserify_process:15}],7:[function(require,module,exports){var Vector=require("./vector").Vector;var Gesture=exports.Gesture=function(data){var gesture=undefined;switch(data.type){case"circle":gesture=new CircleGesture(data);break;case"swipe":gesture=new SwipeGesture(data);break;case"screenTap":gesture=new ScreenTapGesture(data);break;case"keyTap":gesture=new KeyTapGesture(data);break;default:throw"unkown gesture type"}gesture.id=data.id;gesture.handIds=data.handIds;gesture.pointableIds=data.pointableIds;gesture.duration=data.duration;gesture.state=data.state;gesture.type=data.type;return gesture};var CircleGesture=function(data){this.center=new Vector(data.center);this.normal=new Vector(data.normal);this.progress=data.progress;this.radius=data.radius};var SwipeGesture=function(data){this.startPosition=new Vector(data.startPosition);this.position=new Vector(data.position);this.direction=new Vector(data.direction);this.speed=data.speed};var ScreenTapGesture=function(data){this.position=new Vector(data.position);this.direction=new Vector(data.direction);this.progress=data.progress};var KeyTapGesture=function(data){this.position=new Vector(data.position);this.direction=new Vector(data.direction);this.progress=data.progress}},{"./vector":10}],9:[function(require,module,exports){var Vector=require("./vector").Vector;var Pointable=exports.Pointable=function(data){this.valid=true;this.id=data.id;this.handId=data.handId;this.length=data.length;this.tool=data.tool;this.width=data.width;this.direction=new Vector(data.direction);this.tipPosition=new Vector(data.tipPosition);this.tipVelocity=new Vector(data.tipVelocity)};Pointable.prototype.toString=function(){if(this.tool==true){return"Pointable [ id:"+this.id+" "+this.length+"mmx | with:"+this.width+"mm | direction:"+this.direction+" ]"}else{return"Pointable [ id:"+this.id+" "+this.length+"mmx | direction: "+this.direction+" ]"}};Pointable.Invalid={valid:false}},{"./vector":10}],11:[function(require,module,exports){var Vector=require("./vector").Vector;var Matrix=exports.Matrix=function(data){if(data instanceof Matrix){this.xBasis=new Vector(data.xBasis);this.yBasis=new Vector(data.yBasis);this.zBasis=new Vector(data.zBasis);this.origin=new Vector(data.origin)}else if(data instanceof Array){if(data[0]instanceof Vector&&typeof data[1]=="number"){this.setRotation(data[0],data[1]);this.origin=new Vector(data[2])}else{this.xBasis=new Vector(data[0]);this.yBasis=new Vector(data[1]);this.zBasis=new Vector(data[2]);this.origin=new Vector(data[3])}}else{this.xBasis=new Vector([1,0,0]);this.yBasis=new Vector([0,1,0]);this.zBasis=new Vector([0,0,1]);this.origin=new Vector([0,0,0])}};Matrix.prototype={setRotation:function(_axis,angle){var axis=_axis.normalized();var s=Math.sin(angle);var c=Math.cos(angle);var C=1-c;this.xBasis=new Vector([axis.x*axis.x*C+c,axis.x*axis.y*C-axis.z*s,axis.x*axis.z*C+axis.y*s]);this.yBasis=new Vector([axis.y*axis.x*C+axis.z*s,axis.y*axis.y*C+c,axis.y*axis.z*C-axis.x*s]);this.zBasis=new Vector([axis.z*axis.x*C-axis.y*s,axis.z*axis.y*C+axis.x*s,axis.z*axis.z*C+c])},transformPoint:function(data){return this.origin.plus(this.transformDirection(data))},transformDirection:function(data){var x=this.xBasis.multiply(data.x);var y=this.yBasis.multiply(data.y);var z=this.zBasis.multiply(data.z);return x.plus(y).plus(z)},times:function(other){var x=this.transformDirection(other.xBasis);var y=this.transformDirection(other.yBasis);var z=this.transformDirection(other.zBasis);var o=this.transformPoint(other.origin);return new Matrix([x,y,z,o])},rigidInverse:function(){var x=new Vector([this.xBasis.x,this.yBasis.x,this.zBasis.x]);var y=new Vector([this.xBasis.y,this.yBasis.y,this.zBasis.y]);var z=new Vector([this.xBasis.z,this.yBasis.z,this.zBasis.z]);var rotInverse=new Matrix([x,y,z]);rotInverse.origin=rotInverse.transformDirection(Vector.zero().minus(this.origin));return rotInverse},toArray3x3:function(output){if(output==null)output=[];else output.length=0;output[0]=this.xBasis.x;output[1]=this.xBasis.y;output[2]=this.xBasis.z;output[3]=this.yBasis.x;output[4]=this.yBasis.y;output[5]=this.yBasis.z;output[6]=this.zBasis.x;output[7]=this.zBasis.y;output[8]=this.zBasis.z;return output},toArray4x4:function(output){if(output==null)output=[];else output.length=0;output[0]=this.xBasis.x;output[1]=this.xBasis.y;output[2]=this.xBasis.z;output[3]=0;output[4]=this.yBasis.x;output[5]=this.yBasis.y;output[6]=this.yBasis.z;output[7]=0;output[8]=this.zBasis.x;output[9]=this.zBasis.y;output[10]=this.zBasis.z;output[11]=0;output[12]=this.origin.x;output[13]=this.origin.y;output[14]=this.origin.z;output[15]=1;return output},toString:function(){return"{xBasis:"+this.xBasis+",yBasis:"+this.yBasis+",zBasis:"+this.zBasis+",origin:"+this.origin+"}"},compare:function(other){return this.xBasis.compare(other.xBasis)&&this.yBasis.compare(other.yBasis)&&this.zBasis.compare(other.zBasis)&&this.origin.compare(other.origin)}};Matrix.identity=function(){return new Matrix}},{"./vector":10}],12:[function(require,module,exports){var Connection=exports.Connection=require("./base_connection").Connection;Connection.prototype.setupSocket=function(){var connection=this;var socket=new WebSocket("ws://"+this.host+":"+this.port);socket.onopen=function(){connection.handleOpen()};socket.onmessage=function(message){connection.handleData(message.data)};socket.onclose=function(){connection.handleClose()};return socket};Connection.prototype.teardownSocket=function(){this.socket.close();delete this.socket;delete this.protocol}},{"./base_connection":17}],14:[function(require,module,exports){exports.UI={Region:require("./ui/region").Region,Cursor:require("./ui/cursor").Cursor}},{"./ui/region":18,"./ui/cursor":19}],20:[function(require,module,exports){var Pipeline=exports.Pipeline=function(){this.steps=[]};Pipeline.prototype.addStep=function(step){this.steps.push(step)};Pipeline.prototype.run=function(frame){var stepsLength=this.steps.length;for(var i=0;i!=stepsLength;i++){if(!frame)break;frame=this.steps[i](frame)}return frame}},{}],19:[function(require,module,exports){var Cursor=exports.Cursor=function(){return function(frame){var pointable=frame.pointables.sort(function(a,b){return a.z-b.z -})[0];if(pointable&&pointable.valid){frame.cursorPosition=pointable.tipPosition}return frame}}},{}],5:[function(require,module,exports){var inNode=typeof window==="undefined";var Frame=require("./frame").Frame,CircularBuffer=require("./circular_buffer").CircularBuffer,Pipeline=require("./pipeline").Pipeline,EventEmitter=require("events").EventEmitter,_=require("underscore");var Controller=exports.Controller=function(opts){this.opts=_.defaults(opts||{},{frameEventName:this.useAnimationLoop()?"animationFrame":"connectionFrame"});this.history=new CircularBuffer(200);var controller=this;this.lastFrame=Frame.Invalid;this.lastValidFrame=Frame.Invalid;this.lastConnectionFrame=Frame.Invalid;var connectionType=this.opts.connectionType||this.connectionType();this.connection=new connectionType(this.opts);this.accumulatedGestures=[];this.connection.on("frame",function(frame){if(frame.gestures){controller.accumulatedGestures=controller.accumulatedGestures.concat(frame.gestures)}controller.processFrame(frame)});this.on(this.opts.frameEventName,function(frame){controller.processFinishedFrame(frame)});this.connection.on("ready",function(){controller.emit("ready")});this.connection.on("connect",function(){controller.emit("connect")});this.connection.on("disconnect",function(){controller.emit("disconnect")})};Controller.prototype.inBrowser=function(){return!inNode};Controller.prototype.useAnimationLoop=function(){return this.inBrowser()&&typeof chrome==="undefined"};Controller.prototype.connectionType=function(){return(this.inBrowser()?require("./connection"):require("./node_connection")).Connection};Controller.prototype.connect=function(){var controller=this;if(this.connection.connect()&&this.inBrowser()){var callback=function(){controller.emit("animationFrame",controller.lastConnectionFrame);if(controller.opts.supressAnimationLoop!==true)window.requestAnimFrame(callback)};if(controller.opts.supressAnimationLoop!==true){window.requestAnimFrame(callback)}}};Controller.prototype.disconnect=function(){this.connection.disconnect()};Controller.prototype.frame=function(num){return this.history.get(num)||Frame.Invalid};Controller.prototype.loop=function(callback){switch(callback.length){case 1:this.on(this.opts.frameEventName,callback);break;case 2:var controller=this;var scheduler=null;var immediateRunnerCallback=function(frame){callback(frame,function(){if(controller.lastFrame!=frame){immediateRunnerCallback(controller.lastFrame)}else{controller.once(controller.opts.frameEventName,immediateRunnerCallback)}})};this.once(this.opts.frameEventName,immediateRunnerCallback);break}this.connect()};Controller.prototype.addStep=function(step){if(!this.pipeline)this.pipeline=new Pipeline(this);this.pipeline.addStep(step)};Controller.prototype.processFrame=function(frame){if(this.pipeline){frame=this.pipeline.run(frame);if(!frame)frame=Frame.Invalid}this.lastConnectionFrame=frame;this.emit("connectionFrame",frame)};Controller.prototype.processFinishedFrame=function(frame){this.lastFrame=frame;if(frame.valid){this.lastValidFrame=frame}if(frame.gestures){frame.gestures=this.accumulatedGestures;this.accumulatedGestures=[]}frame.controller=this;frame.historyIdx=this.history.push(frame);this.emit("frame",frame)};_.extend(Controller.prototype,EventEmitter.prototype)},{events:16,"./frame":6,"./circular_buffer":13,"./pipeline":20,"./connection":12,"./node_connection":21,underscore:22}],6:[function(require,module,exports){var Hand=require("./hand").Hand,Pointable=require("./pointable").Pointable,Gesture=require("./gesture").Gesture,Vector=require("./vector").Vector,Matrix=require("./matrix").Matrix,_=require("underscore");var Frame=exports.Frame=function(data){this.valid=true;this.id=data.id;this.timestamp=data.timestamp;this.hands=[];this.handsMap={};this.pointables=[];this.tools=[];this.fingers=[];this.gestures=[];this.pointablesMap={};this._translation=new Vector(data.t);this.rotation=new Matrix(data.r);this._scaleFactor=data.s;this.data=data;this.type="frame";var handMap={};for(var handIdx=0,handCount=data.hands.length;handIdx!=handCount;handIdx++){var hand=new Hand(data.hands[handIdx]);hand.frame=this;this.hands.push(hand);this.handsMap[hand.id]=hand;handMap[hand.id]=handIdx}for(var pointableIdx=0,pointableCount=data.pointables.length;pointableIdx!=pointableCount;pointableIdx++){var pointable=new Pointable(data.pointables[pointableIdx]);pointable.frame=this;this.pointables.push(pointable);this.pointablesMap[pointable.id]=pointable;(pointable.tool?this.tools:this.fingers).push(pointable);if(pointable.handId&&handMap.hasOwnProperty(pointable.handId)){var hand=this.hands[handMap[pointable.handId]];hand.pointables.push(pointable);(pointable.tool?hand.tools:hand.fingers).push(pointable)}}if(data.gestures){for(var gestureIdx=0,gestureCount=data.gestures.length;gestureIdx!=gestureCount;gestureIdx++){this.gestures.push(Gesture(data.gestures[gestureIdx]))}}};Frame.prototype.tool=function(id){var pointable=this.pointable(id);return pointable.tool?pointable:Pointable.Invalid};Frame.prototype.pointable=function(id){return this.pointablesMap[id]||Pointable.Invalid};Frame.prototype.finger=function(id){var pointable=this.pointable(id);return!pointable.tool?pointable:Pointable.Invalid};Frame.prototype.hand=function(id){return this.handsMap[id]||Hand.Invalid};Frame.prototype.rotationAngle=function(sinceFrame,axis){if(!this.valid||!sinceFrame.valid)return 0;var rot=this.rotationMatrix(sinceFrame);var cs=(rot.xBasis.x+rot.yBasis.y+rot.zBasis.z-1)*.5;var angle=Math.acos(cs);return isNaN(angle)?0:angle};Frame.prototype.rotationAxis=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var x=this.rotation.zBasis.y-sinceFrame.rotation.yBasis.z;var y=this.rotation.xBasis.z-sinceFrame.rotation.zBasis.x;var z=this.rotation.yBasis.x-sinceFrame.rotation.xBasis.y;var vec=new Vector([x,y,z]);return vec.normalized()};Frame.prototype.rotationMatrix=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Matrix.identity();var xBasis=new Vector([this.rotation.xBasis.x,this.rotation.yBasis.x,this.rotation.zBasis.x]);var yBasis=new Vector([this.rotation.xBasis.y,this.rotation.yBasis.y,this.rotation.zBasis.y]);var zBasis=new Vector([this.rotation.xBasis.z,this.rotation.yBasis.z,this.rotation.zBasis.z]);var transpose=new Matrix([xBasis,yBasis,zBasis]);return sinceFrame.rotation.times(transpose)};Frame.prototype.scaleFactor=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return 1;return Math.exp(this._scaleFactor-sinceFrame._scaleFactor)};Frame.prototype.translation=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var x=this._translation.x-sinceFrame._translation.x;var y=this._translation.y-sinceFrame._translation.y;var z=this._translation.z-sinceFrame._translation.z;return new Vector([x,y,z])};Frame.prototype.toString=function(){var str="Frame [ id:"+this.id+" | timestamp:"+this.timestamp+" | Hand count:("+this.hands.length+") | Pointable count:("+this.pointables.length+")";if(this.gestures)str+=" | Gesture count:("+this.gestures.length+")";str+=" ]";return str};Frame.prototype.dump=function(){var out="";out+="Frame Info:
";out+=this.toString();out+="

Hands:
";for(var handIdx=0,handCount=this.hands.length;handIdx!=handCount;handIdx++){out+=" "+this.hands[handIdx].toString()+"
"}out+="

Pointables:
";for(var pointableIdx=0,pointableCount=this.pointables.length;pointableIdx!=pointableCount;pointableIdx++){out+=" "+this.pointables[pointableIdx].toString()+"
"}if(this.gestures){out+="

Gestures:
";for(var gestureIdx=0,gestureCount=this.gestures.length;gestureIdx!=gestureCount;gestureIdx++){out+=" "+this.gestures[gestureIdx].toString()+"
"}}out+="

Raw JSON:
";out+=JSON.stringify(this.data);return out};Frame.Invalid={valid:false,hands:[],fingers:[],tools:[],gestures:[],pointables:[],pointable:function(){return Pointable.Invalid},finger:function(){return Pointable.Invalid},hand:function(){return Hand.Invalid},toString:function(){return"invalid frame"},dump:function(){return this.toString()}}},{"./hand":8,"./pointable":9,"./gesture":7,"./vector":10,"./matrix":11,underscore:22}],8:[function(require,module,exports){var Pointable=require("./pointable").Pointable,Vector=require("./vector").Vector,Matrix=require("./matrix").Matrix,_=require("underscore");var Hand=exports.Hand=function(data){this.id=data.id;this.palmPosition=new Vector(data.palmPosition);this.direction=new Vector(data.direction);this.palmVelocity=new Vector(data.palmVelocity);this.palmNormal=new Vector(data.palmNormal);this.sphereCenter=new Vector(data.sphereCenter);this.sphereRadius=data.sphereRadius;this.valid=true;this.pointables=[];this.fingers=[];this.tools=[];this._translation=new Vector(data.t);this.rotation=new Matrix(data.r);this._scaleFactor=data.s};Hand.prototype.finger=function(id){var finger=this.frame.finger(id);return finger&&finger.handId==this.id?finger:Pointable.Invalid};Hand.prototype.rotationAngle=function(sinceFrame,axis){if(!this.valid||!sinceFrame.valid)return 0;var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return 0;var rot=this.rotationMatrix(sinceFrame);var cs=(rot.xBasis.x+rot.yBasis.y+rot.zBasis.z-1)*.5;var angle=Math.acos(cs);return isNaN(angle)?0:angle};Hand.prototype.rotationAxis=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return Vector.zero();var x=this.rotation.zBasis.y-sinceHand.rotation.yBasis.z;var y=this.rotation.xBasis.z-sinceHand.rotation.zBasis.x;var z=this.rotation.yBasis.x-sinceHand.rotation.xBasis.y;var vec=new Vector([x,y,z]);return vec.normalized()};Hand.prototype.rotationMatrix=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Matrix.identity();var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return Matrix.identity();var xBasis=new Vector([this.rotation.xBasis.x,this.rotation.yBasis.x,this.rotation.zBasis.x]);var yBasis=new Vector([this.rotation.xBasis.y,this.rotation.yBasis.y,this.rotation.zBasis.y]);var zBasis=new Vector([this.rotation.xBasis.z,this.rotation.yBasis.z,this.rotation.zBasis.z]);var transpose=new Matrix([xBasis,yBasis,zBasis]);return sinceHand.rotation.times(transpose)};Hand.prototype.scaleFactor=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return 1;var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return 1;return Math.exp(this._scaleFactor-sinceHand._scaleFactor)};Hand.prototype.translation=function(sinceFrame){if(!this.valid||!sinceFrame.valid)return Vector.zero();var sinceHand=sinceFrame.hand(this.id);if(!sinceHand.valid)return Vector.zero();var x=this._translation.x-sinceHand._translation.x;var y=this._translation.y-sinceHand._translation.y;var z=this._translation.z-sinceHand._translation.z;return new Vector([x,y,z])};Hand.prototype.toString=function(){return"Hand [ id: "+this.id+" | palm velocity:"+this.palmVelocity+" | sphere center:"+this.sphereCenter+" ] "};Hand.Invalid={valid:false}},{"./pointable":9,"./vector":10,"./matrix":11,underscore:22}],22:[function(require,module,exports){(function(){(function(){var root=this;var previousUnderscore=root._;var breaker={};var ArrayProto=Array.prototype,ObjProto=Object.prototype,FuncProto=Function.prototype;var push=ArrayProto.push,slice=ArrayProto.slice,concat=ArrayProto.concat,toString=ObjProto.toString,hasOwnProperty=ObjProto.hasOwnProperty;var nativeForEach=ArrayProto.forEach,nativeMap=ArrayProto.map,nativeReduce=ArrayProto.reduce,nativeReduceRight=ArrayProto.reduceRight,nativeFilter=ArrayProto.filter,nativeEvery=ArrayProto.every,nativeSome=ArrayProto.some,nativeIndexOf=ArrayProto.indexOf,nativeLastIndexOf=ArrayProto.lastIndexOf,nativeIsArray=Array.isArray,nativeKeys=Object.keys,nativeBind=FuncProto.bind;var _=function(obj){if(obj instanceof _)return obj;if(!(this instanceof _))return new _(obj);this._wrapped=obj};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=_}exports._=_}else{root._=_}_.VERSION="1.4.4";var each=_.each=_.forEach=function(obj,iterator,context){if(obj==null)return;if(nativeForEach&&obj.forEach===nativeForEach){obj.forEach(iterator,context)}else if(obj.length===+obj.length){for(var i=0,l=obj.length;i2;if(obj==null)obj=[];if(nativeReduce&&obj.reduce===nativeReduce){if(context)iterator=_.bind(iterator,context);return initial?obj.reduce(iterator,memo):obj.reduce(iterator)}each(obj,function(value,index,list){if(!initial){memo=value;initial=true}else{memo=iterator.call(context,memo,value,index,list)}});if(!initial)throw new TypeError(reduceError);return memo};_.reduceRight=_.foldr=function(obj,iterator,memo,context){var initial=arguments.length>2;if(obj==null)obj=[];if(nativeReduceRight&&obj.reduceRight===nativeReduceRight){if(context)iterator=_.bind(iterator,context);return initial?obj.reduceRight(iterator,memo):obj.reduceRight(iterator)}var length=obj.length;if(length!==+length){var keys=_.keys(obj);length=keys.length}each(obj,function(value,index,list){index=keys?keys[--length]:--length;if(!initial){memo=obj[index];initial=true}else{memo=iterator.call(context,memo,obj[index],index,list)}});if(!initial)throw new TypeError(reduceError);return memo};_.find=_.detect=function(obj,iterator,context){var result;any(obj,function(value,index,list){if(iterator.call(context,value,index,list)){result=value;return true}});return result};_.filter=_.select=function(obj,iterator,context){var results=[];if(obj==null)return results;if(nativeFilter&&obj.filter===nativeFilter)return obj.filter(iterator,context);each(obj,function(value,index,list){if(iterator.call(context,value,index,list))results[results.length]=value});return results};_.reject=function(obj,iterator,context){return _.filter(obj,function(value,index,list){return!iterator.call(context,value,index,list)},context)};_.every=_.all=function(obj,iterator,context){iterator||(iterator=_.identity);var result=true;if(obj==null)return result;if(nativeEvery&&obj.every===nativeEvery)return obj.every(iterator,context);each(obj,function(value,index,list){if(!(result=result&&iterator.call(context,value,index,list)))return breaker});return!!result};var any=_.some=_.any=function(obj,iterator,context){iterator||(iterator=_.identity);var result=false;if(obj==null)return result;if(nativeSome&&obj.some===nativeSome)return obj.some(iterator,context);each(obj,function(value,index,list){if(result||(result=iterator.call(context,value,index,list)))return breaker});return!!result};_.contains=_.include=function(obj,target){if(obj==null)return false;if(nativeIndexOf&&obj.indexOf===nativeIndexOf)return obj.indexOf(target)!=-1;return any(obj,function(value){return value===target})};_.invoke=function(obj,method){var args=slice.call(arguments,2);var isFunc=_.isFunction(method);return _.map(obj,function(value){return(isFunc?method:value[method]).apply(value,args)})};_.pluck=function(obj,key){return _.map(obj,function(value){return value[key]})};_.where=function(obj,attrs,first){if(_.isEmpty(attrs))return first?null:[];return _[first?"find":"filter"](obj,function(value){for(var key in attrs){if(attrs[key]!==value[key])return false}return true})};_.findWhere=function(obj,attrs){return _.where(obj,attrs,true)};_.max=function(obj,iterator,context){if(!iterator&&_.isArray(obj)&&obj[0]===+obj[0]&&obj.length<65535){return Math.max.apply(Math,obj)}if(!iterator&&_.isEmpty(obj))return-Infinity;var result={computed:-Infinity,value:-Infinity};each(obj,function(value,index,list){var computed=iterator?iterator.call(context,value,index,list):value;computed>=result.computed&&(result={value:value,computed:computed})});return result.value};_.min=function(obj,iterator,context){if(!iterator&&_.isArray(obj)&&obj[0]===+obj[0]&&obj.length<65535){return Math.min.apply(Math,obj)}if(!iterator&&_.isEmpty(obj))return Infinity;var result={computed:Infinity,value:Infinity};each(obj,function(value,index,list){var computed=iterator?iterator.call(context,value,index,list):value;computedb||a===void 0)return 1;if(a>>1;iterator.call(context,array[mid])=0})})};_.difference=function(array){var rest=concat.apply(ArrayProto,slice.call(arguments,1));return _.filter(array,function(value){return!_.contains(rest,value)})};_.zip=function(){var args=slice.call(arguments);var length=_.max(_.pluck(args,"length"));var results=new Array(length);for(var i=0;i=0;i--){args=[funcs[i].apply(this,args)]}return args[0]}};_.after=function(times,func){if(times<=0)return func();return function(){if(--times<1){return func.apply(this,arguments)}}};_.keys=nativeKeys||function(obj){if(obj!==Object(obj))throw new TypeError("Invalid object");var keys=[];for(var key in obj)if(_.has(obj,key))keys[keys.length]=key;return keys};_.values=function(obj){var values=[];for(var key in obj)if(_.has(obj,key))values.push(obj[key]);return values};_.pairs=function(obj){var pairs=[];for(var key in obj)if(_.has(obj,key))pairs.push([key,obj[key]]);return pairs};_.invert=function(obj){var result={};for(var key in obj)if(_.has(obj,key))result[obj[key]]=key;return result};_.functions=_.methods=function(obj){var names=[];for(var key in obj){if(_.isFunction(obj[key]))names.push(key)}return names.sort()};_.extend=function(obj){each(slice.call(arguments,1),function(source){if(source){for(var prop in source){obj[prop]=source[prop]}}});return obj};_.pick=function(obj){var copy={};var keys=concat.apply(ArrayProto,slice.call(arguments,1));each(keys,function(key){if(key in obj)copy[key]=obj[key]});return copy};_.omit=function(obj){var copy={};var keys=concat.apply(ArrayProto,slice.call(arguments,1));for(var key in obj){if(!_.contains(keys,key))copy[key]=obj[key]}return copy};_.defaults=function(obj){each(slice.call(arguments,1),function(source){if(source){for(var prop in source){if(obj[prop]==null)obj[prop]=source[prop]}}});return obj};_.clone=function(obj){if(!_.isObject(obj))return obj;return _.isArray(obj)?obj.slice():_.extend({},obj)};_.tap=function(obj,interceptor){interceptor(obj);return obj};var eq=function(a,b,aStack,bStack){if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;if(a instanceof _)a=a._wrapped;if(b instanceof _)b=b._wrapped;var className=toString.call(a);if(className!=toString.call(b))return false;switch(className){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case"[object Date]":case"[object Boolean]":return+a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if(typeof a!="object"||typeof b!="object")return false;var length=aStack.length;while(length--){if(aStack[length]==a)return bStack[length]==b}aStack.push(a);bStack.push(b);var size=0,result=true;if(className=="[object Array]"){size=a.length;result=size==b.length;if(result){while(size--){if(!(result=eq(a[size],b[size],aStack,bStack)))break}}}else{var aCtor=a.constructor,bCtor=b.constructor;if(aCtor!==bCtor&&!(_.isFunction(aCtor)&&aCtor instanceof aCtor&&_.isFunction(bCtor)&&bCtor instanceof bCtor)){return false}for(var key in a){if(_.has(a,key)){size++;if(!(result=_.has(b,key)&&eq(a[key],b[key],aStack,bStack)))break}}if(result){for(key in b){if(_.has(b,key)&&!size--)break}result=!size}}aStack.pop();bStack.pop();return result};_.isEqual=function(a,b){return eq(a,b,[],[])};_.isEmpty=function(obj){if(obj==null)return true;if(_.isArray(obj)||_.isString(obj))return obj.length===0;for(var key in obj)if(_.has(obj,key))return false;return true};_.isElement=function(obj){return!!(obj&&obj.nodeType===1)};_.isArray=nativeIsArray||function(obj){return toString.call(obj)=="[object Array]"};_.isObject=function(obj){return obj===Object(obj)};each(["Arguments","Function","String","Number","Date","RegExp"],function(name){_["is"+name]=function(obj){return toString.call(obj)=="[object "+name+"]"}});if(!_.isArguments(arguments)){_.isArguments=function(obj){return!!(obj&&_.has(obj,"callee"))}}if(typeof/./!=="function"){_.isFunction=function(obj){return typeof obj==="function"}}_.isFinite=function(obj){return isFinite(obj)&&!isNaN(parseFloat(obj))};_.isNaN=function(obj){return _.isNumber(obj)&&obj!=+obj};_.isBoolean=function(obj){return obj===true||obj===false||toString.call(obj)=="[object Boolean]"};_.isNull=function(obj){return obj===null};_.isUndefined=function(obj){return obj===void 0};_.has=function(obj,key){return hasOwnProperty.call(obj,key)};_.noConflict=function(){root._=previousUnderscore;return this};_.identity=function(value){return value};_.times=function(n,iterator,context){var accum=Array(n);for(var i=0;i":">",'"':""","'":"'","/":"/"}};entityMap.unescape=_.invert(entityMap.escape);var entityRegexes={escape:new RegExp("["+_.keys(entityMap.escape).join("")+"]","g"),unescape:new RegExp("("+_.keys(entityMap.unescape).join("|")+")","g")};_.each(["escape","unescape"],function(method){_[method]=function(string){if(string==null)return"";return(""+string).replace(entityRegexes[method],function(match){return entityMap[method][match]})}});_.result=function(object,property){if(object==null)return null;var value=object[property];return _.isFunction(value)?value.call(object):value};_.mixin=function(obj){each(_.functions(obj),function(name){var func=_[name]=obj[name];_.prototype[name]=function(){var args=[this._wrapped];push.apply(args,arguments);return result.call(this,func.apply(_,args))}})};var idCounter=0;_.uniqueId=function(prefix){var id=++idCounter+"";return prefix?prefix+id:id};_.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var noMatch=/(.)^/;var escapes={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"};var escaper=/\\|'|\r|\n|\t|\u2028|\u2029/g;_.template=function(text,data,settings){var render;settings=_.defaults({},settings,_.templateSettings);var matcher=new RegExp([(settings.escape||noMatch).source,(settings.interpolate||noMatch).source,(settings.evaluate||noMatch).source].join("|")+"|$","g");var index=0;var source="__p+='";text.replace(matcher,function(match,escape,interpolate,evaluate,offset){source+=text.slice(index,offset).replace(escaper,function(match){return"\\"+escapes[match]});if(escape){source+="'+\n((__t=("+escape+"))==null?'':_.escape(__t))+\n'"}if(interpolate){source+="'+\n((__t=("+interpolate+"))==null?'':__t)+\n'"}if(evaluate){source+="';\n"+evaluate+"\n__p+='"}index=offset+match.length;return match});source+="';\n";if(!settings.variable)source="with(obj||{}){\n"+source+"}\n";source="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+source+"return __p;\n";try{render=new Function(settings.variable||"obj","_",source)}catch(e){e.source=source;throw e}if(data)return render(data,_);var template=function(data){return render.call(this,data,_)};template.source="function("+(settings.variable||"obj")+"){\n"+source+"}";return template};_.chain=function(obj){return _(obj).chain()};var result=function(obj){return this._chain?_(obj).chain():obj};_.mixin(_);each(["pop","push","reverse","shift","sort","splice","unshift"],function(name){var method=ArrayProto[name];_.prototype[name]=function(){var obj=this._wrapped;method.apply(obj,arguments);if((name=="shift"||name=="splice")&&obj.length===0)delete obj[0];return result.call(this,obj)}});each(["concat","join","slice"],function(name){var method=ArrayProto[name];_.prototype[name]=function(){return result.call(this,method.apply(this._wrapped,arguments))}});_.extend(_.prototype,{chain:function(){this._chain=true;return this},value:function(){return this._wrapped}})}).call(this)})()},{}],17:[function(require,module,exports){var chooseProtocol=require("./protocol").chooseProtocol,EventEmitter=require("events").EventEmitter,_=require("underscore");var Connection=exports.Connection=function(opts){opts=_.defaults(opts||{},{host:"127.0.0.1",enableGestures:false,port:6437});this.host=opts.host;this.port=opts.port;this.on("ready",function(){this.enableGestures(opts.enableGestures)})};Connection.prototype.handleOpen=function(){this.emit("connect")};Connection.prototype.enableGestures=function(enabled){this.gesturesEnabled=enabled?true:false;this.send(this.protocol.encode({enableGestures:this.gesturesEnabled}))};Connection.prototype.handleClose=function(){this.startReconnection();this.emit("disconnect")};Connection.prototype.startReconnection=function(){var connection=this;setTimeout(function(){connection.connect()},1e3)};Connection.prototype.disconnect=function(){if(!this.socket)return;this.teardownSocket();this.socket=undefined;this.protocol=undefined};Connection.prototype.handleData=function(data){var message=JSON.parse(data);var messageEvent; -if(this.protocol===undefined){messageEvent=this.protocol=chooseProtocol(message);this.emit("ready")}else{messageEvent=this.protocol(message)}this.emit(messageEvent.type,messageEvent)};Connection.prototype.connect=function(){if(this.socket){this.teardownSocket()}this.socket=this.setupSocket();return true};Connection.prototype.send=function(data){this.socket.send(data)};_.extend(Connection.prototype,EventEmitter.prototype)},{events:16,"./protocol":23,underscore:22}],24:[function(require,module,exports){var events=require("events");exports.isArray=isArray;exports.isDate=function(obj){return Object.prototype.toString.call(obj)==="[object Date]"};exports.isRegExp=function(obj){return Object.prototype.toString.call(obj)==="[object RegExp]"};exports.print=function(){};exports.puts=function(){};exports.debug=function(){};exports.inspect=function(obj,showHidden,depth,colors){var seen=[];var stylize=function(str,styleType){var styles={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]};var style={special:"cyan",number:"blue","boolean":"yellow",undefined:"grey","null":"bold",string:"green",date:"magenta",regexp:"red"}[styleType];if(style){return"["+styles[style][0]+"m"+str+"["+styles[style][1]+"m"}else{return str}};if(!colors){stylize=function(str,styleType){return str}}function format(value,recurseTimes){if(value&&typeof value.inspect==="function"&&value!==exports&&!(value.constructor&&value.constructor.prototype===value)){return value.inspect(recurseTimes)}switch(typeof value){case"undefined":return stylize("undefined","undefined");case"string":var simple="'"+JSON.stringify(value).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return stylize(simple,"string");case"number":return stylize(""+value,"number");case"boolean":return stylize(""+value,"boolean")}if(value===null){return stylize("null","null")}var visible_keys=Object_keys(value);var keys=showHidden?Object_getOwnPropertyNames(value):visible_keys;if(typeof value==="function"&&keys.length===0){if(isRegExp(value)){return stylize(""+value,"regexp")}else{var name=value.name?": "+value.name:"";return stylize("[Function"+name+"]","special")}}if(isDate(value)&&keys.length===0){return stylize(value.toUTCString(),"date")}var base,type,braces;if(isArray(value)){type="Array";braces=["[","]"]}else{type="Object";braces=["{","}"]}if(typeof value==="function"){var n=value.name?": "+value.name:"";base=isRegExp(value)?" "+value:" [Function"+n+"]"}else{base=""}if(isDate(value)){base=" "+value.toUTCString()}if(keys.length===0){return braces[0]+base+braces[1]}if(recurseTimes<0){if(isRegExp(value)){return stylize(""+value,"regexp")}else{return stylize("[Object]","special")}}seen.push(value);var output=keys.map(function(key){var name,str;if(value.__lookupGetter__){if(value.__lookupGetter__(key)){if(value.__lookupSetter__(key)){str=stylize("[Getter/Setter]","special")}else{str=stylize("[Getter]","special")}}else{if(value.__lookupSetter__(key)){str=stylize("[Setter]","special")}}}if(visible_keys.indexOf(key)<0){name="["+key+"]"}if(!str){if(seen.indexOf(value[key])<0){if(recurseTimes===null){str=format(value[key])}else{str=format(value[key],recurseTimes-1)}if(str.indexOf("\n")>-1){if(isArray(value)){str=str.split("\n").map(function(line){return" "+line}).join("\n").substr(2)}else{str="\n"+str.split("\n").map(function(line){return" "+line}).join("\n")}}}else{str=stylize("[Circular]","special")}}if(typeof name==="undefined"){if(type==="Array"&&key.match(/^\d+$/)){return str}name=JSON.stringify(""+key);if(name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)){name=name.substr(1,name.length-2);name=stylize(name,"name")}else{name=name.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'");name=stylize(name,"string")}}return name+": "+str});seen.pop();var numLinesEst=0;var length=output.reduce(function(prev,cur){numLinesEst++;if(cur.indexOf("\n")>=0)numLinesEst++;return prev+cur.length+1},0);if(length>50){output=braces[0]+(base===""?"":base+"\n ")+" "+output.join(",\n ")+" "+braces[1]}else{output=braces[0]+base+" "+output.join(", ")+" "+braces[1]}return output}return format(obj,typeof depth==="undefined"?2:depth)};function isArray(ar){return ar instanceof Array||Array.isArray(ar)||ar&&ar!==Object.prototype&&isArray(ar.__proto__)}function isRegExp(re){return re instanceof RegExp||typeof re==="object"&&Object.prototype.toString.call(re)==="[object RegExp]"}function isDate(d){if(d instanceof Date)return true;if(typeof d!=="object")return false;var properties=Date.prototype&&Object_getOwnPropertyNames(Date.prototype);var proto=d.__proto__&&Object_getOwnPropertyNames(d.__proto__);return JSON.stringify(proto)===JSON.stringify(properties)}function pad(n){return n<10?"0"+n.toString(10):n.toString(10)}var months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function timestamp(){var d=new Date;var time=[pad(d.getHours()),pad(d.getMinutes()),pad(d.getSeconds())].join(":");return[d.getDate(),months[d.getMonth()],time].join(" ")}exports.log=function(msg){};exports.pump=null;var Object_keys=Object.keys||function(obj){var res=[];for(var key in obj)res.push(key);return res};var Object_getOwnPropertyNames=Object.getOwnPropertyNames||function(obj){var res=[];for(var key in obj){if(Object.hasOwnProperty.call(obj,key))res.push(key)}return res};var Object_create=Object.create||function(prototype,properties){var object;if(prototype===null){object={__proto__:null}}else{if(typeof prototype!=="object"){throw new TypeError("typeof prototype["+typeof prototype+"] != 'object'")}var Type=function(){};Type.prototype=prototype;object=new Type;object.__proto__=prototype}if(typeof properties!=="undefined"&&Object.defineProperties){Object.defineProperties(object,properties)}return object};exports.inherits=function(ctor,superCtor){ctor.super_=superCtor;ctor.prototype=Object_create(superCtor.prototype,{constructor:{value:ctor,enumerable:false,writable:true,configurable:true}})};var formatRegExp=/%[sdj%]/g;exports.format=function(f){if(typeof f!=="string"){var objects=[];for(var i=0;i=len)return x;switch(x){case"%s":return String(args[i++]);case"%d":return Number(args[i++]);case"%j":return JSON.stringify(args[i++]);default:return x}});for(var x=args[i];i=this.start.x&&position.x<=this.end.x&&position.y>=this.start.y&&position.y<=this.end.y&&position.z>=this.start.z&&position.z<=this.end.z){return true}}return false};Region.prototype.listener=function(opts){var region=this;if(opts&&opts.nearThreshold)this.setupNearRegion(opts.nearThreshold);return function(frame){return region.updatePosition(frame)}};Region.prototype.clipper=function(){var region=this;return function(frame){region.updatePosition(frame);return region.enteredFrame?frame:null}};Region.prototype.setupNearRegion=function(distance){var nearRegion=this.nearRegion=new Region([this.start.x-distance,this.start.y-distance,this.start.z-distance],[this.end.x+distance,this.end.y+distance,this.end.z+distance]);var region=this;nearRegion.on("enter",function(frame){region.emit("near",frame)});nearRegion.on("exit",function(frame){region.emit("far",frame)});region.on("exit",function(frame){region.emit("near",frame)})};Region.prototype.updatePosition=function(frame){if(this.nearRegion)this.nearRegion.updatePosition(frame);if(this.hasPointables(frame)&&this.enteredFrame==null){this.enteredFrame=frame;this.emit("enter",this.enteredFrame)}else if(!this.hasPointables(frame)&&this.enteredFrame!=null){this.enteredFrame=null;this.emit("exit",this.enteredFrame)}return frame};Region.prototype.normalize=function(position){return new Vector([(position.x-this.start.x)/(this.end.x-this.start.x),(position.y-this.start.y)/(this.end.y-this.start.y),(position.z-this.start.z)/(this.end.z-this.start.z)])};Region.prototype.mapToXY=function(position,width,height){var normalized=this.normalize(position);var x=normalized.x,y=normalized.y;if(x>1)x=1;else if(x<-1)x=-1;if(y>1)y=1;else if(y<-1)y=-1;return[(x+1)/2*width,(1-y)/2*height,normalized.z]};_.extend(Region.prototype,EventEmitter.prototype)},{events:16,"../vector":10,underscore:22}],25:[function(require,module,exports){(function(global){module.exports=function(){return global.WebSocket||global.MozWebSocket}})(window)},{}]},{},[1,2,3]); \ No newline at end of file diff --git a/index.html b/index.html index 1d016f3..2703a45 100644 --- a/index.html +++ b/index.html @@ -1,130 +1,13 @@ + Theremin - - + + +
Leap Theremin!
diff --git a/js/arpeggio.js b/js/arpeggio.js new file mode 100644 index 0000000..ed32a7f --- /dev/null +++ b/js/arpeggio.js @@ -0,0 +1,129 @@ +// Data +var noteNames = ['C3','D3','E3','F3','G3','A3','B3','C4','D4','E4','F4','G4','A4','B4','C5','D5','E5','F5','G5','A5','B5','C6']; +var noteFreqs = [130 , 146, 164, 174, 196, 220, 246,261 ,293 ,329 ,349 ,392 ,440 ,493 ,523 ,587 ,659 ,698, 783 ,880, 987 ,1046]; + +var scaleBase = []; + +function initialize() { + + // set up scale base + setScaleBase(majorPentatonicScale); + + // Write note data to screen + if(noteNames.length === noteFreqs.length) { // Array lengths should match + for(var i = 0; i < noteFreqs.length; i++) { + // Convert to % + y_coordinate = 100*(1-((noteFreqs[i] - noteFreqs[0])/(noteFreqs[noteFreqs.length-1] - noteFreqs[0]))); + + // Generate the note label + var div = document.createElement("div"); + div.setAttribute('class', 'floating'); + div.style.top = String(y_coordinate) + "%"; + div.textContent = noteNames[i]; + document.body.appendChild(div); + + // Generate the lines + var hr = document.createElement("hr"); + hr.setAttribute('class', 'line'); + hr.style.top = String(y_coordinate) + "%"; + document.body.appendChild(hr); + + // Hide the controller divs initially + document.getElementById("controller_one").style.visibility = "hidden"; + document.getElementById("controller_two").style.visibility = "hidden"; + } + } +} + +// Declare variables +var sampleRate = 44100; +var frequency = 440; +var audio = []; +var oscillator = []; +var gainNode = []; +for(var i = 0; i < 4; i++) { + audio[i] = new webkitAudioContext(); + + // Audio Variables + oscillator[i] = audio[i].createOscillator(); + gainNode[i] = audio[i].createGainNode(); + oscillator[i].frequency.value = 0; + // 0 - Sine wave + // 1 - Square wave + // 2 - Sawtooth + // 3 - Triangle + oscillator[i].type = 3; + oscillator[i].connect(gainNode[i]); + gainNode[i].connect(audio[i].destination); + gainNode[i].gain.value = 0; + oscillator[i].noteOn(0); +} + +// Leap motion loop +Leap.loop(function(frame) { + // Check if any hands are present + if(frame.hands.length < 1) { + gainNode[0].gain.value = 0; + gainNode[1].gain.value = 0; + document.getElementById("frequency_one").textContent = 0; + document.getElementById("volume_one").textContent = 0; + document.getElementById("frequency_two").textContent = 0; + document.getElementById("volume_two").textContent = 0; + document.getElementById("controller_one").style.visibility = "hidden"; + document.getElementById("controller_two").style.visibility = "hidden"; + return; + } + //console.log(frame); + // Declare variables + var freq = []; + var vol = []; + var hand = []; + var x = []; + var y = []; + var z = []; + + // Get xyz positions of the palm(s); only x and y are used. + for(var i = 0; i < frame.hands.length; i++) { + hand[i] = frame.hands[i]; + // [Changbai] Looks like the new leap.js cleaned up and stopped using vectors. + // x,y,z are stored in as numbers in the array instead + x[i] = hand[i].palmPosition[0]; + y[i] = hand[i].palmPosition[1]; + z[i] = hand[i].palmPosition[2]; + } + + // Modify the sound based on leap coordinates + freq[0] = Math.abs(y[0]*4 - 150); + vol[0] = Math.abs(2 - z[0]*0.01); + if(frame.hands.length === 1) { + freq[1] = 0; + vol[1] = 0; + } else { + freq[1] = Math.abs(y[1]*4 - 150); + vol[1] = Math.abs(2 - z[1]*0.01); + } + + // Output the sound to the oscillator and gainnode + oscillator[0].frequency.value = mapFreqToNote(freq[0]); + gainNode[0].gain.value = vol[0]; + oscillator[1].frequency.value = mapFreqToNote(freq[1]); + gainNode[1].gain.value = vol[1]; + + // Update HTML elements w/ frequency/gain data + document.getElementById("frequency_one").textContent = String(Math.floor(oscillator[0].frequency.value));// String(Math.floor(freq[0])); + document.getElementById("volume_one").textContent = String(Math.floor(vol[0]*25)); + document.getElementById("frequency_two").textContent = String(Math.floor(oscillator[1].frequency.value)); + document.getElementById("volume_two").textContent = String(Math.floor(vol[1]*25)); + + // Move the div based on frequency and volume + document.getElementById("controller_one").style.visibility = "visible"; + if(frame.hands.length === 2) { + document.getElementById("controller_two").style.visibility = "visible"; + } else { + document.getElementById("controller_two").style.visibility = "hidden"; + } + document.getElementById("controller_one").style.top = String(100*(1-((freq[0] - noteFreqs[0])/(noteFreqs[noteFreqs.length-1] - noteFreqs[0])))) + "%"; + document.getElementById("controller_one").style.left = String(100 - Math.abs(2 - x[0]*0.01)*25) + "%"; + document.getElementById("controller_two").style.top = String(100*(1-((freq[1] - noteFreqs[0])/(noteFreqs[noteFreqs.length-1] - noteFreqs[0])))) + "%"; + document.getElementById("controller_two").style.left = String(100 - Math.abs(2 - x[1]*0.01)*25) + "%"; +}); \ No newline at end of file diff --git a/js/scaleManager.js b/js/scaleManager.js new file mode 100644 index 0000000..b5382ec --- /dev/null +++ b/js/scaleManager.js @@ -0,0 +1,75 @@ +// Apparently noteFreqs isn't the actual frequency of the note. (lol) +// It's only used for display purpose, and uses some interesting calculation blablablabla things I don't need to care. +// BUTT! This should be very easy to research. + +// http://www.phy.mtu.edu/~suits/noteFreqs.html +// Table of frequency. +// each octave of a single note has doubled frequency +// I can have ranges of octave (of how many times it gets timed) (get it, times timed, hehehe) +// starting at C0! + +// 12 equal temperament +var basenoteFreqs = [ +16.35, // C +17.32, // C# +18.35, // D +19.45, // Eb +20.60, // E +21.83, // F +23.12, // F# +24.50, // G +25.96, // G# +27.50, // A +29.14, // Bb +30.87 // B +]; +var naturalMajorScale = [ 0, 2, 4, 5, 7, 9, 11 ]; +var naturalMinorScale = [ 0, 2, 3, 5, 7, 8, 10 ]; // this will just sound the same as major - for now +// Here's where I get overly obsessive when it's 7:38 in the morning and I prefer coding like a monkey than writing logics +var harmonicMinorScale = [ 0, 2, 3, 5, 7, 8, 11 ]; +var majorPentatonicScale = [ 0, 2, 4, 7, 9 ]; +var minorPentatonicScale = [ 0, 3, 5, 7, 10 ]; // see natural Minor Scale's comment +// oh come on now, at this point I'm just showing off. +var jazzMinorScale = [ 0, 2, 3, 5, 7, 9, 11 ]; // I actually just found that one on Wikipedia +var bluesMinorScale = [ 0, 3, 5, 6, 7, 10 ]; +var flamencoScale = [ 0, 1, 4, 5, 7, 8, 11 ]; +var whoneToneScale = [ 0, 2, 4, 6, 8, 10 ]; +var japaneseScale = [ 0, 2, 3, 7, 8]; // wow, now that I look at it, this is super sketchy +// Alright, I admit the rest are just scales on Wikipedia +var algerianScale = [ 0, 2, 3, 5, 6, 7, 10 ]; +var enigmaticScale = [ 0, 1, 4, 6, 8, 9, 11 ]; // Joe Satriani, The Enigmatic +var japaneseYoScale = [ 0, 3, 5, 7, 11 ]; +var tritoneScale = [ 0, 1, 4, 6, 7, 10 ]; // It's the devil! +var gypsyScale = [ 0, 2, 3, 6, 7, 8, 10 ]; + +var curOctave = 0; // for displaying octave + +function setScaleBase(inScale) { + for (var i = 0; i < inScale.length; i++) { + scaleBase.push(basenoteFreqs[inScale[i]]); + }; +} + +function mapFreqToNote(inFreq) { + if (inFreq == 0 ) return 0; + var tOctave = 0; + // find the clost octave the freq can ceil to + while (inFreq > basenoteFreqs[0] * Math.pow(2, tOctave)) // scale starts at C. + { + tOctave ++; + } + //console.log(tOctave); + curOctave = tOctave; + for (var i = 0; i < scaleBase.length; i++) { // for every note in a scale + // find the closest note the freq can ceil to + + if ( inFreq >= scaleBase[i] * Math.pow(2, tOctave - 1) ){ + //inFreq = scaleBase[i] * Math.pow(2, tOctave - 1); + } else { + return scaleBase[i] * Math.pow(2, tOctave - 1); + } + }; + + //console.log("inFreq: " + inFreq); + return scaleBase[0] * Math.pow(2, tOctave); +} \ No newline at end of file From 3ec1b5ce2a76c035506eef7c02d19c1ec293058d Mon Sep 17 00:00:00 2001 From: lcb931023 Date: Sat, 4 Apr 2015 17:55:50 -0400 Subject: [PATCH 2/3] update new WebAudio API names --- js/arpeggio.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/arpeggio.js b/js/arpeggio.js index ed32a7f..04db3a2 100644 --- a/js/arpeggio.js +++ b/js/arpeggio.js @@ -42,11 +42,11 @@ var audio = []; var oscillator = []; var gainNode = []; for(var i = 0; i < 4; i++) { - audio[i] = new webkitAudioContext(); + audio[i] = new AudioContext(); // Audio Variables oscillator[i] = audio[i].createOscillator(); - gainNode[i] = audio[i].createGainNode(); + gainNode[i] = audio[i].createGain(); oscillator[i].frequency.value = 0; // 0 - Sine wave // 1 - Square wave @@ -56,7 +56,7 @@ for(var i = 0; i < 4; i++) { oscillator[i].connect(gainNode[i]); gainNode[i].connect(audio[i].destination); gainNode[i].gain.value = 0; - oscillator[i].noteOn(0); + oscillator[i].start(0); } // Leap motion loop @@ -126,4 +126,4 @@ Leap.loop(function(frame) { document.getElementById("controller_one").style.left = String(100 - Math.abs(2 - x[0]*0.01)*25) + "%"; document.getElementById("controller_two").style.top = String(100*(1-((freq[1] - noteFreqs[0])/(noteFreqs[noteFreqs.length-1] - noteFreqs[0])))) + "%"; document.getElementById("controller_two").style.left = String(100 - Math.abs(2 - x[1]*0.01)*25) + "%"; -}); \ No newline at end of file +}); From bb20deb63a464ac84c001e819d30e8539c75cab0 Mon Sep 17 00:00:00 2001 From: lcb931023 Date: Sat, 4 Apr 2015 18:01:07 -0400 Subject: [PATCH 3/3] change readme back --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 1aa0566..3079f9a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,3 @@ Theremin ======== A simple theremin app for the Leap Motion. Supports up to two hands for hours and hours of fun! - -@Original Author: http://yujiangtham.com/ -@Downloaded, dissected and modified by: Changbai Li \ No newline at end of file