Comparing version 0.3.5 to 0.5.0
@@ -1,48 +0,24 @@ | ||
# Contributing to Famo.us | ||
# Contributing | ||
Active involvement from the community is essential to help make Famo.us the most | ||
capable and performant front-end JavaScript framework out there. You can help by | ||
reporting bugs, fixing bugs, adding features, contributing new modules and | ||
providing feedback. | ||
Active involvement from the community is essential to making the Famous Engine the most capable and performant JavaScript rendering engine. You can help by reporting bugs, fixing bugs, adding features, and providing feedback. | ||
## Reporting bugs and other issues | ||
Famo.us is a framework that is always testing the limits of where browsers can go. As a result, it's likely that you may encounter bugs or other issues while developing with it. | ||
Famous is a platform that is always testing the limits of where browsers can go. As a result, it's likely that you may encounter bugs or other issues while developing with it. If you think you've encountered a bug, do the following: | ||
If you think you've encountered a bug, do the following: | ||
1. Make sure you are working with the latest version of the Famous `develop` branch. | ||
2. Browse through the [issues][1] to check if anyone else has already reported the issue you're seeing. If someone has, feel free to add more information to that issue. | ||
3. If no one has yet submitted the issue you are encountering, be sure to include as much information as possible, including errors, warnings, screenshots, or videos that help us reproduce it. | ||
1. Make sure you are working with the latest version of the Famo.us `develop` branch. | ||
2. Browse through the [issues](#issues) to check if | ||
anyone else has already reported. If someone has, feel free to add more | ||
information to that issue to help us solve it. | ||
3. If no one has yet submitted the issue you are encountering, check the | ||
[guidelines for deciding where to file your issue](#issues). Please be sure | ||
to include as much information as possible, include errors, warnings, | ||
screenshots, links to a video showing the problem or code that can reproduce | ||
the issue. | ||
## Contributing code | ||
The Famo.us framework is made possible by open source | ||
contributors like you. We're very interested in getting help from the greater | ||
community, but before you start it's important that you become acquainted with | ||
our workflow. Following these guidelines below will make collaboration much | ||
smoother and increase the chances that we will accept your pull request without | ||
hiccups. | ||
The Famous platform is made possible by contributors like you. We're thrilled to get help from the greater community --- but before you start, **become acquainted with our workflow**. Following these guidelines below will make collaboration much smoother and increase the chances that we will accept your pull request. | ||
### Development process | ||
### Development Process | ||
Our development process is very similar to the approach described in the article [_A Successful Git Branching Model_][2]. Here's an overview: | ||
Our development process is very similar to the approach | ||
described in the well-known article [A Successful Git Branching Model by Vincent | ||
Driessen][git-branching-model]. Here's an overview: | ||
* Our `develop` branch is the branch upon which | ||
Famo.us developers should be basing their work on. The `develop` branch is not guaranteed to be stable. | ||
* All commits intended for `develop` should take place on your own personal | ||
fork, and be submitted via pull request when ready to the `develop` branch. | ||
* Only maintainers can accept pull requests from forks into the core Famo.us | ||
repository. | ||
* Our `develop` branch is the main development branch. | ||
* All commits intended for `develop` should be submitted via pull request. | ||
* Only maintainers can accept pull requests from forks. | ||
* Please squash your commits into a single commit before making a pull request. | ||
@@ -52,47 +28,27 @@ | ||
1. Make sure you have a [GitHub account](https://github.com/signup/free) | ||
2. [Fork famous][fork-famous] | ||
3. Keep your fork up to date. Famo.us is a fast-moving project, and things are | ||
changing all the time. It's important that any changes you make are based on | ||
the most recent version of Famo.us, since it's possible that something may | ||
have changed that breaks your pull request or invalidates its need. | ||
4. Make sure you have a [Contributor License Agreement][cla] on file. | ||
5. Read on ... | ||
1. Make sure you have a [GitHub account][4]. | ||
2. [Fork famous][5]. | ||
3. Keep your fork up to date. | ||
4. Make sure you have a [Contributor License Agreement][6] on file. | ||
### Contributor License Agreement | ||
Before we can accept any contributions to Famo.us, we first require that all | ||
individuals or companies agree to our Contributor License Agreement (CLA). The e-mail | ||
address used in the pull request will be used to check if a CLA has already been | ||
filed, so be sure to list all email addresses that you might use to submit your | ||
pull requests when filling it out. Our CLA can be found [here][cla]. | ||
Before we can accept any contributions to Famous, we first require that all individuals or companies agree to our [Contributor License Agreement (CLA)][6]. | ||
### Testing and Linting | ||
The e-mail address used in the pull request will be used to check if a CLA has already been filed, so be sure to list all email addresses that you might use to submit your pull requests when filling it out. | ||
Travis-ci is integrated into all of our submodules to automatically run tests on our codebase. All pull requests must pass our tests before they can be merged. Currently, the only test we support is a linting test. This ensures a consistently styled codebase. Before making a pull request, please run our linter locally. From the submodule directory, execute | ||
### Testing | ||
```js | ||
npm install | ||
npm test | ||
``` | ||
The created `node_modules` folder will be ignored in your push by our `.gitignore` file. | ||
All pull requests must pass our tests before they can be merged. Any new functionality should have corresponding tests to ensure they are working properly. | ||
### Bug fixes | ||
If you'd like to contribute a fix for a bug you've encountered, first read up on | ||
[how to report a bug](#reporting-bugs-and-other-issues) and report it so we are | ||
aware of the issue. By filing the issue first, we may be able to provide you | ||
with some insight that guides you in the right direction. | ||
If you'd like to contribute a fix for a bug, first read up on [how to report a bug][7]. By filing the issue first, we may be able to provide you with some insight that guides you in the right direction. | ||
[famous-issues]: https://github.com/famous/famous/issues | ||
[famous]: https://github.com/famous/famous | ||
[git-branching-model]: http://nvie.com/posts/a-successful-git-branching-model/ | ||
[semver]: http://semver.org/ | ||
[fork-famous]: https://github.com/Famous/famous/fork | ||
[unix-principles]: http://www.faqs.org/docs/artu/ch01s06.html | ||
[esr]: http://www.catb.org/esr/ | ||
[taoup]: http://www.catb.org/esr/writings/taoup/ | ||
[modifying-ojects-considered-bad]: http://perfectionkills.com/whats-wrong-with-extending-the-dom/ | ||
[cla]: http://famo.us/cla | ||
[1]: https://github.com/Famous/engine/issues | ||
[2]: http://nvie.com/posts/a-successful-git-branching-model/ | ||
[3]: https://github.com/Famous/engine | ||
[4]: https://github.com/signup/free | ||
[5]: https://github.com/Famous/engine/fork | ||
[6]: http://famous.org/cla | ||
[7]: #how-to-report-a-bug |
@@ -0,19 +1,36 @@ | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
module.exports = { | ||
Context: require('./Context'), | ||
ElementAllocator: require('./ElementAllocator'), | ||
ElementOutput: require('./ElementOutput'), | ||
Engine: require('./Engine'), | ||
Entity: require('./Entity'), | ||
EventEmitter: require('./EventEmitter'), | ||
EventHandler: require('./EventHandler'), | ||
Group: require('./Group'), | ||
Modifier: require('./Modifier'), | ||
OptionsManager: require('./OptionsManager'), | ||
RenderNode: require('./RenderNode'), | ||
Scene: require('./Scene'), | ||
SpecParser: require('./SpecParser'), | ||
Surface: require('./Surface'), | ||
Transform: require('./Transform'), | ||
View: require('./View'), | ||
ViewSequence: require('./ViewSequence') | ||
Clock: require('./Clock'), | ||
Event: require('./Event'), | ||
Scene: require('./Scene'), | ||
FamousEngine: require('./FamousEngine'), | ||
Dispatch: require('./Dispatch'), | ||
Node: require('./Node'), | ||
Size: require('./Size'), | ||
Transform: require('./Transform') | ||
}; |
@@ -1,119 +0,141 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
/*jshint -W079 */ | ||
'use strict'; | ||
var Dispatch = require('./Dispatch'); | ||
var Node = require('./Node'); | ||
var Size = require('./Size'); | ||
/** | ||
* Scene is the bottom of the scene graph. It is it's own | ||
* parent and provides the global updater to the scene graph. | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* @class Scene | ||
* @constructor | ||
* | ||
* @param {String} selector a string which is a dom selector | ||
* signifying which dom element the context | ||
* should be set upon | ||
* @param {Famous} updater a class which conforms to Famous' interface | ||
* it needs to be able to send methods to | ||
* the renderers and update nodes in the scene graph | ||
*/ | ||
var Transform = require('./Transform'); | ||
var Modifier = require('./Modifier'); | ||
var RenderNode = require('./RenderNode'); | ||
function Scene(definition) { | ||
this.id = null; | ||
this._objects = null; | ||
this.node = new RenderNode(); | ||
this._definition = null; | ||
if (definition) | ||
this.load(definition); | ||
function Scene (selector, updater) { | ||
if (!selector) throw new Error('Scene needs to be created with a DOM selector'); | ||
if (!updater) throw new Error('Scene needs to be created with a class like Famous'); | ||
Node.call(this); // Scene inherits from node | ||
this._updater = updater; // The updater that will both | ||
// send messages to the renderers | ||
// and update dirty nodes | ||
this._dispatch = new Dispatch(this); // instantiates a dispatcher | ||
// to send events to the scene | ||
// graph below this context | ||
this._selector = selector; // reference to the DOM selector | ||
// that represents the elemnent | ||
// in the dom that this context | ||
// inhabits | ||
this.onMount(this, selector); // Mount the context to itself | ||
// (it is its own parent) | ||
this._updater // message a request for the dom | ||
.message('NEED_SIZE_FOR') // size of the context so that | ||
.message(selector); // the scene graph has a total size | ||
this.show(); // the context begins shown (it's already present in the dom) | ||
} | ||
var _MATRIX_GENERATORS = { | ||
'translate': Transform.translate, | ||
'rotate': Transform.rotate, | ||
'rotateX': Transform.rotateX, | ||
'rotateY': Transform.rotateY, | ||
'rotateZ': Transform.rotateZ, | ||
'rotateAxis': Transform.rotateAxis, | ||
'scale': Transform.scale, | ||
'skew': Transform.skew, | ||
'matrix3d': function () { | ||
return arguments; | ||
} | ||
// Scene inherits from node | ||
Scene.prototype = Object.create(Node.prototype); | ||
Scene.prototype.constructor = Scene; | ||
/** | ||
* Scene getUpdater function returns the passed in updater | ||
* | ||
* @return {Famous} the updater for this Scene | ||
*/ | ||
Scene.prototype.getUpdater = function getUpdater () { | ||
return this._updater; | ||
}; | ||
Scene.prototype.create = function create() { | ||
return new Scene(this._definition); | ||
/** | ||
* Returns the selector that the context was instantiated with | ||
* | ||
* @return {String} dom selector | ||
*/ | ||
Scene.prototype.getSelector = function getSelector () { | ||
return this._selector; | ||
}; | ||
function _resolveTransformMatrix(matrixDefinition) { | ||
for (var type in _MATRIX_GENERATORS) { | ||
if (type in matrixDefinition) { | ||
var args = matrixDefinition[type]; | ||
if (!(args instanceof Array)) | ||
args = [args]; | ||
return _MATRIX_GENERATORS[type].apply(this, args); | ||
} | ||
/** | ||
* Returns the dispatcher of the context. Used to send events | ||
* to the nodes in the scene graph. | ||
* | ||
* @return {Dispatch} the Scene's Dispatch | ||
*/ | ||
Scene.prototype.getDispatch = function getDispatch () { | ||
return this._dispatch; | ||
}; | ||
/** | ||
* Receives an event. If the event is 'CONTEXT_RESIZE' it sets the size of the scene | ||
* graph to the payload, which must be an array of numbers of at least | ||
* length three representing the pixel size in 3 dimensions. | ||
* | ||
* @param {String} event the name of the event being received | ||
* @param {*} payload the object being sent | ||
* | ||
* @return {undefined} undefined | ||
*/ | ||
Scene.prototype.onReceive = function onReceive (event, payload) { | ||
// TODO: In the future the dom element that the context is attached to | ||
// should have a representation as a component. It would be render sized | ||
// and the context would receive its size the same way that any render size | ||
// component receives its size. | ||
if (event === 'CONTEXT_RESIZE') { | ||
if (payload.length < 2) | ||
throw new Error( | ||
'CONTEXT_RESIZE\'s payload needs to be at least a pair' + | ||
' of pixel sizes' | ||
); | ||
this.setSizeMode(Size.ABSOLUTE, Size.ABSOLUTE, Size.ABSOLUTE); | ||
this.setAbsoluteSize(payload[0], | ||
payload[1], | ||
payload[2] ? payload[2] : 0); | ||
} | ||
} | ||
function _parseTransform(definition) { | ||
var transformDefinition = definition.transform; | ||
var opacity = definition.opacity; | ||
var origin = definition.origin; | ||
var align = definition.align; | ||
var size = definition.size; | ||
var transform = Transform.identity; | ||
if (transformDefinition instanceof Array) { | ||
if (transformDefinition.length === 16 && typeof transformDefinition[0] === 'number') { | ||
transform = transformDefinition; | ||
} else { | ||
for (var i = 0; i < transformDefinition.length; i++) { | ||
transform = Transform.multiply(transform, _resolveTransformMatrix(transformDefinition[i])); | ||
} | ||
} | ||
} else if (transformDefinition instanceof Function) { | ||
transform = transformDefinition; | ||
} else if (transformDefinition instanceof Object) { | ||
transform = _resolveTransformMatrix(transformDefinition); | ||
} | ||
var result = new Modifier({ | ||
transform: transform, | ||
opacity: opacity, | ||
origin: origin, | ||
align: align, | ||
size: size | ||
}); | ||
return result; | ||
} | ||
function _parseArray(definition) { | ||
var result = new RenderNode(); | ||
for (var i = 0; i < definition.length; i++) { | ||
var obj = _parse.call(this, definition[i]); | ||
if (obj) | ||
result.add(obj); | ||
} | ||
return result; | ||
} | ||
function _parse(definition) { | ||
var result; | ||
var id; | ||
if (definition instanceof Array) { | ||
result = _parseArray.call(this, definition); | ||
} else { | ||
id = this._objects.length; | ||
if (definition.render && definition.render instanceof Function) { | ||
result = definition; | ||
} else if (definition.target) { | ||
var targetObj = _parse.call(this, definition.target); | ||
var obj = _parseTransform.call(this, definition); | ||
result = new RenderNode(obj); | ||
result.add(targetObj); | ||
if (definition.id) | ||
this.id[definition.id] = obj; | ||
} else if (definition.id) { | ||
result = new RenderNode(); | ||
this.id[definition.id] = result; | ||
} | ||
} | ||
this._objects[id] = result; | ||
return result; | ||
} | ||
Scene.prototype.load = function load(definition) { | ||
this._definition = definition; | ||
this.id = {}; | ||
this._objects = []; | ||
this.node.set(_parse.call(this, definition)); | ||
}; | ||
Scene.prototype.add = function add() { | ||
return this.node.add.apply(this.node, arguments); | ||
}; | ||
Scene.prototype.render = function render() { | ||
return this.node.render.apply(this.node, arguments); | ||
}; | ||
module.exports = Scene; | ||
module.exports = Scene; | ||
@@ -1,704 +0,162 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
/** | ||
* The transform class is responsible for calculating the transform of a particular | ||
* node from the data on the node and its parent | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* @constructor Transform | ||
*/ | ||
var Transform = {}; | ||
Transform.precision = 0.000001; | ||
Transform.identity = [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
Transform.multiply4x4 = function multiply4x4(a, b) { | ||
return [ | ||
a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3], | ||
a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3], | ||
a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3], | ||
a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3], | ||
a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7], | ||
a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7], | ||
a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7], | ||
a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7], | ||
a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11], | ||
a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11], | ||
a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11], | ||
a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11], | ||
a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15], | ||
a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15], | ||
a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15], | ||
a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15] | ||
]; | ||
}; | ||
Transform.multiply = function multiply(a, b) { | ||
return [ | ||
a[0] * b[0] + a[4] * b[1] + a[8] * b[2], | ||
a[1] * b[0] + a[5] * b[1] + a[9] * b[2], | ||
a[2] * b[0] + a[6] * b[1] + a[10] * b[2], | ||
0, | ||
a[0] * b[4] + a[4] * b[5] + a[8] * b[6], | ||
a[1] * b[4] + a[5] * b[5] + a[9] * b[6], | ||
a[2] * b[4] + a[6] * b[5] + a[10] * b[6], | ||
0, | ||
a[0] * b[8] + a[4] * b[9] + a[8] * b[10], | ||
a[1] * b[8] + a[5] * b[9] + a[9] * b[10], | ||
a[2] * b[8] + a[6] * b[9] + a[10] * b[10], | ||
0, | ||
a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12], | ||
a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13], | ||
a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14], | ||
1 | ||
]; | ||
}; | ||
Transform.thenMove = function thenMove(m, t) { | ||
if (!t[2]) | ||
t[2] = 0; | ||
return [ | ||
m[0], | ||
m[1], | ||
m[2], | ||
0, | ||
m[4], | ||
m[5], | ||
m[6], | ||
0, | ||
m[8], | ||
m[9], | ||
m[10], | ||
0, | ||
m[12] + t[0], | ||
m[13] + t[1], | ||
m[14] + t[2], | ||
1 | ||
]; | ||
}; | ||
Transform.moveThen = function moveThen(v, m) { | ||
if (!v[2]) | ||
v[2] = 0; | ||
var t0 = v[0] * m[0] + v[1] * m[4] + v[2] * m[8]; | ||
var t1 = v[0] * m[1] + v[1] * m[5] + v[2] * m[9]; | ||
var t2 = v[0] * m[2] + v[1] * m[6] + v[2] * m[10]; | ||
return Transform.thenMove(m, [ | ||
t0, | ||
t1, | ||
t2 | ||
]); | ||
}; | ||
Transform.translate = function translate(x, y, z) { | ||
if (z === undefined) | ||
z = 0; | ||
return [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
x, | ||
y, | ||
z, | ||
1 | ||
]; | ||
}; | ||
Transform.thenScale = function thenScale(m, s) { | ||
return [ | ||
s[0] * m[0], | ||
s[1] * m[1], | ||
s[2] * m[2], | ||
0, | ||
s[0] * m[4], | ||
s[1] * m[5], | ||
s[2] * m[6], | ||
0, | ||
s[0] * m[8], | ||
s[1] * m[9], | ||
s[2] * m[10], | ||
0, | ||
s[0] * m[12], | ||
s[1] * m[13], | ||
s[2] * m[14], | ||
1 | ||
]; | ||
}; | ||
Transform.scale = function scale(x, y, z) { | ||
if (z === undefined) | ||
z = 1; | ||
if (y === undefined) | ||
y = x; | ||
return [ | ||
x, | ||
0, | ||
0, | ||
0, | ||
0, | ||
y, | ||
0, | ||
0, | ||
0, | ||
0, | ||
z, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.rotateX = function rotateX(theta) { | ||
var cosTheta = Math.cos(theta); | ||
var sinTheta = Math.sin(theta); | ||
return [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
cosTheta, | ||
sinTheta, | ||
0, | ||
0, | ||
-sinTheta, | ||
cosTheta, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.rotateY = function rotateY(theta) { | ||
var cosTheta = Math.cos(theta); | ||
var sinTheta = Math.sin(theta); | ||
return [ | ||
cosTheta, | ||
0, | ||
-sinTheta, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
sinTheta, | ||
0, | ||
cosTheta, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.rotateZ = function rotateZ(theta) { | ||
var cosTheta = Math.cos(theta); | ||
var sinTheta = Math.sin(theta); | ||
return [ | ||
cosTheta, | ||
sinTheta, | ||
0, | ||
0, | ||
-sinTheta, | ||
cosTheta, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.rotate = function rotate(phi, theta, psi) { | ||
var cosPhi = Math.cos(phi); | ||
var sinPhi = Math.sin(phi); | ||
var cosTheta = Math.cos(theta); | ||
var sinTheta = Math.sin(theta); | ||
var cosPsi = Math.cos(psi); | ||
var sinPsi = Math.sin(psi); | ||
var result = [ | ||
cosTheta * cosPsi, | ||
cosPhi * sinPsi + sinPhi * sinTheta * cosPsi, | ||
sinPhi * sinPsi - cosPhi * sinTheta * cosPsi, | ||
0, | ||
-cosTheta * sinPsi, | ||
cosPhi * cosPsi - sinPhi * sinTheta * sinPsi, | ||
sinPhi * cosPsi + cosPhi * sinTheta * sinPsi, | ||
0, | ||
sinTheta, | ||
-sinPhi * cosTheta, | ||
cosPhi * cosTheta, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
return result; | ||
}; | ||
Transform.rotateAxis = function rotateAxis(v, theta) { | ||
var sinTheta = Math.sin(theta); | ||
var cosTheta = Math.cos(theta); | ||
var verTheta = 1 - cosTheta; | ||
var xxV = v[0] * v[0] * verTheta; | ||
var xyV = v[0] * v[1] * verTheta; | ||
var xzV = v[0] * v[2] * verTheta; | ||
var yyV = v[1] * v[1] * verTheta; | ||
var yzV = v[1] * v[2] * verTheta; | ||
var zzV = v[2] * v[2] * verTheta; | ||
var xs = v[0] * sinTheta; | ||
var ys = v[1] * sinTheta; | ||
var zs = v[2] * sinTheta; | ||
var result = [ | ||
xxV + cosTheta, | ||
xyV + zs, | ||
xzV - ys, | ||
0, | ||
xyV - zs, | ||
yyV + cosTheta, | ||
yzV + xs, | ||
0, | ||
xzV + ys, | ||
yzV - xs, | ||
zzV + cosTheta, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
return result; | ||
}; | ||
Transform.aboutOrigin = function aboutOrigin(v, m) { | ||
var t0 = v[0] - (v[0] * m[0] + v[1] * m[4] + v[2] * m[8]); | ||
var t1 = v[1] - (v[0] * m[1] + v[1] * m[5] + v[2] * m[9]); | ||
var t2 = v[2] - (v[0] * m[2] + v[1] * m[6] + v[2] * m[10]); | ||
return Transform.thenMove(m, [ | ||
t0, | ||
t1, | ||
t2 | ||
]); | ||
}; | ||
Transform.skew = function skew(phi, theta, psi) { | ||
return [ | ||
1, | ||
Math.tan(theta), | ||
0, | ||
0, | ||
Math.tan(psi), | ||
1, | ||
0, | ||
0, | ||
0, | ||
Math.tan(phi), | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.skewX = function skewX(angle) { | ||
return [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
Math.tan(angle), | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.skewY = function skewY(angle) { | ||
return [ | ||
1, | ||
Math.tan(angle), | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.perspective = function perspective(focusZ) { | ||
return [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
-1 / focusZ, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
Transform.getTranslate = function getTranslate(m) { | ||
return [ | ||
m[12], | ||
m[13], | ||
m[14] | ||
]; | ||
}; | ||
Transform.inverse = function inverse(m) { | ||
var c0 = m[5] * m[10] - m[6] * m[9]; | ||
var c1 = m[4] * m[10] - m[6] * m[8]; | ||
var c2 = m[4] * m[9] - m[5] * m[8]; | ||
var c4 = m[1] * m[10] - m[2] * m[9]; | ||
var c5 = m[0] * m[10] - m[2] * m[8]; | ||
var c6 = m[0] * m[9] - m[1] * m[8]; | ||
var c8 = m[1] * m[6] - m[2] * m[5]; | ||
var c9 = m[0] * m[6] - m[2] * m[4]; | ||
var c10 = m[0] * m[5] - m[1] * m[4]; | ||
var detM = m[0] * c0 - m[1] * c1 + m[2] * c2; | ||
var invD = 1 / detM; | ||
var result = [ | ||
invD * c0, | ||
-invD * c4, | ||
invD * c8, | ||
0, | ||
-invD * c1, | ||
invD * c5, | ||
-invD * c9, | ||
0, | ||
invD * c2, | ||
-invD * c6, | ||
invD * c10, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
result[12] = -m[12] * result[0] - m[13] * result[4] - m[14] * result[8]; | ||
result[13] = -m[12] * result[1] - m[13] * result[5] - m[14] * result[9]; | ||
result[14] = -m[12] * result[2] - m[13] * result[6] - m[14] * result[10]; | ||
return result; | ||
}; | ||
Transform.transpose = function transpose(m) { | ||
return [ | ||
m[0], | ||
m[4], | ||
m[8], | ||
m[12], | ||
m[1], | ||
m[5], | ||
m[9], | ||
m[13], | ||
m[2], | ||
m[6], | ||
m[10], | ||
m[14], | ||
m[3], | ||
m[7], | ||
m[11], | ||
m[15] | ||
]; | ||
}; | ||
function _normSquared(v) { | ||
return v.length === 2 ? v[0] * v[0] + v[1] * v[1] : v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; | ||
function Transform () { | ||
this._matrix = new Float32Array(16); | ||
} | ||
function _norm(v) { | ||
return Math.sqrt(_normSquared(v)); | ||
} | ||
function _sign(n) { | ||
return n < 0 ? -1 : 1; | ||
} | ||
Transform.interpret = function interpret(M) { | ||
var x = [ | ||
M[0], | ||
M[1], | ||
M[2] | ||
]; | ||
var sgn = _sign(x[0]); | ||
var xNorm = _norm(x); | ||
var v = [ | ||
x[0] + sgn * xNorm, | ||
x[1], | ||
x[2] | ||
]; | ||
var mult = 2 / _normSquared(v); | ||
if (mult >= Infinity) { | ||
return { | ||
translate: Transform.getTranslate(M), | ||
rotate: [ | ||
0, | ||
0, | ||
0 | ||
], | ||
scale: [ | ||
0, | ||
0, | ||
0 | ||
], | ||
skew: [ | ||
0, | ||
0, | ||
0 | ||
] | ||
}; | ||
} | ||
var Q1 = [ | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
Q1[0] = 1 - mult * v[0] * v[0]; | ||
Q1[5] = 1 - mult * v[1] * v[1]; | ||
Q1[10] = 1 - mult * v[2] * v[2]; | ||
Q1[1] = -mult * v[0] * v[1]; | ||
Q1[2] = -mult * v[0] * v[2]; | ||
Q1[6] = -mult * v[1] * v[2]; | ||
Q1[4] = Q1[1]; | ||
Q1[8] = Q1[2]; | ||
Q1[9] = Q1[6]; | ||
var MQ1 = Transform.multiply(Q1, M); | ||
var x2 = [ | ||
MQ1[5], | ||
MQ1[6] | ||
]; | ||
var sgn2 = _sign(x2[0]); | ||
var x2Norm = _norm(x2); | ||
var v2 = [ | ||
x2[0] + sgn2 * x2Norm, | ||
x2[1] | ||
]; | ||
var mult2 = 2 / _normSquared(v2); | ||
var Q2 = [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
Q2[5] = 1 - mult2 * v2[0] * v2[0]; | ||
Q2[10] = 1 - mult2 * v2[1] * v2[1]; | ||
Q2[6] = -mult2 * v2[0] * v2[1]; | ||
Q2[9] = Q2[6]; | ||
var Q = Transform.multiply(Q2, Q1); | ||
var R = Transform.multiply(Q, M); | ||
var remover = Transform.scale(R[0] < 0 ? -1 : 1, R[5] < 0 ? -1 : 1, R[10] < 0 ? -1 : 1); | ||
R = Transform.multiply(R, remover); | ||
Q = Transform.multiply(remover, Q); | ||
var result = {}; | ||
result.translate = Transform.getTranslate(M); | ||
result.rotate = [ | ||
Math.atan2(-Q[6], Q[10]), | ||
Math.asin(Q[2]), | ||
Math.atan2(-Q[1], Q[0]) | ||
]; | ||
if (!result.rotate[0]) { | ||
result.rotate[0] = 0; | ||
result.rotate[2] = Math.atan2(Q[4], Q[5]); | ||
} | ||
result.scale = [ | ||
R[0], | ||
R[5], | ||
R[10] | ||
]; | ||
result.skew = [ | ||
Math.atan2(R[9], result.scale[2]), | ||
Math.atan2(R[8], result.scale[2]), | ||
Math.atan2(R[4], result.scale[0]) | ||
]; | ||
if (Math.abs(result.rotate[0]) + Math.abs(result.rotate[2]) > 1.5 * Math.PI) { | ||
result.rotate[1] = Math.PI - result.rotate[1]; | ||
if (result.rotate[1] > Math.PI) | ||
result.rotate[1] -= 2 * Math.PI; | ||
if (result.rotate[1] < -Math.PI) | ||
result.rotate[1] += 2 * Math.PI; | ||
if (result.rotate[0] < 0) | ||
result.rotate[0] += Math.PI; | ||
else | ||
result.rotate[0] -= Math.PI; | ||
if (result.rotate[2] < 0) | ||
result.rotate[2] += Math.PI; | ||
else | ||
result.rotate[2] -= Math.PI; | ||
} | ||
return result; | ||
/** | ||
* Returns the last calculated transform | ||
* | ||
* @return {Array} a transform | ||
*/ | ||
Transform.prototype.get = function get () { | ||
return this._matrix; | ||
}; | ||
Transform.average = function average(M1, M2, t) { | ||
t = t === undefined ? 0.5 : t; | ||
var specM1 = Transform.interpret(M1); | ||
var specM2 = Transform.interpret(M2); | ||
var specAvg = { | ||
translate: [ | ||
0, | ||
0, | ||
0 | ||
], | ||
rotate: [ | ||
0, | ||
0, | ||
0 | ||
], | ||
scale: [ | ||
0, | ||
0, | ||
0 | ||
], | ||
skew: [ | ||
0, | ||
0, | ||
0 | ||
] | ||
}; | ||
for (var i = 0; i < 3; i++) { | ||
specAvg.translate[i] = (1 - t) * specM1.translate[i] + t * specM2.translate[i]; | ||
specAvg.rotate[i] = (1 - t) * specM1.rotate[i] + t * specM2.rotate[i]; | ||
specAvg.scale[i] = (1 - t) * specM1.scale[i] + t * specM2.scale[i]; | ||
specAvg.skew[i] = (1 - t) * specM1.skew[i] + t * specM2.skew[i]; | ||
} | ||
return Transform.build(specAvg); | ||
/** | ||
* Uses the parent transform, the node's spec, the node's size, and the parent's size | ||
* to calculate a final transform for the node. Returns true if the transform has changed. | ||
* | ||
* @param {Array} parentMatrix the parent matrix | ||
* @param {Node.Spec} spec the target node's spec | ||
* @param {Array} mySize the size of the node | ||
* @param {Array} parentSize the size of the parent | ||
* @param {Array} target the target array to write the resulting transform to | ||
* | ||
* @return {Boolean} whether or not the transform changed | ||
*/ | ||
Transform.prototype.fromSpecWithParent = function fromSpecWithParent (parentMatrix, spec, mySize, parentSize, target) { | ||
target = target ? target : this._matrix; | ||
// local cache of everything | ||
var t00 = target[0]; | ||
var t01 = target[1]; | ||
var t02 = target[2]; | ||
var t10 = target[4]; | ||
var t11 = target[5]; | ||
var t12 = target[6]; | ||
var t20 = target[8]; | ||
var t21 = target[9]; | ||
var t22 = target[10]; | ||
var t30 = target[12]; | ||
var t31 = target[13]; | ||
var t32 = target[14]; | ||
var p00 = parentMatrix[0]; | ||
var p01 = parentMatrix[1]; | ||
var p02 = parentMatrix[2]; | ||
var p10 = parentMatrix[4]; | ||
var p11 = parentMatrix[5]; | ||
var p12 = parentMatrix[6]; | ||
var p20 = parentMatrix[8]; | ||
var p21 = parentMatrix[9]; | ||
var p22 = parentMatrix[10]; | ||
var p30 = parentMatrix[12]; | ||
var p31 = parentMatrix[13]; | ||
var p32 = parentMatrix[14]; | ||
var posX = spec.vectors.position[0]; | ||
var posY = spec.vectors.position[1]; | ||
var posZ = spec.vectors.position[2]; | ||
var rotX = spec.vectors.rotation[0]; | ||
var rotY = spec.vectors.rotation[1]; | ||
var rotZ = spec.vectors.rotation[2]; | ||
var rotW = spec.vectors.rotation[3]; | ||
var scaleX = spec.vectors.scale[0]; | ||
var scaleY = spec.vectors.scale[1]; | ||
var scaleZ = spec.vectors.scale[2]; | ||
var alignX = spec.offsets.align[0] * parentSize[0]; | ||
var alignY = spec.offsets.align[1] * parentSize[1]; | ||
var alignZ = spec.offsets.align[2] * parentSize[2]; | ||
var mountPointX = spec.offsets.mountPoint[0] * mySize[0]; | ||
var mountPointY = spec.offsets.mountPoint[1] * mySize[1]; | ||
var mountPointZ = spec.offsets.mountPoint[2] * mySize[2]; | ||
var originX = spec.offsets.origin[0] * mySize[0]; | ||
var originY = spec.offsets.origin[1] * mySize[1]; | ||
var originZ = spec.offsets.origin[2] * mySize[2]; | ||
var wx = rotW * rotX; | ||
var wy = rotW * rotY; | ||
var wz = rotW * rotZ; | ||
var xx = rotX * rotX; | ||
var yy = rotY * rotY; | ||
var zz = rotZ * rotZ; | ||
var xy = rotX * rotY; | ||
var xz = rotX * rotZ; | ||
var yz = rotY * rotZ; | ||
var rs0 = (1 - 2 * (yy + zz)) * scaleX; | ||
var rs1 = (2 * (xy + wz)) * scaleX; | ||
var rs2 = (2 * (xz - wy)) * scaleX; | ||
var rs3 = (2 * (xy - wz)) * scaleY; | ||
var rs4 = (1 - 2 * (xx + zz)) * scaleY; | ||
var rs5 = (2 * (yz + wx)) * scaleY; | ||
var rs6 = (2 * (xz + wy)) * scaleZ; | ||
var rs7 = (2 * (yz - wx)) * scaleZ; | ||
var rs8 = (1 - 2 * (xx + yy)) * scaleZ; | ||
var tx = alignX + posX - mountPointX + originX - (rs0 * originX + rs3 * originY + rs6 * originZ); | ||
var ty = alignY + posY - mountPointY + originY - (rs1 * originX + rs4 * originY + rs7 * originZ); | ||
var tz = alignZ + posZ - mountPointZ + originZ - (rs2 * originX + rs5 * originY + rs8 * originZ); | ||
target[0] = p00 * rs0 + p10 * rs1 + p20 * rs2; | ||
target[1] = p01 * rs0 + p11 * rs1 + p21 * rs2; | ||
target[2] = p02 * rs0 + p12 * rs1 + p22 * rs2; | ||
target[3] = 0; | ||
target[4] = p00 * rs3 + p10 * rs4 + p20 * rs5; | ||
target[5] = p01 * rs3 + p11 * rs4 + p21 * rs5; | ||
target[6] = p02 * rs3 + p12 * rs4 + p22 * rs5; | ||
target[7] = 0; | ||
target[8] = p00 * rs6 + p10 * rs7 + p20 * rs8; | ||
target[9] = p01 * rs6 + p11 * rs7 + p21 * rs8; | ||
target[10] = p02 * rs6 + p12 * rs7 + p22 * rs8; | ||
target[11] = 0; | ||
target[12] = p00 * tx + p10 * ty + p20 * tz + p30; | ||
target[13] = p01 * tx + p11 * ty + p21 * tz + p31; | ||
target[14] = p02 * tx + p12 * ty + p22 * tz + p32; | ||
target[15] = 1; | ||
return t00 !== target[0] || | ||
t01 !== target[1] || | ||
t02 !== target[2] || | ||
t10 !== target[4] || | ||
t11 !== target[5] || | ||
t12 !== target[6] || | ||
t20 !== target[8] || | ||
t21 !== target[9] || | ||
t22 !== target[10] || | ||
t30 !== target[12] || | ||
t31 !== target[13] || | ||
t32 !== target[14]; | ||
}; | ||
Transform.build = function build(spec) { | ||
var scaleMatrix = Transform.scale(spec.scale[0], spec.scale[1], spec.scale[2]); | ||
var skewMatrix = Transform.skew(spec.skew[0], spec.skew[1], spec.skew[2]); | ||
var rotateMatrix = Transform.rotate(spec.rotate[0], spec.rotate[1], spec.rotate[2]); | ||
return Transform.thenMove(Transform.multiply(Transform.multiply(rotateMatrix, skewMatrix), scaleMatrix), spec.translate); | ||
}; | ||
Transform.equals = function equals(a, b) { | ||
return !Transform.notEquals(a, b); | ||
}; | ||
Transform.notEquals = function notEquals(a, b) { | ||
if (a === b) | ||
return false; | ||
return !(a && b) || a[12] !== b[12] || a[13] !== b[13] || a[14] !== b[14] || a[0] !== b[0] || a[1] !== b[1] || a[2] !== b[2] || a[4] !== b[4] || a[5] !== b[5] || a[6] !== b[6] || a[8] !== b[8] || a[9] !== b[9] || a[10] !== b[10]; | ||
}; | ||
Transform.normalizeRotation = function normalizeRotation(rotation) { | ||
var result = rotation.slice(0); | ||
if (result[0] === Math.PI * 0.5 || result[0] === -Math.PI * 0.5) { | ||
result[0] = -result[0]; | ||
result[1] = Math.PI - result[1]; | ||
result[2] -= Math.PI; | ||
} | ||
if (result[0] > Math.PI * 0.5) { | ||
result[0] = result[0] - Math.PI; | ||
result[1] = Math.PI - result[1]; | ||
result[2] -= Math.PI; | ||
} | ||
if (result[0] < -Math.PI * 0.5) { | ||
result[0] = result[0] + Math.PI; | ||
result[1] = -Math.PI - result[1]; | ||
result[2] -= Math.PI; | ||
} | ||
while (result[1] < -Math.PI) | ||
result[1] += 2 * Math.PI; | ||
while (result[1] >= Math.PI) | ||
result[1] -= 2 * Math.PI; | ||
while (result[2] < -Math.PI) | ||
result[2] += 2 * Math.PI; | ||
while (result[2] >= Math.PI) | ||
result[2] -= 2 * Math.PI; | ||
return result; | ||
}; | ||
Transform.inFront = [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0.001, | ||
1 | ||
]; | ||
Transform.behind = [ | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1, | ||
0, | ||
0, | ||
0, | ||
-0.001, | ||
1 | ||
]; | ||
module.exports = Transform; | ||
module.exports = Transform; |
51
index.js
@@ -0,13 +1,42 @@ | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
module.exports = { | ||
core: require('./core'), | ||
events: require('./events'), | ||
inputs: require('./inputs'), | ||
math: require('./math'), | ||
modifiers: require('./modifiers'), | ||
physics: require('./physics'), | ||
surfaces: require('./surfaces'), | ||
transitions: require('./transitions'), | ||
utilities: require('./utilities'), | ||
views: require('./views'), | ||
widgets: require('./widgets') | ||
components: require('./components'), | ||
core: require('./core'), | ||
renderLoops: require('./render-loops'), | ||
domRenderables: require('./dom-renderables'), | ||
domRenderers: require('./dom-renderers'), | ||
math: require('./math'), | ||
physics: require('./physics'), | ||
renderers: require('./renderers'), | ||
transitions: require('./transitions'), | ||
utilities: require('./utilities'), | ||
webglRenderables: require('./webgl-renderables'), | ||
webglRenderers: require('./webgl-renderers'), | ||
webglGeometries: require('./webgl-geometries'), | ||
webglMaterials: require('./webgl-materials'), | ||
webglShaders: require('./webgl-shaders'), | ||
polyfills: require('./polyfills') | ||
}; |
@@ -0,7 +1,31 @@ | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
module.exports = { | ||
Matrix: require('./Matrix'), | ||
Quaternion: require('./Quaternion'), | ||
Random: require('./Random'), | ||
Utilities: require('./Utilities'), | ||
Vector: require('./Vector') | ||
Mat33: require('./Mat33'), | ||
Quaternion: require('./Quaternion'), | ||
Vec2: require('./Vec2'), | ||
Vec3: require('./Vec3') | ||
}; | ||
@@ -1,33 +0,64 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
var Matrix = require('./Matrix'); | ||
'use strict'; | ||
var sin = Math.sin; | ||
var cos = Math.cos; | ||
var asin = Math.asin; | ||
var acos = Math.acos; | ||
var atan2 = Math.atan2; | ||
var sqrt = Math.sqrt; | ||
/** | ||
* A vector-like object used to represent rotations. If theta is the angle of | ||
* rotation, and (x', y', z') is a normalized vector representing the axis of | ||
* rotation, then w = cos(theta/2), x = sin(theta/2)*x', y = sin(theta/2)*y', | ||
* and z = sin(theta/2)*z'. | ||
* | ||
* @class Quaternion | ||
* | ||
* @param {Number} w The w component. | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
*/ | ||
function Quaternion(w, x, y, z) { | ||
if (arguments.length === 1) | ||
this.set(w); | ||
else { | ||
this.w = w !== undefined ? w : 1; | ||
this.x = x !== undefined ? x : 0; | ||
this.y = y !== undefined ? y : 0; | ||
this.z = z !== undefined ? z : 0; | ||
} | ||
return this; | ||
this.w = w || 1; | ||
this.x = x || 0; | ||
this.y = y || 0; | ||
this.z = z || 0; | ||
} | ||
var register = new Quaternion(1, 0, 0, 0); | ||
Quaternion.prototype.add = function add(q) { | ||
return register.setWXYZ(this.w + q.w, this.x + q.x, this.y + q.y, this.z + q.z); | ||
}; | ||
Quaternion.prototype.sub = function sub(q) { | ||
return register.setWXYZ(this.w - q.w, this.x - q.x, this.y - q.y, this.z - q.z); | ||
}; | ||
Quaternion.prototype.scalarDivide = function scalarDivide(s) { | ||
return this.scalarMultiply(1 / s); | ||
}; | ||
Quaternion.prototype.scalarMultiply = function scalarMultiply(s) { | ||
return register.setWXYZ(this.w * s, this.x * s, this.y * s, this.z * s); | ||
}; | ||
/** | ||
* Multiply the current Quaternion by input Quaternion q. | ||
* Left-handed multiplication. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q The Quaternion to multiply by on the right. | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.multiply = function multiply(q) { | ||
@@ -42,62 +73,184 @@ var x1 = this.x; | ||
var w2 = q.w || 0; | ||
return register.setWXYZ(w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2, x1 * w2 + x2 * w1 + y2 * z1 - y1 * z2, y1 * w2 + y2 * w1 + x1 * z2 - x2 * z1, z1 * w2 + z2 * w1 + x2 * y1 - x1 * y2); | ||
this.w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2; | ||
this.x = x1 * w2 + x2 * w1 + y2 * z1 - y1 * z2; | ||
this.y = y1 * w2 + y2 * w1 + x1 * z2 - x2 * z1; | ||
this.z = z1 * w2 + z2 * w1 + x2 * y1 - x1 * y2; | ||
return this; | ||
}; | ||
var conj = new Quaternion(1, 0, 0, 0); | ||
Quaternion.prototype.rotateVector = function rotateVector(v) { | ||
conj.set(this.conj()); | ||
return register.set(this.multiply(v).multiply(conj)); | ||
/** | ||
* Multiply the current Quaternion by input Quaternion q on the left, i.e. q * this. | ||
* Left-handed multiplication. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q The Quaternion to multiply by on the left. | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.leftMultiply = function leftMultiply(q) { | ||
var x1 = q.x; | ||
var y1 = q.y; | ||
var z1 = q.z; | ||
var w1 = q.w || 0; | ||
var x2 = this.x; | ||
var y2 = this.y; | ||
var z2 = this.z; | ||
var w2 = this.w; | ||
this.w = w1*w2 - x1*x2 - y1*y2 - z1*z2; | ||
this.x = x1*w2 + x2*w1 + y2*z1 - y1*z2; | ||
this.y = y1*w2 + y2*w1 + x1*z2 - x2*z1; | ||
this.z = z1*w2 + z2*w1 + x2*y1 - x1*y2; | ||
return this; | ||
}; | ||
Quaternion.prototype.inverse = function inverse() { | ||
return register.set(this.conj().scalarDivide(this.normSquared())); | ||
/** | ||
* Apply the current Quaternion to input Vec3 v, according to | ||
* v' = ~q * v * q. | ||
* | ||
* @method | ||
* | ||
* @param {Vec3} v The reference Vec3. | ||
* @param {Vec3} output Vec3 in which to place the result. | ||
* | ||
* @return {Vec3} The rotated version of the Vec3. | ||
*/ | ||
Quaternion.prototype.rotateVector = function rotateVector(v, output) { | ||
var cw = this.w; | ||
var cx = -this.x; | ||
var cy = -this.y; | ||
var cz = -this.z; | ||
var vx = v.x; | ||
var vy = v.y; | ||
var vz = v.z; | ||
var tw = -cx * vx - cy * vy - cz * vz; | ||
var tx = vx * cw + vy * cz - cy * vz; | ||
var ty = vy * cw + cx * vz - vx * cz; | ||
var tz = vz * cw + vx * cy - cx * vy; | ||
var w = cw; | ||
var x = -cx; | ||
var y = -cy; | ||
var z = -cz; | ||
output.x = tx * w + x * tw + y * tz - ty * z; | ||
output.y = ty * w + y * tw + tx * z - x * tz; | ||
output.z = tz * w + z * tw + x * ty - tx * y; | ||
return output; | ||
}; | ||
Quaternion.prototype.negate = function negate() { | ||
return this.scalarMultiply(-1); | ||
/** | ||
* Invert the current Quaternion. | ||
* | ||
* @method | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.invert = function invert() { | ||
this.w = -this.w; | ||
this.x = -this.x; | ||
this.y = -this.y; | ||
this.z = -this.z; | ||
return this; | ||
}; | ||
Quaternion.prototype.conj = function conj() { | ||
return register.setWXYZ(this.w, -this.x, -this.y, -this.z); | ||
/** | ||
* Conjugate the current Quaternion. | ||
* | ||
* @method | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.conjugate = function conjugate() { | ||
this.x = -this.x; | ||
this.y = -this.y; | ||
this.z = -this.z; | ||
return this; | ||
}; | ||
Quaternion.prototype.normalize = function normalize(length) { | ||
length = length === undefined ? 1 : length; | ||
return this.scalarDivide(length * this.norm()); | ||
/** | ||
* Compute the length (norm) of the current Quaternion. | ||
* | ||
* @method | ||
* | ||
* @return {Number} length of the Quaternion | ||
*/ | ||
Quaternion.prototype.length = function length() { | ||
var w = this.w; | ||
var x = this.x; | ||
var y = this.y; | ||
var z = this.z; | ||
return sqrt(w * w + x * x + y * y + z * z); | ||
}; | ||
Quaternion.prototype.makeFromAngleAndAxis = function makeFromAngleAndAxis(angle, v) { | ||
var n = v.normalize(); | ||
var ha = angle * 0.5; | ||
var s = -Math.sin(ha); | ||
this.x = s * n.x; | ||
this.y = s * n.y; | ||
this.z = s * n.z; | ||
this.w = Math.cos(ha); | ||
/** | ||
* Alter the current Quaternion to be of unit length; | ||
* | ||
* @method | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.normalize = function normalize() { | ||
var w = this.w; | ||
var x = this.x; | ||
var y = this.y; | ||
var z = this.z; | ||
var length = sqrt(w * w + x * x + y * y + z * z); | ||
if (length === 0) return this; | ||
length = 1 / length; | ||
this.w *= length; | ||
this.x *= length; | ||
this.y *= length; | ||
this.z *= length; | ||
return this; | ||
}; | ||
Quaternion.prototype.setWXYZ = function setWXYZ(w, x, y, z) { | ||
register.clear(); | ||
this.w = w; | ||
this.x = x; | ||
this.y = y; | ||
this.z = z; | ||
/** | ||
* Set the w, x, y, z components of the current Quaternion. | ||
* | ||
* @method | ||
* | ||
* @param {Number} w The w component. | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.set = function set(w, x ,y, z) { | ||
if (w != null) this.w = w; | ||
if (x != null) this.x = x; | ||
if (y != null) this.y = y; | ||
if (z != null) this.z = z; | ||
return this; | ||
}; | ||
Quaternion.prototype.set = function set(v) { | ||
if (v instanceof Array) { | ||
this.w = 0; | ||
this.x = v[0]; | ||
this.y = v[1]; | ||
this.z = v[2]; | ||
} else { | ||
this.w = v.w; | ||
this.x = v.x; | ||
this.y = v.y; | ||
this.z = v.z; | ||
} | ||
if (this !== register) | ||
register.clear(); | ||
/** | ||
* Copy input Quaternion q onto the current Quaternion. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q The reference Quaternion. | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.copy = function copy(q) { | ||
this.w = q.w; | ||
this.x = q.x; | ||
this.y = q.y; | ||
this.z = q.z; | ||
return this; | ||
}; | ||
Quaternion.prototype.put = function put(q) { | ||
q.set(register); | ||
}; | ||
Quaternion.prototype.clone = function clone() { | ||
return new Quaternion(this); | ||
}; | ||
/** | ||
* Reset the current Quaternion. | ||
* | ||
* @method | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.clear = function clear() { | ||
@@ -110,69 +263,39 @@ this.w = 1; | ||
}; | ||
Quaternion.prototype.isEqual = function isEqual(q) { | ||
return q.w === this.w && q.x === this.x && q.y === this.y && q.z === this.z; | ||
}; | ||
/** | ||
* The dot product. Can be used to determine the cosine of the angle between | ||
* the two rotations, assuming both Quaternions are of unit length. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q The other Quaternion. | ||
* | ||
* @return {Number} the resulting dot product | ||
*/ | ||
Quaternion.prototype.dot = function dot(q) { | ||
return this.w * q.w + this.x * q.x + this.y * q.y + this.z * q.z; | ||
}; | ||
Quaternion.prototype.normSquared = function normSquared() { | ||
return this.dot(this); | ||
}; | ||
Quaternion.prototype.norm = function norm() { | ||
return Math.sqrt(this.normSquared()); | ||
}; | ||
Quaternion.prototype.isZero = function isZero() { | ||
return !(this.x || this.y || this.z); | ||
}; | ||
Quaternion.prototype.getTransform = function getTransform() { | ||
var temp = this.normalize(1); | ||
var x = temp.x; | ||
var y = temp.y; | ||
var z = temp.z; | ||
var w = temp.w; | ||
return [ | ||
1 - 2 * y * y - 2 * z * z, | ||
2 * x * y - 2 * z * w, | ||
2 * x * z + 2 * y * w, | ||
0, | ||
2 * x * y + 2 * z * w, | ||
1 - 2 * x * x - 2 * z * z, | ||
2 * y * z - 2 * x * w, | ||
0, | ||
2 * x * z - 2 * y * w, | ||
2 * y * z + 2 * x * w, | ||
1 - 2 * x * x - 2 * y * y, | ||
0, | ||
0, | ||
0, | ||
0, | ||
1 | ||
]; | ||
}; | ||
var matrixRegister = new Matrix(); | ||
Quaternion.prototype.getMatrix = function getMatrix() { | ||
var temp = this.normalize(1); | ||
var x = temp.x; | ||
var y = temp.y; | ||
var z = temp.z; | ||
var w = temp.w; | ||
return matrixRegister.set([ | ||
[ | ||
1 - 2 * y * y - 2 * z * z, | ||
2 * x * y + 2 * z * w, | ||
2 * x * z - 2 * y * w | ||
], | ||
[ | ||
2 * x * y - 2 * z * w, | ||
1 - 2 * x * x - 2 * z * z, | ||
2 * y * z + 2 * x * w | ||
], | ||
[ | ||
2 * x * z + 2 * y * w, | ||
2 * y * z - 2 * x * w, | ||
1 - 2 * x * x - 2 * y * y | ||
] | ||
]); | ||
}; | ||
var epsilon = 0.00001; | ||
Quaternion.prototype.slerp = function slerp(q, t) { | ||
/** | ||
* Spherical linear interpolation. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q The final orientation. | ||
* @param {Number} t The tween parameter. | ||
* @param {Vec3} output Vec3 in which to put the result. | ||
* | ||
* @return {Quaternion} The quaternion the slerp results were saved to | ||
*/ | ||
Quaternion.prototype.slerp = function slerp(q, t, output) { | ||
var w = this.w; | ||
var x = this.x; | ||
var y = this.y; | ||
var z = this.z; | ||
var qw = q.w; | ||
var qx = q.x; | ||
var qy = q.y; | ||
var qz = q.z; | ||
var omega; | ||
@@ -183,14 +306,246 @@ var cosomega; | ||
var scaleTo; | ||
cosomega = this.dot(q); | ||
if (1 - cosomega > epsilon) { | ||
omega = Math.acos(cosomega); | ||
sinomega = Math.sin(omega); | ||
scaleFrom = Math.sin((1 - t) * omega) / sinomega; | ||
scaleTo = Math.sin(t * omega) / sinomega; | ||
} else { | ||
scaleFrom = 1 - t; | ||
cosomega = w * qw + x * qx + y * qy + z * qz; | ||
if ((1.0 - cosomega) > 1e-5) { | ||
omega = acos(cosomega); | ||
sinomega = sin(omega); | ||
scaleFrom = sin((1.0 - t) * omega) / sinomega; | ||
scaleTo = sin(t * omega) / sinomega; | ||
} | ||
else { | ||
scaleFrom = 1.0 - t; | ||
scaleTo = t; | ||
} | ||
return register.set(this.scalarMultiply(scaleFrom / scaleTo).add(q).scalarMultiply(scaleTo)); | ||
output.w = w * scaleFrom + qw * scaleTo; | ||
output.x = x * scaleFrom + qx * scaleTo; | ||
output.y = y * scaleFrom + qy * scaleTo; | ||
output.z = z * scaleFrom + qz * scaleTo; | ||
return output; | ||
}; | ||
module.exports = Quaternion; | ||
/** | ||
* Get the Mat33 matrix corresponding to the current Quaternion. | ||
* | ||
* @method | ||
* | ||
* @param {Object} output Object to process the Transform matrix | ||
* | ||
* @return {Array} the Quaternion as a Transform matrix | ||
*/ | ||
Quaternion.prototype.toMatrix = function toMatrix(output) { | ||
var w = this.w; | ||
var x = this.x; | ||
var y = this.y; | ||
var z = this.z; | ||
var xx = x*x; | ||
var yy = y*y; | ||
var zz = z*z; | ||
var xy = x*y; | ||
var xz = x*z; | ||
var yz = y*z; | ||
return output.set([ | ||
1 - 2 * (yy + zz), 2 * (xy - w*z), 2 * (xz + w*y), | ||
2 * (xy + w*z), 1 - 2 * (xx + zz), 2 * (yz - w*x), | ||
2 * (xz - w*y), 2 * (yz + w*x), 1 - 2 * (xx + yy) | ||
]); | ||
}; | ||
/** | ||
* The rotation angles about the x, y, and z axes corresponding to the | ||
* current Quaternion, when applied in the ZYX order. | ||
* | ||
* @method | ||
* | ||
* @param {Vec3} output Vec3 in which to put the result. | ||
* | ||
* @return {Vec3} the Vec3 the result was stored in | ||
*/ | ||
Quaternion.prototype.toEuler = function toEuler(output) { | ||
var w = this.w; | ||
var x = this.x; | ||
var y = this.y; | ||
var z = this.z; | ||
var xx = x * x; | ||
var yy = y * y; | ||
var zz = z * z; | ||
var ty = 2 * (x * z + y * w); | ||
ty = ty < -1 ? -1 : ty > 1 ? 1 : ty; | ||
output.x = atan2(2 * (x * w - y * z), 1 - 2 * (xx + yy)); | ||
output.y = asin(ty); | ||
output.z = atan2(2 * (z * w - x * y), 1 - 2 * (yy + zz)); | ||
return output; | ||
}; | ||
/** | ||
* The Quaternion corresponding to the Euler angles x, y, and z, | ||
* applied in the ZYX order. | ||
* | ||
* @method | ||
* | ||
* @param {Number} x The angle of rotation about the x axis. | ||
* @param {Number} y The angle of rotation about the y axis. | ||
* @param {Number} z The angle of rotation about the z axis. | ||
* @param {Quaternion} output Quaternion in which to put the result. | ||
* | ||
* @return {Quaternion} The equivalent Quaternion. | ||
*/ | ||
Quaternion.prototype.fromEuler = function fromEuler(x, y, z) { | ||
var hx = x * 0.5; | ||
var hy = y * 0.5; | ||
var hz = z * 0.5; | ||
var sx = sin(hx); | ||
var sy = sin(hy); | ||
var sz = sin(hz); | ||
var cx = cos(hx); | ||
var cy = cos(hy); | ||
var cz = cos(hz); | ||
this.w = cx * cy * cz - sx * sy * sz; | ||
this.x = sx * cy * cz + cx * sy * sz; | ||
this.y = cx * sy * cz - sx * cy * sz; | ||
this.z = cx * cy * sz + sx * sy * cz; | ||
return this; | ||
}; | ||
/** | ||
* Alter the current Quaternion to reflect a rotation of input angle about | ||
* input axis x, y, and z. | ||
* | ||
* @method | ||
* | ||
* @param {Number} angle The angle of rotation. | ||
* @param {Vec3} x The axis of rotation. | ||
* @param {Vec3} y The axis of rotation. | ||
* @param {Vec3} z The axis of rotation. | ||
* | ||
* @return {Quaternion} this | ||
*/ | ||
Quaternion.prototype.fromAngleAxis = function fromAngleAxis(angle, x, y, z) { | ||
var len = sqrt(x * x + y * y + z * z); | ||
if (len === 0) { | ||
this.w = 1; | ||
this.x = this.y = this.z = 0; | ||
} | ||
else { | ||
len = 1 / len; | ||
var halfTheta = angle * 0.5; | ||
var s = sin(halfTheta); | ||
this.w = cos(halfTheta); | ||
this.x = s * x * len; | ||
this.y = s * y * len; | ||
this.z = s * z * len; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Multiply the input Quaternions. | ||
* Left-handed coordinate system multiplication. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q1 The left Quaternion. | ||
* @param {Quaternion} q2 The right Quaternion. | ||
* @param {Quaternion} output Quaternion in which to place the result. | ||
* | ||
* @return {Quaternion} The product of multiplication. | ||
*/ | ||
Quaternion.multiply = function multiply(q1, q2, output) { | ||
var w1 = q1.w || 0; | ||
var x1 = q1.x; | ||
var y1 = q1.y; | ||
var z1 = q1.z; | ||
var w2 = q2.w || 0; | ||
var x2 = q2.x; | ||
var y2 = q2.y; | ||
var z2 = q2.z; | ||
output.w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2; | ||
output.x = x1 * w2 + x2 * w1 + y2 * z1 - y1 * z2; | ||
output.y = y1 * w2 + y2 * w1 + x1 * z2 - x2 * z1; | ||
output.z = z1 * w2 + z2 * w1 + x2 * y1 - x1 * y2; | ||
return output; | ||
}; | ||
/** | ||
* Normalize the input quaternion. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q The reference Quaternion. | ||
* @param {Quaternion} output Quaternion in which to place the result. | ||
* | ||
* @return {Quaternion} The normalized quaternion. | ||
*/ | ||
Quaternion.normalize = function normalize(q, output) { | ||
var w = q.w; | ||
var x = q.x; | ||
var y = q.y; | ||
var z = q.z; | ||
var length = sqrt(w * w + x * x + y * y + z * z); | ||
if (length === 0) return this; | ||
length = 1 / length; | ||
output.w *= length; | ||
output.x *= length; | ||
output.y *= length; | ||
output.z *= length; | ||
return output; | ||
}; | ||
/** | ||
* The conjugate of the input Quaternion. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q The reference Quaternion. | ||
* @param {Quaternion} output Quaternion in which to place the result. | ||
* | ||
* @return {Quaternion} The conjugate Quaternion. | ||
*/ | ||
Quaternion.conjugate = function conjugate(q, output) { | ||
output.w = q.w; | ||
output.x = -q.x; | ||
output.y = -q.y; | ||
output.z = -q.z; | ||
return output; | ||
}; | ||
/** | ||
* Clone the input Quaternion. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q the reference Quaternion. | ||
* | ||
* @return {Quaternion} The cloned Quaternion. | ||
*/ | ||
Quaternion.clone = function clone(q) { | ||
return new Quaternion(q.w, q.x, q.y, q.z); | ||
}; | ||
/** | ||
* The dot product of the two input Quaternions. | ||
* | ||
* @method | ||
* | ||
* @param {Quaternion} q1 The left Quaternion. | ||
* @param {Quaternion} q2 The right Quaternion. | ||
* | ||
* @return {Number} The dot product of the two Quaternions. | ||
*/ | ||
Quaternion.dot = function dot(q1, q2) { | ||
return q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z; | ||
}; | ||
module.exports = Quaternion; |
148
package.json
{ | ||
"name": "famous", | ||
"version": "0.3.5", | ||
"description": "Famo.us is a JavaScript framework for everyone who wants to build beautiful experiences on any device.", | ||
"version": "0.5.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"test-components": "browserify components/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-core": "browserify core/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-dom-renderables": "browserify dom-renderables/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-dom-renderers": "browserify dom-renderers/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-render-loops": "browserify render-loops/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-math": "browserify math/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-physics": "browserify physics/test/*.js physics/test/*/*.js | tap-closer | smokestack -b firefox", | ||
"test-polyfills": "browserify polyfills/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-renderers": "browserify renderers/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-transitions": "browserify transitions/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-utilities": "browserify utilities/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-webgl-geometries": "browserify webgl-geometries/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-webgl-materials": "browserify webgl-materials/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-webgl-renderables": "browserify webgl-renderables/test/*.js | tap-closer | smokestack -b firefox", | ||
"test-webgl-renderers": "browserify webgl-renderers/test/*.js | tap-closer | smokestack -b firefox", | ||
"test": "EXIT_STATUS=0; npm run test-components || EXIT_STATUS=$?; npm run test-core || EXIT_STATUS=$?; npm run test-dom-renderables || EXIT_STATUS=$?; npm run test-dom-renderers || EXIT_STATUS=$?; npm run test-render-loops || EXIT_STATUS=$?; npm run test-math || EXIT_STATUS=$?; npm run test-physics || EXIT_STATUS=$?; npm run test-polyfills || EXIT_STATUS=$?; npm run test-renderers || EXIT_STATUS=$?; npm run test-transitions || EXIT_STATUS=$?; npm run test-utilities || EXIT_STATUS=$?; npm run test-webgl-geometries || EXIT_STATUS=$?; npm run test-webgl-materials || EXIT_STATUS=$?; npm run test-webgl-renderables || EXIT_STATUS=$?; npm run test-webgl-renderers; exit $EXIT_STATUS", | ||
"check-jsdoc": "eslint --reset --no-eslintrc --rule 'valid-jsdoc: 2' --ignore-path .gitignore .", | ||
"lint": "eslint --ignore-path .gitignore .", | ||
"build": "mkdir dist; browserify index.js --standalone famous | uglifyjs --screw-ie8 -m -c dead_code,sequences,conditionals,booleans,unused,if_return,join_vars,drop_debugger > dist/famous.min.js" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/famous/famous" | ||
"url": "https://github.com/Famous/engine.git" | ||
}, | ||
"keywords": [ | ||
"famous" | ||
], | ||
"scripts": { | ||
"test": "npm run lint -s", | ||
"lint-eslint": "eslint src/", | ||
"lint-jscs": "jscs src/", | ||
"lint": "npm run lint-eslint && npm run lint-jscs", | ||
"build": "npm run build-amd && npm run build-amd-min && npm run build-global && npm run build-global-min && npm run build-css", | ||
"build-amd": "famous-dist-generator src/ dist/famous.js --amd", | ||
"build-amd-min": "famous-dist-generator src/ dist/famous.min.js --amd --minify", | ||
"build-global": "famous-dist-generator src/ dist/famous-global.js", | ||
"build-global-min": "famous-dist-generator src dist/famous-global.min.js --minify", | ||
"build-common": "famous-dist-generator src/ dist/common --common", | ||
"build-css": "famous-dist-generator src/ dist/ --css ", | ||
"build-docs": "famous-doc-generator --base src/ --out docs/ --createIndex", | ||
"build-examples": "famous-examples-generator -i ./examples" | ||
}, | ||
"authors": [ | ||
"Adam Cmiel <adam@famo.us>", | ||
"Adnan Wahab <mail@adnanwahab.com>", | ||
"Adrian Rossouw <adrian@daemon.co.za>", | ||
"Alex McLeod <alxmcleod@gmail.com>", | ||
"Alexander Gugel <alexander.gugel@gmail.com>", | ||
"Andrew de Andrade <andrew@deandrade.com.br>", | ||
"Anthony Talves <tony@3alves.com>", | ||
"Antmanler <antmanler@gmail.com>", | ||
"Arkady Pevzner <arkady.pevzner@gmail.com> <arkady@famo.us>", | ||
"Austen Talbot <austentalbot@gmail.com>", | ||
"Avi Haiat <thaiat@ipelia.com>", | ||
"Brendan Graetz <brendan.graetz@gmail.com>", | ||
"Carl A. Fahlstrom <carl.fahlstrom@gmail.com>", | ||
"Cem Turan <cem2ran@gmail.com>", | ||
"Christopher Cook <christopher.cook@webprofusion.com>", | ||
"Cliff Crosland <cliffcrosland@gmail.com>", | ||
"Corey Wilson <corey@ereatah.com>", | ||
"Daniel Lewis <Thecontrarian@users.noreply.github.com>", | ||
"Dan Miller <yajnavalkya@cox.net>", | ||
"Dave Fetterman <fettermania@gmail.com>", | ||
"David Valdman <david@famo.us> <dmvaldman@gmail.com>", | ||
"Denis Gorbachev <Denis.Gorbachev@faster-than-wind.ru>", | ||
"Eric Schoffstall <contra@wearefractal.com> <contra@maricopa.edu>", | ||
"extempl <extempl@gmail.com>", | ||
"Felix Tripier <ftripier@gmail.com>", | ||
"Gregory Lull <gregorylull@gmail.com>", | ||
"Hongxu Liu <djshu.us@gmail.com>", | ||
"IjzerenHein <hrutjes@gmail.com>", | ||
"Imtiaz Majeed <imtiazmaj@gmail.com>", | ||
"J. Andrew Brassington <jabbrass@zoho.com>", | ||
"Jacques Desmarais <jdesma@gmail.com>", | ||
"James Snowden <jamesvsnowden@me.com>", | ||
"Joe Johnston <joe@simple10.com>", | ||
"John Hurliman <jhurliman@jhurliman.org>", | ||
"Jonathan Reem <jonathan.reem@gmail.com>", | ||
"Jordan Papaleo <papaleowebdev@gmail.com>", | ||
"Josef Reinhard Brandl <mail@josefbrandl.de>", | ||
"Joseph Carroll <jdsalingerjr@gmail.com>", | ||
"Joseph Orbegoso Pea <trusktr@gmail.com>", | ||
"Joseph Sample <joseph.michael.sample@gmail.com>", | ||
"Kevin McGee <kevin.mcgee@cssian.com>", | ||
"Kyle Craft <craftjk@gmail.com>", | ||
"Lauricio Su <me@lauricio.com>", | ||
"Mark Lu <mark@famo.us> <marklu@marklu.net>", | ||
"Mark Marijnissen <markmarijnissen@gmail.com>", | ||
"Matthew Trost <matthew@famo.us> <matthew@trost.co>", | ||
"Max Xu <deepthought@gmail.com>", | ||
"Michael O'Brien <michael.obrien.a@gmail.com> <michael.obrien.a@gmail.com>", | ||
"Michael Xia <xia.umd@gmail.com>", | ||
"Morten Henriksen <mh@gi2.dk>", | ||
"Murillo <mmbfreitas@hotmail.com>", | ||
"Myles Borins <myles.borins@gmail.com> <myles@famo.us>", | ||
"Nitin Saroha <nitinsaroha6@hotmail.com>", | ||
"Oleg Slobodskoi <oleg008@gmail.com>", | ||
"PatrickJS <github@gdi2290.com>", | ||
"Peter Finley <peter.finley@gmail.com>", | ||
"Peter Kiers <pkiers@MacBook-Pro-van-Peter.local>", | ||
"Pierre-Eric Marchandet <pemarchandet@gmail.com>", | ||
"Raphaël Benitte <benitteraphael@gmail.com>", | ||
"rsperberg <rsperberg@gmail.com>", | ||
"Sabith Pocker <sabithpocker@gmail.com>", | ||
"Sing Li <sli@makawave.com>", | ||
"skoczen <skoczen@gmail.com>", | ||
"Spencer Stebbins <sjfstebbins@gmail.com>", | ||
"Stephan Bijzitter <stephanbijzitter@gmail.com>", | ||
"Steve Mao <maochenyan@msn.com>", | ||
"Tal Gleichger <tal@godisco.net>", | ||
"Thomas L. <thomas.laurent.gm@gmail.com>", | ||
"Tim Chin <timjchin@gmail.com> <tim@famo.us>", | ||
"Tom Hornos <tom.hornos@gmail.com>", | ||
"Travis Cherry <travis_cherry@premierinc.com>", | ||
"Wayland Woodruff <waylandwoodruff@gmail.com>", | ||
"Will Gester <will@famo.us> <willgester@gmail.com>", | ||
"Zach Gardner <zgardner@keyholesoftware.com>", | ||
"Zack Brown <z@charybrown.com>", | ||
"Zeqiu Wu <allenwu1018@gmail.com>" | ||
], | ||
"license": "MPL v2.0", | ||
"bugs": { | ||
"url": "https://github.com/famous/famous/issues" | ||
}, | ||
"homepage": "https://famo.us", | ||
"author": "Famous", | ||
"license": "All rights reserved", | ||
"devDependencies": { | ||
"eslint": "^0.13.0", | ||
"famous-dist-generator": "^1.0.2", | ||
"famous-doc-generator": "^0.7.0", | ||
"famous-examples-generator": "^0.2.1", | ||
"jscs": "^1.5.9" | ||
"browserify": "^9.0.3", | ||
"eslint": "^0.21.2", | ||
"smokestack": "^3.2.2", | ||
"tap-closer": "^1.0.0", | ||
"tape": "^4.0.0", | ||
"uglify-js": "^2.4.17" | ||
}, | ||
"dependencies": { | ||
"cssify": "^0.6.0", | ||
"deamdify": "^0.1.1" | ||
"glslify": "^1.6.0" | ||
}, | ||
"browserify": { | ||
"transform": [ | ||
"cssify", | ||
"deamdify" | ||
"glslify" | ||
] | ||
} | ||
} |
@@ -1,203 +0,502 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
var Vector = require('../../math/Vector'); | ||
var Transform = require('../../core/Transform'); | ||
var EventHandler = require('../../core/EventHandler'); | ||
var Integrator = require('../integrators/SymplecticEuler'); | ||
'use strict'; | ||
var Vec3 = require('../../math/Vec3'); | ||
var Quaternion = require('../../math/Quaternion'); | ||
var Mat33 = require('../../math/Mat33'); | ||
var CallbackStore = require('../../utilities/CallbackStore'); | ||
var ZERO_VECTOR = new Vec3(); | ||
var MAT1_REGISTER = new Mat33(); | ||
var _ID = 0; | ||
/** | ||
* Fundamental physical body. Maintains translational and angular momentum, position and orientation, and other properties | ||
* such as size and coefficients of restitution and friction used in collision response. | ||
* | ||
* @class Particle | ||
* @param {Object} options Initial state of the body. | ||
*/ | ||
function Particle(options) { | ||
this.events = new CallbackStore(); | ||
options = options || {}; | ||
var defaults = Particle.DEFAULT_OPTIONS; | ||
this.position = new Vector(); | ||
this.velocity = new Vector(); | ||
this.force = new Vector(); | ||
this._engine = null; | ||
this._isSleeping = true; | ||
this._eventOutput = null; | ||
this.mass = options.mass !== undefined ? options.mass : defaults.mass; | ||
this.position = options.position || new Vec3(); | ||
this.orientation = options.orientation || new Quaternion(); | ||
this.velocity = new Vec3(); | ||
this.momentum = new Vec3(); | ||
this.angularVelocity = new Vec3(); | ||
this.angularMomentum = new Vec3(); | ||
this.mass = options.mass || 1; | ||
this.inverseMass = 1 / this.mass; | ||
this.setPosition(options.position || defaults.position); | ||
this.setVelocity(options.velocity || defaults.velocity); | ||
this.force.set(options.force || [ | ||
0, | ||
0, | ||
0 | ||
]); | ||
this.transform = Transform.identity.slice(); | ||
this._spec = { | ||
size: [ | ||
true, | ||
true | ||
], | ||
target: { | ||
transform: this.transform, | ||
origin: [ | ||
0.5, | ||
0.5 | ||
], | ||
target: null | ||
} | ||
}; | ||
this.force = new Vec3(); | ||
this.torque = new Vec3(); | ||
this.restitution = options.restitution != null ? options.restitution : 0.4; | ||
this.friction = options.friction != null ? options.friction : 0.2; | ||
this.inverseInertia = new Mat33([0,0,0,0,0,0,0,0,0]); | ||
this.localInertia = new Mat33([0,0,0,0,0,0,0,0,0]); | ||
this.localInverseInertia = new Mat33([0,0,0,0,0,0,0,0,0]); | ||
this.size = options.size || [0, 0, 0]; | ||
var v = options.velocity; | ||
if (v) this.setVelocity(v.x, v.y, v.z); | ||
this.restrictions = 0; | ||
this.setRestrictions.apply(this, options.restrictions || []); | ||
this.collisionMask = options.collisionMask || 1; | ||
this.collisionGroup = options.collisionGroup || 1; | ||
this.type = 1 << 0; | ||
this._ID = _ID++; | ||
} | ||
Particle.DEFAULT_OPTIONS = { | ||
position: [ | ||
0, | ||
0, | ||
0 | ||
], | ||
velocity: [ | ||
0, | ||
0, | ||
0 | ||
], | ||
mass: 1 | ||
/** | ||
* Listen for a specific event. | ||
* | ||
* @method | ||
* @param {String} key Name of the event. | ||
* @param {Function} callback Callback to register for the event. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.on = function on(key, callback) { | ||
this.events.on(key, callback); | ||
return this; | ||
}; | ||
var _events = { | ||
start: 'start', | ||
update: 'update', | ||
end: 'end' | ||
/** | ||
* Stop listening for a specific event. | ||
* | ||
* @method | ||
* @param {String} key Name of the event. | ||
* @param {Function} callback Callback to deregister for the event. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.off = function off(key, callback) { | ||
this.events.off(key, callback); | ||
return this; | ||
}; | ||
var now = Date.now; | ||
Particle.prototype.isBody = false; | ||
Particle.prototype.isActive = function isActive() { | ||
return !this._isSleeping; | ||
/** | ||
* Trigger an event. | ||
* | ||
* @method | ||
* @param {String} key Name of the event. | ||
* @param {Object} payload Payload to pass to the event listeners. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.trigger = function trigger(key, payload) { | ||
this.events.trigger(key, payload); | ||
return this; | ||
}; | ||
Particle.prototype.sleep = function sleep() { | ||
if (this._isSleeping) | ||
return; | ||
this.emit(_events.end, this); | ||
this._isSleeping = true; | ||
/** | ||
* Getter for the restriction bitmask. Converts the restrictions to their string representation. | ||
* | ||
* @method | ||
* @return {String[]} restrictions | ||
*/ | ||
Particle.prototype.getRestrictions = function getRestrictions() { | ||
var linear = ''; | ||
var angular = ''; | ||
var restrictions = this.restrictions; | ||
if (restrictions & 32) linear += 'x'; | ||
if (restrictions & 16) linear += 'y'; | ||
if (restrictions & 8) linear += 'z'; | ||
if (restrictions & 4) angular += 'x'; | ||
if (restrictions & 2) angular += 'y'; | ||
if (restrictions & 1) angular += 'z'; | ||
return [linear, angular]; | ||
}; | ||
Particle.prototype.wake = function wake() { | ||
if (!this._isSleeping) | ||
return; | ||
this.emit(_events.start, this); | ||
this._isSleeping = false; | ||
this._prevTime = now(); | ||
if (this._engine) | ||
this._engine.wake(); | ||
/** | ||
* Setter for the particle restriction bitmask. | ||
* | ||
* @method | ||
* @param {String} transRestrictions The restrictions to linear motion. | ||
* @param {String} rotRestrictions The restrictions to rotational motion. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setRestrictions = function setRestrictions(transRestrictions, rotRestrictions) { | ||
transRestrictions = transRestrictions || ''; | ||
rotRestrictions = rotRestrictions || ''; | ||
this.restrictions = 0; | ||
if (transRestrictions.indexOf('x') > -1) this.restrictions |= 32; | ||
if (transRestrictions.indexOf('y') > -1) this.restrictions |= 16; | ||
if (transRestrictions.indexOf('z') > -1) this.restrictions |= 8; | ||
if (rotRestrictions.indexOf('x') > -1) this.restrictions |= 4; | ||
if (rotRestrictions.indexOf('y') > -1) this.restrictions |= 2; | ||
if (rotRestrictions.indexOf('z') > -1) this.restrictions |= 1; | ||
return this; | ||
}; | ||
Particle.prototype.setPosition = function setPosition(position) { | ||
this.position.set(position); | ||
/** | ||
* Getter for mass | ||
* | ||
* @method | ||
* @return {Number} mass | ||
*/ | ||
Particle.prototype.getMass = function getMass() { | ||
return this.mass; | ||
}; | ||
Particle.prototype.setPosition1D = function setPosition1D(x) { | ||
this.position.x = x; | ||
/** | ||
* Set the mass of the Particle. | ||
* | ||
* @method | ||
* @param {Number} mass The mass. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setMass = function setMass(mass) { | ||
this.mass = mass; | ||
this.inverseMass = 1 / mass; | ||
return this; | ||
}; | ||
Particle.prototype.getPosition = function getPosition() { | ||
this._engine.step(); | ||
return this.position.get(); | ||
/** | ||
* Getter for inverse mass | ||
* | ||
* @method | ||
* @return {Number} inverse mass | ||
*/ | ||
Particle.prototype.getInverseMass = function() { | ||
return this.inverseMass; | ||
}; | ||
Particle.prototype.getPosition1D = function getPosition1D() { | ||
this._engine.step(); | ||
return this.position.x; | ||
/** | ||
* Resets the inertia tensor and its inverse to reflect the current shape. | ||
* | ||
* @method | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.updateLocalInertia = function updateLocalInertia() { | ||
this.localInertia.set([0,0,0,0,0,0,0,0,0]); | ||
this.localInverseInertia.set([0,0,0,0,0,0,0,0,0]); | ||
return this; | ||
}; | ||
Particle.prototype.setVelocity = function setVelocity(velocity) { | ||
this.velocity.set(velocity); | ||
if (!(velocity[0] === 0 && velocity[1] === 0 && velocity[2] === 0)) | ||
this.wake(); | ||
/** | ||
* Updates the world inverse inertia tensor. | ||
* | ||
* @method | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.updateInertia = function updateInertia() { | ||
var localInvI = this.localInverseInertia; | ||
var q = this.orientation; | ||
if ((localInvI[0] === localInvI[4] && localInvI[4] === localInvI[8]) || q.w === 1) return this; | ||
var R = q.toMatrix(MAT1_REGISTER); | ||
Mat33.multiply(R, this.inverseInertia, this.inverseInertia); | ||
Mat33.multiply(this.localInverseInertia, R.transpose(), this.inverseInertia); | ||
return this; | ||
}; | ||
Particle.prototype.setVelocity1D = function setVelocity1D(x) { | ||
this.velocity.x = x; | ||
if (x !== 0) | ||
this.wake(); | ||
/** | ||
* Getter for position | ||
* | ||
* @method | ||
* @return {Vec3} position | ||
*/ | ||
Particle.prototype.getPosition = function getPosition() { | ||
return this.position; | ||
}; | ||
/** | ||
* Setter for position | ||
* | ||
* @method | ||
* @param {Number} x the x coordinate for position | ||
* @param {Number} y the y coordinate for position | ||
* @param {Number} z the z coordinate for position | ||
* @return {Particle} this | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setPosition = function setPosition(x, y, z) { | ||
this.position.set(x, y, z); | ||
return this; | ||
}; | ||
/** | ||
* Getter for velocity | ||
* | ||
* @method | ||
* @return {Vec3} velocity | ||
*/ | ||
Particle.prototype.getVelocity = function getVelocity() { | ||
return this.velocity.get(); | ||
return this.velocity; | ||
}; | ||
Particle.prototype.setForce = function setForce(force) { | ||
this.force.set(force); | ||
this.wake(); | ||
/** | ||
* Setter for velocity | ||
* | ||
* @method | ||
* @param {Number} x the x coordinate for velocity | ||
* @param {Number} y the y coordinate for velocity | ||
* @param {Number} z the z coordinate for velocity | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setVelocity = function setVelocity(x, y, z) { | ||
this.velocity.set(x, y, z); | ||
Vec3.scale(this.velocity, this.mass, this.momentum); | ||
return this; | ||
}; | ||
Particle.prototype.getVelocity1D = function getVelocity1D() { | ||
return this.velocity.x; | ||
/** | ||
* Getter for momenutm | ||
* | ||
* @method | ||
* @return {Vec3} momentum | ||
*/ | ||
Particle.prototype.getMomentum = function getMomentum() { | ||
return this.momentum; | ||
}; | ||
Particle.prototype.setMass = function setMass(mass) { | ||
this.mass = mass; | ||
this.inverseMass = 1 / mass; | ||
/** | ||
* Setter for momentum | ||
* | ||
* @method | ||
* @param {Number} x the x coordinate for momentum | ||
* @param {Number} y the y coordinate for momentum | ||
* @param {Number} z the z coordinate for momentum | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setMomentum = function setMomentum(x, y, z) { | ||
this.momentum.set(x, y, z); | ||
Vec3.scale(this.momentum, this.inverseMass, this.velocity); | ||
return this; | ||
}; | ||
Particle.prototype.getMass = function getMass() { | ||
return this.mass; | ||
/** | ||
* Getter for orientation | ||
* | ||
* @method | ||
* @return {Quaternion} orientation | ||
*/ | ||
Particle.prototype.getOrientation = function getOrientation() { | ||
return this.orientation; | ||
}; | ||
Particle.prototype.reset = function reset(position, velocity) { | ||
this.setPosition(position || [ | ||
0, | ||
0, | ||
0 | ||
]); | ||
this.setVelocity(velocity || [ | ||
0, | ||
0, | ||
0 | ||
]); | ||
/** | ||
* Setter for orientation | ||
* | ||
* @method | ||
* @param {Number} w The w component. | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setOrientation = function setOrientation(w,x,y,z) { | ||
this.orientation.set(w,x,y,z).normalize(); | ||
this.updateInertia(); | ||
return this; | ||
}; | ||
Particle.prototype.applyForce = function applyForce(force) { | ||
if (force.isZero()) | ||
return; | ||
this.force.add(force).put(this.force); | ||
this.wake(); | ||
/** | ||
* Getter for angular velocity | ||
* | ||
* @method | ||
* @return {Vec3} angularVelocity | ||
*/ | ||
Particle.prototype.getAngularVelocity = function getAngularVelocity() { | ||
return this.angularVelocity; | ||
}; | ||
Particle.prototype.applyImpulse = function applyImpulse(impulse) { | ||
if (impulse.isZero()) | ||
return; | ||
var velocity = this.velocity; | ||
velocity.add(impulse.mult(this.inverseMass)).put(velocity); | ||
/** | ||
* Setter for angular velocity | ||
* | ||
* @method | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setAngularVelocity = function setAngularVelocity(x,y,z) { | ||
this.angularVelocity.set(x,y,z); | ||
var I = Mat33.inverse(this.inverseInertia, MAT1_REGISTER); | ||
if (I) I.vectorMultiply(this.angularVelocity, this.angularMomentum); | ||
else this.angularMomentum.clear(); | ||
return this; | ||
}; | ||
Particle.prototype.integrateVelocity = function integrateVelocity(dt) { | ||
Integrator.integrateVelocity(this, dt); | ||
/** | ||
* Getter for angular momentum | ||
* | ||
* @method | ||
* @return {Vec3} angular momentum | ||
*/ | ||
Particle.prototype.getAngularMomentum = function getAngularMomentum() { | ||
return this.angularMomentum; | ||
}; | ||
Particle.prototype.integratePosition = function integratePosition(dt) { | ||
Integrator.integratePosition(this, dt); | ||
/** | ||
* Setter for angular momentum | ||
* | ||
* @method | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setAngularMomentum = function setAngularMomentum(x,y,z) { | ||
this.angularMomentum.set(x,y,z); | ||
this.inverseInertia.vectorMultiply(this.angularMomentum, this.angularVelocity); | ||
return this; | ||
}; | ||
Particle.prototype._integrate = function _integrate(dt) { | ||
this.integrateVelocity(dt); | ||
this.integratePosition(dt); | ||
/** | ||
* Getter for the force on the Particle | ||
* | ||
* @method | ||
* @return {Vec3} force | ||
*/ | ||
Particle.prototype.getForce = function getForce() { | ||
return this.force; | ||
}; | ||
Particle.prototype.getEnergy = function getEnergy() { | ||
return 0.5 * this.mass * this.velocity.normSquared(); | ||
/** | ||
* Setter for the force on the Particle | ||
* | ||
* @method | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setForce = function setForce(x, y, z) { | ||
this.force.set(x, y, z); | ||
return this; | ||
}; | ||
Particle.prototype.getTransform = function getTransform() { | ||
this._engine.step(); | ||
var position = this.position; | ||
var transform = this.transform; | ||
transform[12] = position.x; | ||
transform[13] = position.y; | ||
transform[14] = position.z; | ||
return transform; | ||
/** | ||
* Getter for torque. | ||
* | ||
* @method | ||
* @return {Vec3} torque | ||
*/ | ||
Particle.prototype.getTorque = function getTorque() { | ||
return this.torque; | ||
}; | ||
Particle.prototype.modify = function modify(target) { | ||
var _spec = this._spec.target; | ||
_spec.transform = this.getTransform(); | ||
_spec.target = target; | ||
return this._spec; | ||
/** | ||
* Setter for torque. | ||
* | ||
* @method | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.setTorque = function setTorque(x, y, z) { | ||
this.torque.set(x, y, z); | ||
return this; | ||
}; | ||
function _createEventOutput() { | ||
this._eventOutput = new EventHandler(); | ||
this._eventOutput.bindThis(this); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
} | ||
Particle.prototype.emit = function emit(type, data) { | ||
if (!this._eventOutput) | ||
return; | ||
this._eventOutput.emit(type, data); | ||
/** | ||
* Extends Particle.applyForce with an optional argument | ||
* to apply the force at an off-centered location, resulting in a torque. | ||
* | ||
* @method | ||
* @param {Vec3} force Force to apply. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.applyForce = function applyForce(force) { | ||
this.force.add(force); | ||
return this; | ||
}; | ||
Particle.prototype.on = function on() { | ||
_createEventOutput.call(this); | ||
return this.on.apply(this, arguments); | ||
/** | ||
* Applied a torque force to a Particle, inducing a rotation. | ||
* | ||
* @method | ||
* @param {Vec3} torque Torque to apply. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.applyTorque = function applyTorque(torque) { | ||
this.torque.add(torque); | ||
return this; | ||
}; | ||
Particle.prototype.removeListener = function removeListener() { | ||
_createEventOutput.call(this); | ||
return this.removeListener.apply(this, arguments); | ||
/** | ||
* Applies an impulse to momentum and updates velocity. | ||
* | ||
* @method | ||
* @param {Vec3} impulse Impulse to apply. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.applyImpulse = function applyImpulse(impulse) { | ||
this.momentum.add(impulse); | ||
Vec3.scale(this.momentum, this.inverseMass, this.velocity); | ||
return this; | ||
}; | ||
Particle.prototype.pipe = function pipe() { | ||
_createEventOutput.call(this); | ||
return this.pipe.apply(this, arguments); | ||
/** | ||
* Applies an angular impulse to angular momentum and updates angular velocity. | ||
* | ||
* @method | ||
* @param {Vec3} angularImpulse Angular impulse to apply. | ||
* @return {Particle} this | ||
*/ | ||
Particle.prototype.applyAngularImpulse = function applyAngularImpulse(angularImpulse) { | ||
this.angularMomentum.add(angularImpulse); | ||
this.inverseInertia.vectorMultiply(this.angularMomentum, this.angularVelocity); | ||
return this; | ||
}; | ||
Particle.prototype.unpipe = function unpipe() { | ||
_createEventOutput.call(this); | ||
return this.unpipe.apply(this, arguments); | ||
/** | ||
* Used in collision detection. The support function should accept a Vec3 direction | ||
* and return the point on the body's shape furthest in that direction. For point particles, | ||
* this returns the zero vector. | ||
* | ||
* @method | ||
* @return {Vec3} The zero vector. | ||
*/ | ||
Particle.prototype.support = function support() { | ||
return ZERO_VECTOR; | ||
}; | ||
module.exports = Particle; | ||
/** | ||
* Update the body's shape to reflect current orientation. Called in Collision. | ||
* Noop for point particles. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Particle.prototype.updateShape = function updateShape() {}; | ||
module.exports = Particle; |
@@ -1,87 +0,408 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
var Vec3 = require('../../math/Vec3'); | ||
var Constraint = require('./Constraint'); | ||
var Vector = require('../../math/Vector'); | ||
function Collision(options) { | ||
this.options = Object.create(Collision.DEFAULT_OPTIONS); | ||
if (options) | ||
this.setOptions(options); | ||
this.normal = new Vector(); | ||
this.pDiff = new Vector(); | ||
this.vDiff = new Vector(); | ||
this.impulse1 = new Vector(); | ||
this.impulse2 = new Vector(); | ||
Constraint.call(this); | ||
var SweepAndPrune = require('./collision/SweepAndPrune'); | ||
var BruteForce = require('./collision/BruteForce'); | ||
var ConvexCollision = require('./collision/ConvexCollisionDetection'); | ||
var gjk = ConvexCollision.gjk; | ||
var epa = ConvexCollision.epa; | ||
var ContactManifoldTable = require('./collision/ContactManifold'); | ||
var ObjectManager = require('../../utilities/ObjectManager'); | ||
ObjectManager.register('CollisionData', CollisionData); | ||
var oMRequestCollisionData = ObjectManager.requestCollisionData; | ||
var VEC_REGISTER = new Vec3(); | ||
/** | ||
* Helper function to clamp a value to a given range. | ||
* | ||
* @method | ||
* @private | ||
* @param {Number} value The value to clamp. | ||
* @param {Number} lower The lower end. | ||
* @param {Number} upper The upper end. | ||
* @return {Number} The clamped value. | ||
*/ | ||
function clamp(value, lower, upper) { | ||
return value < lower ? lower : value > upper ? upper : value; | ||
} | ||
/** | ||
* Object maintaining various figures of a collision. Registered in ObjectManager. | ||
* | ||
* @class CollisionData | ||
* @param {Number} penetration The degree of penetration. | ||
* @param {Vec3} normal The normal for the collision. | ||
* @param {Vec3} worldContactA The contact for A in world coordinates. | ||
* @param {Vec3} worldContactB The contact for B in world coordinates. | ||
* @param {Vec3} localContactA The contact for A in local coordinates. | ||
* @param {Vec3} localContactB The contact for B in local coordinates. | ||
*/ | ||
function CollisionData(penetration, normal, worldContactA, worldContactB, localContactA, localContactB) { | ||
this.penetration = penetration; | ||
this.normal = normal; | ||
this.worldContactA = worldContactA; | ||
this.worldContactB = worldContactB; | ||
this.localContactA = localContactA; | ||
this.localContactB = localContactB; | ||
} | ||
/** | ||
* Used by ObjectManager to reset the object with different data. | ||
* | ||
* @method | ||
* @param {Number} penetration The degree of penetration. | ||
* @param {Vec3} normal The normal for the collision. | ||
* @param {Vec3} worldContactA The contact for A in world coordinates. | ||
* @param {Vec3} worldContactB The contact for B in world coordinates. | ||
* @param {Vec3} localContactA The contact for A in local coordinates. | ||
* @param {Vec3} localContactB The contact for B in local coordinates. | ||
* @return {CollisionData} this | ||
*/ | ||
CollisionData.prototype.reset = function reset(penetration, normal, worldContactA, worldContactB, localContactA, localContactB) { | ||
this.penetration = penetration; | ||
this.normal = normal; | ||
this.worldContactA = worldContactA; | ||
this.worldContactB = worldContactB; | ||
this.localContactA = localContactA; | ||
this.localContactB = localContactB; | ||
return this; | ||
}; | ||
/** | ||
* Ridid body Elastic Collision | ||
* | ||
* @class Collision | ||
* @extends Constraint | ||
* @param {Particle[]} targets The bodies to track. | ||
* @param {Object} options The options hash. | ||
*/ | ||
function Collision(targets, options) { | ||
this.targets = []; | ||
if (targets) this.targets = this.targets.concat(targets); | ||
Constraint.call(this, options); | ||
} | ||
Collision.prototype = Object.create(Constraint.prototype); | ||
Collision.prototype.constructor = Collision; | ||
Collision.DEFAULT_OPTIONS = { | ||
restitution: 0.5, | ||
drift: 0.5, | ||
slop: 0 | ||
/** | ||
* Initialize the Collision tracker. Sets defaults if a property was not already set. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Collision.prototype.init = function() { | ||
if (this.broadPhase) { | ||
var BroadPhase = this.broadphase; | ||
if (BroadPhase instanceof Function) this.broadPhase = new BroadPhase(this.targets); | ||
} | ||
else this.broadPhase = new SweepAndPrune(this.targets); | ||
this.contactManifoldTable = this.contactManifoldTable || new ContactManifoldTable(); | ||
}; | ||
function _normalVelocity(particle1, particle2) { | ||
return particle1.velocity.dot(particle2.velocity); | ||
} | ||
Collision.prototype.setOptions = function setOptions(options) { | ||
for (var key in options) | ||
this.options[key] = options[key]; | ||
/** | ||
* Collison detection. Updates the existing contact manifolds, runs the broadphase, and performs narrowphase | ||
* collision detection. Warm starts the contacts based on the results of the previous physics frame | ||
* and prepares necesssary calculations for the resolution. | ||
* | ||
* @method | ||
* @param {Number} time The current time in the physics engine. | ||
* @param {Number} dt The physics engine frame delta. | ||
* @return {undefined} undefined | ||
*/ | ||
Collision.prototype.update = function update(time, dt) { | ||
this.contactManifoldTable.update(dt); | ||
if (this.targets.length === 0) return; | ||
var i, len; | ||
for (i = 0, len = this.targets.length; i < len; i++) { | ||
this.targets[i].updateShape(); | ||
} | ||
var potentialCollisions = this.broadPhase.update(); | ||
var pair; | ||
for (i = 0, len = potentialCollisions.length; i < len; i++) { | ||
pair = potentialCollisions[i]; | ||
if (pair) this.applyNarrowPhase(pair); | ||
} | ||
this.contactManifoldTable.prepContacts(dt); | ||
}; | ||
Collision.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | ||
if (source === undefined) | ||
return; | ||
var v1 = source.velocity; | ||
var p1 = source.position; | ||
var w1 = source.inverseMass; | ||
var r1 = source.radius; | ||
var options = this.options; | ||
var drift = options.drift; | ||
var slop = -options.slop; | ||
var restitution = options.restitution; | ||
var n = this.normal; | ||
var pDiff = this.pDiff; | ||
var vDiff = this.vDiff; | ||
var impulse1 = this.impulse1; | ||
var impulse2 = this.impulse2; | ||
for (var i = 0; i < targets.length; i++) { | ||
var target = targets[i]; | ||
if (target === source) | ||
continue; | ||
var v2 = target.velocity; | ||
var p2 = target.position; | ||
var w2 = target.inverseMass; | ||
var r2 = target.radius; | ||
pDiff.set(p2.sub(p1)); | ||
vDiff.set(v2.sub(v1)); | ||
var dist = pDiff.norm(); | ||
var overlap = dist - (r1 + r2); | ||
var effMass = 1 / (w1 + w2); | ||
var gamma = 0; | ||
if (overlap < 0) { | ||
n.set(pDiff.normalize()); | ||
if (this._eventOutput) { | ||
var collisionData = { | ||
target: target, | ||
source: source, | ||
overlap: overlap, | ||
normal: n | ||
}; | ||
this._eventOutput.emit('preCollision', collisionData); | ||
this._eventOutput.emit('collision', collisionData); | ||
} | ||
var lambda = overlap <= slop ? ((1 + restitution) * n.dot(vDiff) + drift / dt * (overlap - slop)) / (gamma + dt / effMass) : (1 + restitution) * n.dot(vDiff) / (gamma + dt / effMass); | ||
n.mult(dt * lambda).put(impulse1); | ||
impulse1.mult(-1).put(impulse2); | ||
source.applyImpulse(impulse1); | ||
target.applyImpulse(impulse2); | ||
if (this._eventOutput) | ||
this._eventOutput.emit('postCollision', collisionData); | ||
/** | ||
* Apply impulses to resolve all Contact constraints. | ||
* | ||
* @method | ||
* @param {Number} time The current time in the physics engine. | ||
* @param {Number} dt The physics engine frame delta. | ||
* @return {undefined} undefined | ||
*/ | ||
Collision.prototype.resolve = function resolve(time, dt) { | ||
this.contactManifoldTable.resolveManifolds(dt); | ||
}; | ||
/** | ||
* Add a target or targets to the collision system. | ||
* | ||
* @method | ||
* @param {Particle} target The body to begin tracking. | ||
* @return {undefined} undefined | ||
*/ | ||
Collision.prototype.addTarget = function addTarget(target) { | ||
this.targets.push(target); | ||
this.broadPhase.add(target); | ||
}; | ||
/** | ||
* Remove a target or targets from the collision system. | ||
* | ||
* @method | ||
* @param {Particle} target The body to remove. | ||
* @return {undefined} undefined | ||
*/ | ||
Collision.prototype.removeTarget = function removeTarget(target) { | ||
var index = this.targets.indexOf(target); | ||
if (index < 0) return; | ||
this.targets.splice(index, 1); | ||
this.broadPhase.remove(target); | ||
}; | ||
var CONVEX = 1 << 0; | ||
var BOX = 1 << 1; | ||
var SPHERE = 1 << 2; | ||
var WALL = 1 << 3; | ||
var CONVEX_CONVEX = CONVEX | CONVEX; | ||
var BOX_BOX = BOX | BOX; | ||
var BOX_CONVEX = BOX | CONVEX; | ||
var SPHERE_SPHERE = SPHERE | SPHERE; | ||
var BOX_SPHERE = BOX | SPHERE; | ||
var CONVEX_SPHERE = CONVEX | SPHERE; | ||
var CONVEX_WALL = CONVEX | WALL; | ||
var BOX_WALL = BOX | WALL; | ||
var SPHERE_WALL = SPHERE | WALL; | ||
var dispatch = {}; | ||
dispatch[CONVEX_CONVEX] = convexIntersectConvex; | ||
dispatch[BOX_BOX] = convexIntersectConvex; | ||
dispatch[BOX_CONVEX] = convexIntersectConvex; | ||
dispatch[CONVEX_SPHERE] = convexIntersectConvex; | ||
dispatch[SPHERE_SPHERE] = sphereIntersectSphere; | ||
dispatch[BOX_SPHERE] = boxIntersectSphere; | ||
dispatch[CONVEX_WALL] = convexIntersectWall; | ||
dispatch[BOX_WALL] = convexIntersectWall; | ||
dispatch[SPHERE_WALL] = convexIntersectWall; | ||
/** | ||
* Narrowphase collision detection, | ||
* registers the Contact constraints for colliding bodies. | ||
* | ||
* Will detect the type of bodies in the collision. | ||
* | ||
* @method | ||
* @param {Particle[]} targets The targets. | ||
* @return {undefined} undefined | ||
*/ | ||
Collision.prototype.applyNarrowPhase = function applyNarrowPhase(targets) { | ||
for (var i = 0, len = targets.length; i < len; i++) { | ||
for (var j = i + 1; j < len; j++) { | ||
var a = targets[i]; | ||
var b = targets[j]; | ||
if ((a.collisionMask & b.collisionGroup && a.collisionGroup & b.collisionMask) === 0) continue; | ||
var collisionType = a.type | b.type; | ||
if (dispatch[collisionType]) dispatch[collisionType](this, a, b); | ||
} | ||
} | ||
}; | ||
module.exports = Collision; | ||
/** | ||
* Detects sphere-sphere collisions and registers the Contact. | ||
* | ||
* @private | ||
* @method | ||
* @param {Object} context The Collision instance. | ||
* @param {Sphere} sphere1 One sphere collider. | ||
* @param {Sphere} sphere2 The other sphere collider. | ||
* @return {undefined} undefined | ||
*/ | ||
function sphereIntersectSphere(context, sphere1, sphere2) { | ||
var p1 = sphere1.position; | ||
var p2 = sphere2.position; | ||
var relativePosition = Vec3.subtract(p2, p1, new Vec3()); | ||
var distance = relativePosition.length(); | ||
var sumRadii = sphere1.radius + sphere2.radius; | ||
var n = relativePosition.scale(1/distance); | ||
var overlap = sumRadii - distance; | ||
// Distance check | ||
if (overlap < 0) return; | ||
var rSphere1 = Vec3.scale(n, sphere1.radius, new Vec3()); | ||
var rSphere2 = Vec3.scale(n, -sphere2.radius, new Vec3()); | ||
var wSphere1 = Vec3.add(p1, rSphere1, new Vec3()); | ||
var wSphere2 = Vec3.add(p2, rSphere2, new Vec3()); | ||
var collisionData = oMRequestCollisionData().reset(overlap, n, wSphere1, wSphere2, rSphere1, rSphere2); | ||
context.contactManifoldTable.registerContact(sphere1, sphere2, collisionData); | ||
} | ||
/** | ||
* Detects box-sphere collisions and registers the Contact. | ||
* | ||
* @param {Object} context The Collision instance. | ||
* @param {Box} box The box collider. | ||
* @param {Sphere} sphere The sphere collider. | ||
* @return {undefined} undefined | ||
*/ | ||
function boxIntersectSphere(context, box, sphere) { | ||
if (box.type === SPHERE) { | ||
var temp = sphere; | ||
sphere = box; | ||
box = temp; | ||
} | ||
var pb = box.position; | ||
var ps = sphere.position; | ||
var relativePosition = Vec3.subtract(ps, pb, VEC_REGISTER); | ||
var q = box.orientation; | ||
var r = sphere.radius; | ||
var bsize = box.size; | ||
var halfWidth = bsize[0]*0.5; | ||
var halfHeight = bsize[1]*0.5; | ||
var halfDepth = bsize[2]*0.5; | ||
// x, y, z | ||
var bnormals = box.normals; | ||
var n1 = q.rotateVector(bnormals[1], new Vec3()); | ||
var n2 = q.rotateVector(bnormals[0], new Vec3()); | ||
var n3 = q.rotateVector(bnormals[2], new Vec3()); | ||
// Find the point on the cube closest to the center of the sphere | ||
var closestPoint = new Vec3(); | ||
closestPoint.x = clamp(Vec3.dot(relativePosition,n1), -halfWidth, halfWidth); | ||
closestPoint.y = clamp(Vec3.dot(relativePosition,n2), -halfHeight, halfHeight); | ||
closestPoint.z = clamp(Vec3.dot(relativePosition,n3), -halfDepth, halfDepth); | ||
// The vector found is relative to the center of the unrotated box -- rotate it | ||
// to find the point w.r.t. to current orientation | ||
closestPoint.applyRotation(q); | ||
// The impact point in world space | ||
var impactPoint = Vec3.add(pb, closestPoint, new Vec3()); | ||
var sphereToImpact = Vec3.subtract(impactPoint, ps, impactPoint); | ||
var distanceToSphere = sphereToImpact.length(); | ||
// If impact point is not closer to the sphere's center than its radius -> no collision | ||
var overlap = r - distanceToSphere; | ||
if (overlap < 0) return; | ||
var n = Vec3.scale(sphereToImpact, -1 / distanceToSphere, new Vec3()); | ||
var rBox = closestPoint; | ||
var rSphere = sphereToImpact; | ||
var wBox = Vec3.add(pb, rBox, new Vec3()); | ||
var wSphere = Vec3.add(ps, rSphere, new Vec3()); | ||
var collisionData = oMRequestCollisionData().reset(overlap, n, wBox, wSphere, rBox, rSphere); | ||
context.contactManifoldTable.registerContact(box, sphere, collisionData); | ||
} | ||
/** | ||
* Detects convex-convex collisions and registers the Contact. Uses GJK to determine overlap and then | ||
* EPA to determine the actual collision data. | ||
* | ||
* @param {Object} context The Collision instance. | ||
* @param {Particle} convex1 One convex body collider. | ||
* @param {Particle} convex2 The other convex body collider. | ||
* @return {undefined} undefined | ||
*/ | ||
function convexIntersectConvex(context, convex1, convex2) { | ||
var glkSimplex = gjk(convex1, convex2); | ||
// No simplex -> no collision | ||
if (!glkSimplex) return; | ||
var collisionData = epa(convex1, convex2, glkSimplex); | ||
if (collisionData !== null) context.contactManifoldTable.registerContact(convex1, convex2, collisionData); | ||
} | ||
/** | ||
* Detects convex-wall collisions and registers the Contact. | ||
* | ||
* @param {Object} context The Collision instance. | ||
* @param {Particle} convex The convex body collider. | ||
* @param {Wall} wall The wall collider. | ||
* @return {undefined} undefined | ||
*/ | ||
function convexIntersectWall(context, convex, wall) { | ||
if (convex.type === WALL) { | ||
var temp = wall; | ||
wall = convex; | ||
convex = temp; | ||
} | ||
var convexPos = convex.position; | ||
var wallPos = wall.position; | ||
var n = wall.normal; | ||
var invN = wall.invNormal; | ||
var rConvex = convex.support(invN); | ||
var wConvex = Vec3.add(convexPos, rConvex, new Vec3()); | ||
var diff = Vec3.subtract(wConvex, wallPos, VEC_REGISTER); | ||
var penetration = Vec3.dot(diff, invN); | ||
if (penetration < 0) return; | ||
var wWall = Vec3.scale(n, penetration, new Vec3()).add(wConvex); | ||
var rWall = Vec3.subtract(wWall, wall.position, new Vec3()); | ||
var collisionData = oMRequestCollisionData().reset(penetration, invN, wConvex, wWall, rConvex, rWall); | ||
context.contactManifoldTable.registerContact(convex, wall, collisionData); | ||
} | ||
Collision.SweepAndPrune = SweepAndPrune; | ||
Collision.BruteForce = BruteForce.BruteForce; | ||
Collision.BruteForceAABB = BruteForce.BruteForceAABB; | ||
module.exports = Collision; |
@@ -1,22 +0,84 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
var EventHandler = require('../../core/EventHandler'); | ||
function Constraint() { | ||
this.options = this.options || {}; | ||
this._eventOutput = new EventHandler(); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
'use strict'; | ||
var _ID = 0; | ||
/** | ||
* Base Constraint class to be used in the Physics | ||
* Subclass this class to implement a constraint | ||
* | ||
* @virtual | ||
* @class Constraint | ||
* @param {Object} options The options hash. | ||
*/ | ||
function Constraint(options) { | ||
options = options || {}; | ||
this.setOptions(options); | ||
this._ID = _ID++; | ||
} | ||
/** | ||
* Decorates the Constraint with the options object. | ||
* | ||
* @method | ||
* @param {Object} options The options hash. | ||
* @return {undefined} undefined | ||
*/ | ||
Constraint.prototype.setOptions = function setOptions(options) { | ||
this._eventOutput.emit('change', options); | ||
for (var key in options) this[key] = options[key]; | ||
this.init(options); | ||
}; | ||
Constraint.prototype.applyConstraint = function applyConstraint() { | ||
}; | ||
Constraint.prototype.getEnergy = function getEnergy() { | ||
return 0; | ||
}; | ||
module.exports = Constraint; | ||
/** | ||
* Method invoked upon instantiation and the setting of options. | ||
* | ||
* @method | ||
* @param {Object} options The options hash. | ||
* @return {undefined} undefined | ||
*/ | ||
Constraint.prototype.init = function init(options) {}; | ||
/** | ||
* Detect violations of the constraint. Warm start the constraint, if possible. | ||
* | ||
* @method | ||
* @param {Number} time The current time in the physics engine. | ||
* @param {Number} dt The physics engine frame delta. | ||
* @return {undefined} undefined | ||
*/ | ||
Constraint.prototype.update = function update(time, dt) {}; | ||
/** | ||
* Apply impulses to resolve the constraint. | ||
* | ||
* @method | ||
* @param {Number} time The current time in the physics engine. | ||
* @param {Number} dt The physics engine frame delta. | ||
* @return {undefined} undefined | ||
*/ | ||
Constraint.prototype.resolve = function resolve(time, dt) {}; | ||
module.exports = Constraint; |
@@ -1,78 +0,199 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
var Constraint = require('./Constraint'); | ||
var Vector = require('../../math/Vector'); | ||
function Curve(options) { | ||
this.options = Object.create(Curve.DEFAULT_OPTIONS); | ||
if (options) | ||
this.setOptions(options); | ||
this.J = new Vector(); | ||
this.impulse = new Vector(); | ||
Constraint.call(this); | ||
var Vec3 = require('../../math/Vec3'); | ||
var IMPULSE_REGISTER = new Vec3(); | ||
var NORMAL_REGISTER = new Vec3(); | ||
/** @const */ | ||
var EPSILSON = 1e-7; | ||
/** @const */ | ||
var PI = Math.PI; | ||
/** | ||
* A constraint that keeps a physics body on a given implicit curve. | ||
* | ||
* @class Curve | ||
* @extends Constraint | ||
* @param {Particle[]} targets The bodies to track. | ||
* @param {Object} options The options hash. | ||
*/ | ||
function Curve(targets, options) { | ||
if (targets) { | ||
if (targets instanceof Array) this.targets = targets; | ||
else this.targets = [targets]; | ||
} | ||
else this.targets = []; | ||
Constraint.call(this, options); | ||
this.impulses = {}; | ||
this.normals = {}; | ||
this.velocityBiases = {}; | ||
this.divisors = {}; | ||
} | ||
Curve.prototype = Object.create(Constraint.prototype); | ||
Curve.prototype.constructor = Curve; | ||
var epsilon = 1e-7; | ||
var pi = Math.PI; | ||
Curve.DEFAULT_OPTIONS = { | ||
equation: function (x, y, z) { | ||
/** | ||
* Initialize the Curve. Sets defaults if a property was not already set. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Curve.prototype.init = function() { | ||
this.equation1 = this.equation1 || function() { | ||
return 0; | ||
}, | ||
plane: function (x, y, z) { | ||
}; | ||
this.equation2 = this.equation2 || function(x, y, z) { | ||
return z; | ||
}, | ||
period: 0, | ||
dampingRatio: 0 | ||
}; | ||
this.period = this.period || 1; | ||
this.dampingRatio = this.dampingRatio || 0.5; | ||
this.stiffness = 4 * PI * PI / (this.period * this.period); | ||
this.damping = 4 * PI * this.dampingRatio / this.period; | ||
}; | ||
Curve.prototype.setOptions = function setOptions(options) { | ||
for (var key in options) | ||
this.options[key] = options[key]; | ||
}; | ||
Curve.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | ||
var options = this.options; | ||
var impulse = this.impulse; | ||
var J = this.J; | ||
var f = options.equation; | ||
var g = options.plane; | ||
var dampingRatio = options.dampingRatio; | ||
var period = options.period; | ||
for (var i = 0; i < targets.length; i++) { | ||
/** | ||
* Warmstart the constraint and prepare calculations used in the .resolve step. | ||
* | ||
* @method | ||
* @param {Number} time The current time in the physics engine. | ||
* @param {Number} dt The physics engine frame delta. | ||
* @return {undefined} undefined | ||
*/ | ||
Curve.prototype.update = function update(time, dt) { | ||
var targets = this.targets; | ||
var normals = this.normals; | ||
var velocityBiases = this.velocityBiases; | ||
var divisors = this.divisors; | ||
var impulses = this.impulses; | ||
var impulse = IMPULSE_REGISTER; | ||
var n = NORMAL_REGISTER; | ||
var f = this.equation1; | ||
var g = this.equation2; | ||
var _c = this.damping; | ||
var _k = this.stiffness; | ||
for (var i = 0, len = targets.length; i < len; i++) { | ||
var body = targets[i]; | ||
var v = body.velocity; | ||
var ID = body._ID; | ||
if (body.immune) continue; | ||
var p = body.position; | ||
var m = body.mass; | ||
var gamma; | ||
var beta; | ||
if (period === 0) { | ||
if (this.period === 0) { | ||
gamma = 0; | ||
beta = 1; | ||
} else { | ||
var c = 4 * m * pi * dampingRatio / period; | ||
var k = 4 * m * pi * pi / (period * period); | ||
gamma = 1 / (c + dt * k); | ||
beta = dt * k / (c + dt * k); | ||
} | ||
else { | ||
var c = _c * m; | ||
var k = _k * m; | ||
gamma = 1 / (dt*(c + dt*k)); | ||
beta = dt*k / (c + dt*k); | ||
} | ||
var x = p.x; | ||
var y = p.y; | ||
var z = p.z; | ||
var f0 = f(x, y, z); | ||
var dfx = (f(x + epsilon, y, z) - f0) / epsilon; | ||
var dfy = (f(x, y + epsilon, z) - f0) / epsilon; | ||
var dfz = (f(x, y, z + epsilon) - f0) / epsilon; | ||
var dfx = (f(x + EPSILSON, y, z) - f0) / EPSILSON; | ||
var dfy = (f(x, y + EPSILSON, z) - f0) / EPSILSON; | ||
var dfz = (f(x, y, z + EPSILSON) - f0) / EPSILSON; | ||
var g0 = g(x, y, z); | ||
var dgx = (g(x + epsilon, y, z) - g0) / epsilon; | ||
var dgy = (g(x, y + epsilon, z) - g0) / epsilon; | ||
var dgz = (g(x, y, z + epsilon) - g0) / epsilon; | ||
J.setXYZ(dfx + dgx, dfy + dgy, dfz + dgz); | ||
var antiDrift = beta / dt * (f0 + g0); | ||
var lambda = -(J.dot(v) + antiDrift) / (gamma + dt * J.normSquared() / m); | ||
impulse.set(J.mult(dt * lambda)); | ||
var dgx = (g(x + EPSILSON, y, z) - g0) / EPSILSON; | ||
var dgy = (g(x, y + EPSILSON, z) - g0) / EPSILSON; | ||
var dgz = (g(x, y, z + EPSILSON) - g0) / EPSILSON; | ||
n.set(dfx + dgx, dfy + dgy, dfz + dgz); | ||
n.normalize(); | ||
var baumgarte = beta * (f0 + g0) / dt; | ||
var divisor = gamma + 1 / m; | ||
var lambda = impulses[ID] || 0; | ||
Vec3.scale(n, lambda, impulse); | ||
body.applyImpulse(impulse); | ||
normals[ID] = normals[ID] || new Vec3(); | ||
normals[ID].copy(n); | ||
velocityBiases[ID] = baumgarte; | ||
divisors[ID] = divisor; | ||
impulses[ID] = 0; | ||
} | ||
}; | ||
module.exports = Curve; | ||
/** | ||
* Adds a curve impulse to a physics body. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Curve.prototype.resolve = function resolve() { | ||
var targets = this.targets; | ||
var normals = this.normals; | ||
var velocityBiases = this.velocityBiases; | ||
var divisors = this.divisors; | ||
var impulses = this.impulses; | ||
var impulse = IMPULSE_REGISTER; | ||
for (var i = 0, len = targets.length; i < len; i++) { | ||
var body = targets[i]; | ||
var ID = body._ID; | ||
if (body.immune) continue; | ||
var v = body.velocity; | ||
var n = normals[ID]; | ||
var lambda = -(Vec3.dot(n, v) + velocityBiases[ID]) / divisors[ID]; | ||
Vec3.scale(n, lambda, impulse); | ||
body.applyImpulse(impulse); | ||
impulses[ID] += lambda; | ||
} | ||
}; | ||
module.exports = Curve; |
@@ -1,110 +0,175 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
var Constraint = require('./Constraint'); | ||
var Vector = require('../../math/Vector'); | ||
function Distance(options) { | ||
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | ||
if (options) | ||
this.setOptions(options); | ||
this.impulse = new Vector(); | ||
this.normal = new Vector(); | ||
this.diffP = new Vector(); | ||
this.diffV = new Vector(); | ||
Constraint.call(this); | ||
var Vec3 = require('../../math/Vec3'); | ||
var NORMAL_REGISTER = new Vec3(); | ||
var IMPULSE_REGISTER = new Vec3(); | ||
var V_REGISTER = new Vec3(); | ||
var P_REGISTER = new Vec3(); | ||
/** @const */ | ||
var PI = Math.PI; | ||
/** | ||
* A constraint that keeps two bodies within a certain distance. | ||
* | ||
* @class Distance | ||
* @extends Constraint | ||
* @param {Particle} a One of the bodies. | ||
* @param {Particle} b The other body. | ||
* @param {Object} options An object of configurable options. | ||
*/ | ||
function Distance(a, b, options) { | ||
this.a = a; | ||
this.b = b; | ||
Constraint.call(this, options); | ||
this.impulse = 0; | ||
this.distance = 0; | ||
this.normal = new Vec3(); | ||
this.velocityBias = 0; | ||
this.divisor = 0; | ||
} | ||
Distance.prototype = Object.create(Constraint.prototype); | ||
Distance.prototype.constructor = Distance; | ||
Distance.DEFAULT_OPTIONS = { | ||
anchor: null, | ||
length: 0, | ||
minLength: 0, | ||
period: 0, | ||
dampingRatio: 0 | ||
/** | ||
* Initialize the Distance. Sets defaults if a property was not already set. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Distance.prototype.init = function() { | ||
this.length = this.length || Vec3.subtract(this.b.position, this.a.position, P_REGISTER).length(); | ||
this.minLength = this.minLength || 0; | ||
this.period = this.period || 0.2; | ||
this.dampingRatio = this.dampingRatio || 0.5; | ||
this.stiffness = 4 * PI * PI / (this.period * this.period); | ||
this.damping = 4 * PI * this.dampingRatio / this.period; | ||
}; | ||
var pi = Math.PI; | ||
Distance.prototype.setOptions = function setOptions(options) { | ||
if (options.anchor) { | ||
if (options.anchor.position instanceof Vector) | ||
this.options.anchor = options.anchor.position; | ||
if (options.anchor instanceof Vector) | ||
this.options.anchor = options.anchor; | ||
if (options.anchor instanceof Array) | ||
this.options.anchor = new Vector(options.anchor); | ||
/** | ||
* Detect violations of the constraint. Warm start the constraint, if possible. | ||
* | ||
* @method | ||
* @param {Number} time The current time in the physics engine. | ||
* @param {Number} dt The physics engine frame delta. | ||
* @return {undefined} undefined | ||
*/ | ||
Distance.prototype.update = function(time, dt) { | ||
var a = this.a; | ||
var b = this.b; | ||
var n = NORMAL_REGISTER; | ||
var diffP = P_REGISTER; | ||
var impulse = IMPULSE_REGISTER; | ||
var length = this.length; | ||
var p1 = a.position; | ||
var w1 = a.inverseMass; | ||
var p2 = b.position; | ||
var w2 = b.inverseMass; | ||
Vec3.subtract(p2, p1, diffP); | ||
var separation = diffP.length(); | ||
Vec3.scale(diffP, 1 / separation, n); | ||
var dist = separation - length; | ||
var invEffectiveMass = w1 + w2; | ||
var effectiveMass = 1 / invEffectiveMass; | ||
var gamma; | ||
var beta; | ||
if (this.period === 0) { | ||
gamma = 0; | ||
beta = 1; | ||
} | ||
if (options.length !== undefined) | ||
this.options.length = options.length; | ||
if (options.dampingRatio !== undefined) | ||
this.options.dampingRatio = options.dampingRatio; | ||
if (options.period !== undefined) | ||
this.options.period = options.period; | ||
if (options.minLength !== undefined) | ||
this.options.minLength = options.minLength; | ||
else { | ||
var c = this.damping * effectiveMass; | ||
var k = this.stiffness * effectiveMass; | ||
gamma = 1 / (dt*(c + dt*k)); | ||
beta = dt*k / (c + dt*k); | ||
} | ||
var baumgarte = beta * dist / dt; | ||
var divisor = gamma + invEffectiveMass; | ||
var lambda = this.impulse; | ||
Vec3.scale(n, lambda, impulse); | ||
b.applyImpulse(impulse); | ||
a.applyImpulse(impulse.invert()); | ||
this.normal.copy(n); | ||
this.distance = dist; | ||
this.velocityBias = baumgarte; | ||
this.divisor = divisor; | ||
this.impulse = 0; | ||
}; | ||
function _calcError(impulse, body) { | ||
return body.mass * impulse.norm(); | ||
} | ||
Distance.prototype.setAnchor = function setAnchor(anchor) { | ||
if (!this.options.anchor) | ||
this.options.anchor = new Vector(); | ||
this.options.anchor.set(anchor); | ||
}; | ||
Distance.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | ||
/** | ||
* Apply impulses to resolve the constraint. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Distance.prototype.resolve = function resolve() { | ||
var a = this.a; | ||
var b = this.b; | ||
var impulse = IMPULSE_REGISTER; | ||
var diffV = V_REGISTER; | ||
var minLength = this.minLength; | ||
var dist = this.distance; | ||
if (Math.abs(dist) < minLength) return; | ||
var v1 = a.getVelocity(); | ||
var v2 = b.getVelocity(); | ||
var n = this.normal; | ||
var diffP = this.diffP; | ||
var diffV = this.diffV; | ||
var impulse = this.impulse; | ||
var options = this.options; | ||
var dampingRatio = options.dampingRatio; | ||
var period = options.period; | ||
var minLength = options.minLength; | ||
var p2; | ||
var w2; | ||
if (source) { | ||
var v2 = source.velocity; | ||
p2 = source.position; | ||
w2 = source.inverseMass; | ||
} else { | ||
p2 = this.options.anchor; | ||
w2 = 0; | ||
} | ||
var length = this.options.length; | ||
for (var i = 0; i < targets.length; i++) { | ||
var body = targets[i]; | ||
var v1 = body.velocity; | ||
var p1 = body.position; | ||
var w1 = body.inverseMass; | ||
diffP.set(p1.sub(p2)); | ||
n.set(diffP.normalize()); | ||
var dist = diffP.norm() - length; | ||
if (Math.abs(dist) < minLength) | ||
return; | ||
if (source) | ||
diffV.set(v1.sub(v2)); | ||
else | ||
diffV.set(v1); | ||
var effMass = 1 / (w1 + w2); | ||
var gamma; | ||
var beta; | ||
if (period === 0) { | ||
gamma = 0; | ||
beta = 1; | ||
} else { | ||
var c = 4 * effMass * pi * dampingRatio / period; | ||
var k = 4 * effMass * pi * pi / (period * period); | ||
gamma = 1 / (c + dt * k); | ||
beta = dt * k / (c + dt * k); | ||
} | ||
var antiDrift = beta / dt * dist; | ||
var lambda = -(n.dot(diffV) + antiDrift) / (gamma + dt / effMass); | ||
impulse.set(n.mult(dt * lambda)); | ||
body.applyImpulse(impulse); | ||
if (source) | ||
source.applyImpulse(impulse.mult(-1)); | ||
} | ||
Vec3.subtract(v2, v1, diffV); | ||
var lambda = -(Vec3.dot(n, diffV) + this.velocityBias) / this.divisor; | ||
Vec3.scale(n, lambda, impulse); | ||
b.applyImpulse(impulse); | ||
a.applyImpulse(impulse.invert()); | ||
this.impulse += lambda; | ||
}; | ||
module.exports = Distance; | ||
module.exports = Distance; |
@@ -1,45 +0,107 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
var Force = require('./Force'); | ||
function Drag(options) { | ||
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | ||
if (options) | ||
this.setOptions(options); | ||
Force.call(this); | ||
var Vec3 = require('../../math/Vec3'); | ||
var FORCE_REGISTER = new Vec3(); | ||
/** | ||
* Use drag to oppose momentum of a moving object | ||
* | ||
* @class Drag | ||
* @extends Force | ||
* @param {Particle[]} targets The targets to affect. | ||
* @param {Object} options The options hash. | ||
*/ | ||
function Drag(targets, options) { | ||
Force.call(this, targets, options); | ||
} | ||
Drag.prototype = Object.create(Force.prototype); | ||
Drag.prototype.constructor = Drag; | ||
Drag.FORCE_FUNCTIONS = { | ||
LINEAR: function (velocity) { | ||
return velocity; | ||
}, | ||
QUADRATIC: function (velocity) { | ||
return velocity.mult(velocity.norm()); | ||
} | ||
/** | ||
* Used to scale velocity in the computation of the drag force. | ||
* | ||
* @property {Function} QUADRATIC | ||
* @param {Number} v The speed. | ||
* @return {Number} The scale by which to multiply. | ||
*/ | ||
Drag.QUADRATIC = function QUADRATIC(v) { | ||
return v*v; | ||
}; | ||
Drag.DEFAULT_OPTIONS = { | ||
strength: 0.01, | ||
forceFunction: Drag.FORCE_FUNCTIONS.LINEAR | ||
/** | ||
* Used to scale velocity in the computation of the drag force. | ||
* | ||
* @property {Function} LINEAR | ||
* @param {Number} v The speed. | ||
* @return {Number} The scale by which to multiply. | ||
*/ | ||
Drag.LINEAR = function LINEAR(v) { | ||
return v; | ||
}; | ||
Drag.prototype.applyForce = function applyForce(targets) { | ||
var strength = this.options.strength; | ||
var forceFunction = this.options.forceFunction; | ||
var force = this.force; | ||
var index; | ||
var particle; | ||
for (index = 0; index < targets.length; index++) { | ||
particle = targets[index]; | ||
forceFunction(particle.velocity).mult(-strength).put(force); | ||
particle.applyForce(force); | ||
/** | ||
* Initialize the Force. Sets defaults if a property was not already set. | ||
* | ||
* @method | ||
* @param {Object} options The options hash. | ||
* @return {undefined} undefined | ||
*/ | ||
Drag.prototype.init = function() { | ||
this.max = this.max || Infinity; | ||
this.strength = this.strength || 1; | ||
this.type = this.type || Drag.LINEAR; | ||
}; | ||
/** | ||
* Apply the force. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Drag.prototype.update = function update() { | ||
var targets = this.targets; | ||
var type = this.type; | ||
var force = FORCE_REGISTER; | ||
var max = this.max; | ||
var strength = this.strength; | ||
for (var i = 0, len = targets.length; i < len; i++) { | ||
var target = targets[i]; | ||
var velocity = target.velocity; | ||
var v = velocity.length(); | ||
var invV = v ? 1 / v : 0; | ||
var magnitude = -strength * type(v); | ||
Vec3.scale(velocity, (magnitude < -max ? -max : magnitude) * invV, force); | ||
target.applyForce(force); | ||
} | ||
}; | ||
Drag.prototype.setOptions = function setOptions(options) { | ||
for (var key in options) | ||
this.options[key] = options[key]; | ||
}; | ||
module.exports = Drag; | ||
module.exports = Drag; |
@@ -1,27 +0,104 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
var Vector = require('../../math/Vector'); | ||
var EventHandler = require('../../core/EventHandler'); | ||
function Force(force) { | ||
this.force = new Vector(force); | ||
this._eventOutput = new EventHandler(); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
'use strict'; | ||
var _ID = 0; | ||
/** | ||
* Abstract force manager to apply forces to targets. | ||
* | ||
* @class Force | ||
* @virtual | ||
* @param {Particle[]} targets The targets of the force. | ||
* @param {Object} options The options hash. | ||
*/ | ||
function Force(targets, options) { | ||
if (targets) { | ||
if (targets instanceof Array) this.targets = targets; | ||
else this.targets = [targets]; | ||
} | ||
else this.targets = []; | ||
options = options || {}; | ||
this.setOptions(options); | ||
this._ID = _ID++; | ||
} | ||
/** | ||
* Decorates the Force with the options object. | ||
* | ||
* @method | ||
* @param {Object} options The options hash. | ||
* @return {undefined} undefined | ||
*/ | ||
Force.prototype.setOptions = function setOptions(options) { | ||
this._eventOutput.emit('change', options); | ||
for (var key in options) this[key] = options[key]; | ||
this.init(options); | ||
}; | ||
Force.prototype.applyForce = function applyForce(targets) { | ||
var length = targets.length; | ||
while (length--) { | ||
targets[length].applyForce(this.force); | ||
} | ||
/** | ||
* Add a target or targets to the Force. | ||
* | ||
* @method | ||
* @param {Particle} target The body to begin targetting. | ||
* @return {undefined} undefined | ||
*/ | ||
Force.prototype.addTarget = function addTarget(target) { | ||
this.targets.push(target); | ||
}; | ||
Force.prototype.getEnergy = function getEnergy() { | ||
return 0; | ||
/** | ||
* Remove a target or targets from the Force. | ||
* | ||
* @method | ||
* @param {Particle} target The body to stop targetting. | ||
* @return {undefined} undefined | ||
*/ | ||
Force.prototype.removeTarget = function removeTarget(target) { | ||
var index = this.targets.indexOf(target); | ||
if (index < 0) return; | ||
this.targets.splice(index, 1); | ||
}; | ||
module.exports = Force; | ||
/** | ||
* Method invoked upon instantiation and the setting of options. | ||
* | ||
* @method | ||
* @param {Object} options The options hash. | ||
* @return {undefined} undefined | ||
*/ | ||
Force.prototype.init = function init(options) {}; | ||
/** | ||
* Apply forces on each target. | ||
* | ||
* @method | ||
* @param {Number} time The current time in the physics engine. | ||
* @param {Number} dt The physics engine frame delta. | ||
* @return {undefined} undefined | ||
*/ | ||
Force.prototype.update = function update(time, dt) {}; | ||
module.exports = Force; |
@@ -1,40 +0,103 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
var Drag = require('./Drag'); | ||
function RotationalDrag(options) { | ||
Drag.call(this, options); | ||
'use strict'; | ||
var Force = require('./Force'); | ||
var Vec3 = require('../../math/Vec3'); | ||
var TORQUE_REGISTER = new Vec3(); | ||
/** | ||
* A behavior that slows angular velocity by applying torque. | ||
* | ||
* @class RotationalDrag | ||
* @extends Force | ||
* @param {Particle[]} targets The targets to affect. | ||
* @param {Object} options options to set on drag | ||
*/ | ||
function RotationalDrag(targets, options) { | ||
Force.call(this, targets, options); | ||
} | ||
RotationalDrag.prototype = Object.create(Drag.prototype); | ||
RotationalDrag.prototype = Object.create(Force.prototype); | ||
RotationalDrag.prototype.constructor = RotationalDrag; | ||
RotationalDrag.DEFAULT_OPTIONS = Drag.DEFAULT_OPTIONS; | ||
RotationalDrag.FORCE_FUNCTIONS = Drag.FORCE_FUNCTIONS; | ||
RotationalDrag.FORCE_FUNCTIONS = { | ||
LINEAR: function (angularVelocity) { | ||
return angularVelocity; | ||
}, | ||
QUADRATIC: function (angularVelocity) { | ||
return angularVelocity.mult(angularVelocity.norm()); | ||
} | ||
/** | ||
* Used to scale angular velocity in the computation of the drag torque. | ||
* | ||
* @property {Function} QUADRATIC | ||
* @param {Vec3} omega The angular velocity. | ||
* @return {Number} The scale by which to multiply. | ||
*/ | ||
RotationalDrag.QUADRATIC = function QUADRATIC(omega) { | ||
return omega.length(); | ||
}; | ||
RotationalDrag.prototype.applyForce = function applyForce(targets) { | ||
var strength = this.options.strength; | ||
var forceFunction = this.options.forceFunction; | ||
var force = this.force; | ||
var index; | ||
var particle; | ||
for (index = 0; index < targets.length; index++) { | ||
particle = targets[index]; | ||
forceFunction(particle.angularVelocity).mult(-100 * strength).put(force); | ||
particle.applyTorque(force); | ||
/** | ||
* Used to scale angular velocity in the computation of the drag torque. | ||
* | ||
* @property {Function} LINEAR | ||
* @return {Number} The scale by which to multiply. | ||
*/ | ||
RotationalDrag.LINEAR = function LINEAR() { | ||
return 1; | ||
}; | ||
/** | ||
* Initialize the Force. Sets defaults if a property was not already set. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
RotationalDrag.prototype.init = function init() { | ||
this.max = this.max || Infinity; | ||
this.strength = this.strength || 1; | ||
this.type = this.type || RotationalDrag.LINEAR; | ||
}; | ||
/** | ||
* Adds a rotational drag force to a physics body's torque accumulator. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
RotationalDrag.prototype.update = function update() { | ||
var targets = this.targets; | ||
var type = this.type; | ||
var torque = TORQUE_REGISTER; | ||
var max = this.max; | ||
var strength = this.strength; | ||
for (var i = 0, len = targets.length; i < len; i++) { | ||
var target = targets[i]; | ||
var omega = target.angularVelocity; | ||
var magnitude = -strength * type(omega); | ||
Vec3.scale(omega, magnitude < -max ? -max : magnitude, torque); | ||
target.applyTorque(torque); | ||
} | ||
}; | ||
RotationalDrag.prototype.setOptions = function setOptions(options) { | ||
for (var key in options) | ||
this.options[key] = options[key]; | ||
}; | ||
module.exports = RotationalDrag; | ||
module.exports = RotationalDrag; |
@@ -1,94 +0,142 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
var Force = require('./Force'); | ||
var Spring = require('./Spring'); | ||
var Vec3 = require('../../math/Vec3'); | ||
var Mat33 = require('../../math/Mat33'); | ||
var Quaternion = require('../../math/Quaternion'); | ||
function RotationalSpring(options) { | ||
Spring.call(this, options); | ||
var Q_REGISTER = new Quaternion(); | ||
var DAMPING_REGISTER = new Vec3(); | ||
var XYZ_REGISTER = new Vec3(); | ||
var MAT_REGISTER = new Mat33(); | ||
/** @const PI */ | ||
var PI = Math.PI; | ||
/** | ||
* A spring-like behavior that attempts to enforce a specfic orientation by applying torque. | ||
* | ||
* @class RotationalSpring | ||
* @extends Force | ||
* @param {Particle} source The optional source of the spring. | ||
* @param {Particle[]} targets The targets to affect. | ||
* @param {Object} options The options hash. | ||
*/ | ||
function RotationalSpring(source, targets, options) { | ||
this.source = source || null; | ||
Force.call(this, targets, options); | ||
} | ||
RotationalSpring.prototype = Object.create(Spring.prototype); | ||
RotationalSpring.prototype = Object.create(Force.prototype); | ||
RotationalSpring.prototype.constructor = RotationalSpring; | ||
RotationalSpring.DEFAULT_OPTIONS = Spring.DEFAULT_OPTIONS; | ||
RotationalSpring.FORCE_FUNCTIONS = Spring.FORCE_FUNCTIONS; | ||
var pi = Math.PI; | ||
function _calcStiffness() { | ||
var options = this.options; | ||
options.stiffness = Math.pow(2 * pi / options.period, 2); | ||
} | ||
function _calcDamping() { | ||
var options = this.options; | ||
options.damping = 4 * pi * options.dampingRatio / options.period; | ||
} | ||
function _init() { | ||
_calcStiffness.call(this); | ||
_calcDamping.call(this); | ||
} | ||
RotationalSpring.prototype.setOptions = function setOptions(options) { | ||
if (options.anchor !== undefined) { | ||
if (options.anchor instanceof Quaternion) | ||
this.options.anchor = options.anchor; | ||
if (options.anchor instanceof Array) | ||
this.options.anchor = new Quaternion(options.anchor); | ||
/** | ||
* Initialize the Force. Sets defaults if a property was not already set. | ||
* | ||
* @method | ||
* @param {Object} options The options hash. | ||
* @return {undefined} undefined | ||
*/ | ||
RotationalSpring.prototype.init = function init(options) { | ||
if (!this.source) this.anchor = this.anchor ? this.anchor.normalize() : new Quaternion(1,0,0,0); | ||
if (options.stiffness || options.damping) { | ||
this.stiffness = this.stiffness || 100; | ||
this.damping = this.damping || 0; | ||
this.period = null; | ||
this.dampingRatio = null; | ||
} | ||
if (options.period !== undefined) { | ||
this.options.period = options.period; | ||
else if (options.period || options.dampingRatio) { | ||
this.period = this.period || 1; | ||
this.dampingRatio = this.dampingRatio || 0; | ||
this.stiffness = 2 * PI / this.period; | ||
this.stiffness *= this.stiffness; | ||
this.damping = 4 * PI * this.dampingRatio / this.period; | ||
} | ||
if (options.dampingRatio !== undefined) | ||
this.options.dampingRatio = options.dampingRatio; | ||
if (options.length !== undefined) | ||
this.options.length = options.length; | ||
if (options.forceFunction !== undefined) | ||
this.options.forceFunction = options.forceFunction; | ||
if (options.maxLength !== undefined) | ||
this.options.maxLength = options.maxLength; | ||
_init.call(this); | ||
Force.prototype.setOptions.call(this, options); | ||
}; | ||
RotationalSpring.prototype.applyForce = function applyForce(targets) { | ||
var force = this.force; | ||
var options = this.options; | ||
var disp = this.disp; | ||
var stiffness = options.stiffness; | ||
var damping = options.damping; | ||
var restLength = options.length; | ||
var anchor = options.anchor; | ||
var forceFunction = options.forceFunction; | ||
var maxLength = options.maxLength; | ||
var i; | ||
var target; | ||
var dist; | ||
var m; | ||
for (i = 0; i < targets.length; i++) { | ||
target = targets[i]; | ||
disp.set(anchor.sub(target.orientation)); | ||
dist = disp.norm() - restLength; | ||
if (dist === 0) | ||
return; | ||
m = target.mass; | ||
stiffness *= m; | ||
damping *= m; | ||
force.set(disp.normalize(stiffness * forceFunction(dist, maxLength))); | ||
if (damping) | ||
force.add(target.angularVelocity.mult(-damping)).put(force); | ||
target.applyTorque(force); | ||
} | ||
}; | ||
RotationalSpring.prototype.getEnergy = function getEnergy(targets) { | ||
var options = this.options; | ||
var restLength = options.length; | ||
var anchor = options.anchor; | ||
var strength = options.stiffness; | ||
var energy = 0; | ||
for (var i = 0; i < targets.length; i++) { | ||
/** | ||
* Adds a torque force to a physics body's torque accumulator. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
RotationalSpring.prototype.update = function update() { | ||
var source = this.source; | ||
var targets = this.targets; | ||
var deltaQ = Q_REGISTER; | ||
var dampingTorque = DAMPING_REGISTER; | ||
var XYZ = XYZ_REGISTER; | ||
var effInertia = MAT_REGISTER; | ||
var max = this.max; | ||
var stiffness = this.stiffness; | ||
var damping = this.damping; | ||
var anchor = this.anchor || source.orientation; | ||
var invSourceInertia = this.anchor ? null : source.inverseInertia; | ||
for (var i = 0, len = targets.length; i < len; i++) { | ||
var target = targets[i]; | ||
var dist = anchor.sub(target.orientation).norm() - restLength; | ||
energy += 0.5 * strength * dist * dist; | ||
var q = target.orientation; | ||
Quaternion.conjugate(q, deltaQ); | ||
deltaQ.multiply(anchor); | ||
if (deltaQ.w >= 1) continue; | ||
var halftheta = Math.acos(deltaQ.w); | ||
var length = Math.sqrt(1 - deltaQ.w * deltaQ.w); | ||
var deltaOmega = XYZ.copy(deltaQ).scale(2 * halftheta / length); | ||
deltaOmega.scale(stiffness); | ||
if (invSourceInertia !== null) { | ||
Mat33.add(invSourceInertia, target.inverseInertia, effInertia).inverse(); | ||
} | ||
else { | ||
Mat33.inverse(target.inverseInertia, effInertia); | ||
} | ||
if (damping !== 0) { | ||
if (source) { | ||
deltaOmega.add(Vec3.subtract(target.angularVelocity, source.angularVelocity, dampingTorque).scale(-damping)); | ||
} | ||
else { | ||
deltaOmega.add(Vec3.scale(target.angularVelocity, -damping, dampingTorque)); | ||
} | ||
} | ||
var torque = deltaOmega.applyMatrix(effInertia); | ||
var magnitude = torque.length(); | ||
if (magnitude > max) torque.scale(max/magnitude); | ||
target.applyTorque(torque); | ||
if (source) source.applyTorque(torque.invert()); | ||
} | ||
return energy; | ||
}; | ||
module.exports = RotationalSpring; | ||
module.exports = RotationalSpring; |
@@ -1,130 +0,164 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
var Force = require('./Force'); | ||
var Vector = require('../../math/Vector'); | ||
function Spring(options) { | ||
Force.call(this); | ||
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | ||
if (options) | ||
this.setOptions(options); | ||
this.disp = new Vector(0, 0, 0); | ||
_init.call(this); | ||
var Vec3 = require('../../math/Vec3'); | ||
var FORCE_REGISTER = new Vec3(); | ||
var DAMPING_REGISTER = new Vec3(); | ||
/** | ||
* A force that accelerates a Particle towards a specific anchor point. Can be anchored to | ||
* a Vec3 or another source Particle. | ||
* | ||
* @class Spring | ||
* @extends Force | ||
* @param {Particle} source The optional source of the spring. | ||
* @param {Particle[]} targets The targets to affect. | ||
* @param {Object} options The options hash. | ||
*/ | ||
function Spring(source, targets, options) { | ||
this.source = source || null; | ||
Force.call(this, targets, options); | ||
} | ||
Spring.prototype = Object.create(Force.prototype); | ||
Spring.prototype.constructor = Spring; | ||
var pi = Math.PI; | ||
var MIN_PERIOD = 150; | ||
Spring.FORCE_FUNCTIONS = { | ||
FENE: function (dist, rMax) { | ||
var rMaxSmall = rMax * 0.99; | ||
var r = Math.max(Math.min(dist, rMaxSmall), -rMaxSmall); | ||
return r / (1 - r * r / (rMax * rMax)); | ||
}, | ||
HOOK: function (dist) { | ||
return dist; | ||
} | ||
/** @const */ | ||
var PI = Math.PI; | ||
/** | ||
* A FENE (Finitely Extensible Nonlinear Elastic) spring force. See: http://en.wikipedia.org/wiki/FENE | ||
* | ||
* @property {Function} FENE | ||
* @param {Number} dist Current distance from source body. | ||
* @param {Number} rMax Maximum range of influence. | ||
* @return {Number} unscaled force | ||
*/ | ||
Spring.FENE = function(dist, rMax) { | ||
var rMaxSmall = rMax * 0.99; | ||
var r = Math.max(Math.min(dist, rMaxSmall), -rMaxSmall); | ||
return r / (1 - r * r/(rMax * rMax)); | ||
}; | ||
Spring.DEFAULT_OPTIONS = { | ||
period: 300, | ||
dampingRatio: 0.1, | ||
length: 0, | ||
maxLength: Infinity, | ||
anchor: undefined, | ||
forceFunction: Spring.FORCE_FUNCTIONS.HOOK | ||
/** | ||
* A Hookean spring force, linear in the displacement | ||
* see: http://en.wikipedia.org/wiki/Hooke's_law | ||
* @property {Function} HOOKE | ||
* @param {Number} dist Current distance from source body/ | ||
* @return {Number} unscaled force | ||
*/ | ||
Spring.HOOKE = function(dist) { | ||
return dist; | ||
}; | ||
function _calcStiffness() { | ||
var options = this.options; | ||
options.stiffness = Math.pow(2 * pi / options.period, 2); | ||
} | ||
function _calcDamping() { | ||
var options = this.options; | ||
options.damping = 4 * pi * options.dampingRatio / options.period; | ||
} | ||
function _init() { | ||
_calcStiffness.call(this); | ||
_calcDamping.call(this); | ||
} | ||
Spring.prototype.setOptions = function setOptions(options) { | ||
if (options.anchor !== undefined) { | ||
if (options.anchor.position instanceof Vector) | ||
this.options.anchor = options.anchor.position; | ||
if (options.anchor instanceof Vector) | ||
this.options.anchor = options.anchor; | ||
if (options.anchor instanceof Array) | ||
this.options.anchor = new Vector(options.anchor); | ||
/** | ||
* Initialize the Force. Sets defaults if a property was not already set. | ||
* | ||
* @method | ||
* @param {Object} options The options hash. | ||
* @return {undefined} undefined | ||
*/ | ||
Spring.prototype.init = function(options) { | ||
this.max = this.max || Infinity; | ||
this.length = this.length || 0; | ||
this.type = this.type || Spring.HOOKE; | ||
this.maxLength = this.maxLength || Infinity; | ||
if (options.stiffness || options.damping) { | ||
this.stiffness = this.stiffness || 100; | ||
this.damping = this.damping || 0; | ||
this.period = null; | ||
this.dampingRatio = null; | ||
} | ||
if (options.period !== undefined) { | ||
if (options.period < MIN_PERIOD) { | ||
options.period = MIN_PERIOD; | ||
console.warn('The period of a SpringTransition is capped at ' + MIN_PERIOD + ' ms. Use a SnapTransition for faster transitions'); | ||
} | ||
this.options.period = options.period; | ||
else if (options.period || options.dampingRatio) { | ||
this.period = this.period || 1; | ||
this.dampingRatio = this.dampingRatio || 0; | ||
this.stiffness = 2 * PI / this.period; | ||
this.stiffness *= this.stiffness; | ||
this.damping = 4 * PI * this.dampingRatio / this.period; | ||
} | ||
if (options.dampingRatio !== undefined) | ||
this.options.dampingRatio = options.dampingRatio; | ||
if (options.length !== undefined) | ||
this.options.length = options.length; | ||
if (options.forceFunction !== undefined) | ||
this.options.forceFunction = options.forceFunction; | ||
if (options.maxLength !== undefined) | ||
this.options.maxLength = options.maxLength; | ||
_init.call(this); | ||
Force.prototype.setOptions.call(this, options); | ||
}; | ||
Spring.prototype.applyForce = function applyForce(targets, source) { | ||
var force = this.force; | ||
var disp = this.disp; | ||
var options = this.options; | ||
var stiffness = options.stiffness; | ||
var damping = options.damping; | ||
var restLength = options.length; | ||
var maxLength = options.maxLength; | ||
var anchor = options.anchor || source.position; | ||
var forceFunction = options.forceFunction; | ||
var i; | ||
var target; | ||
var p2; | ||
var v2; | ||
var dist; | ||
var m; | ||
for (i = 0; i < targets.length; i++) { | ||
target = targets[i]; | ||
p2 = target.position; | ||
v2 = target.velocity; | ||
anchor.sub(p2).put(disp); | ||
dist = disp.norm() - restLength; | ||
if (dist === 0) | ||
return; | ||
m = target.mass; | ||
stiffness *= m; | ||
damping *= m; | ||
disp.normalize(stiffness * forceFunction(dist, maxLength)).put(force); | ||
if (damping) | ||
if (source) | ||
force.add(v2.sub(source.velocity).mult(-damping)).put(force); | ||
else | ||
force.add(v2.mult(-damping)).put(force); | ||
/** | ||
* Apply the force. | ||
* | ||
* @method | ||
* @return {undefined} undefined | ||
*/ | ||
Spring.prototype.update = function() { | ||
var source = this.source; | ||
var targets = this.targets; | ||
var force = FORCE_REGISTER; | ||
var dampingForce = DAMPING_REGISTER; | ||
var max = this.max; | ||
var stiffness = this.stiffness; | ||
var damping = this.damping; | ||
var restLength = this.length; | ||
var maxLength = this.maxLength; | ||
var anchor = this.anchor || source.position; | ||
var invSourceMass = this.anchor ? 0 : source.inverseMass; | ||
var type = this.type; | ||
for (var i = 0, len = targets.length; i < len; i++) { | ||
var target = targets[i]; | ||
Vec3.subtract(anchor, target.position, force); | ||
var dist = force.length(); | ||
var stretch = dist - restLength; | ||
if (Math.abs(stretch) < 1e-6) continue; | ||
var effMass = 1 / (target.inverseMass + invSourceMass); | ||
if (this.period !== null) { | ||
stiffness *= effMass; | ||
damping *= effMass; | ||
} | ||
force.scale(stiffness * type(stretch, maxLength) / stretch); | ||
if (damping !== 0) { | ||
if (source) { | ||
force.add(Vec3.subtract(target.velocity, source.velocity, dampingForce).scale(-damping)); | ||
} | ||
else { | ||
force.add(Vec3.scale(target.velocity, -damping, dampingForce)); | ||
} | ||
} | ||
var magnitude = force.length(); | ||
var invMag = magnitude ? 1 / magnitude : 0; | ||
Vec3.scale(force, (magnitude > max ? max : magnitude) * invMag, force); | ||
target.applyForce(force); | ||
if (source) | ||
source.applyForce(force.mult(-1)); | ||
if (source) source.applyForce(force.invert()); | ||
} | ||
}; | ||
Spring.prototype.getEnergy = function getEnergy(targets, source) { | ||
var options = this.options; | ||
var restLength = options.length; | ||
var anchor = source ? source.position : options.anchor; | ||
var strength = options.stiffness; | ||
var energy = 0; | ||
for (var i = 0; i < targets.length; i++) { | ||
var target = targets[i]; | ||
var dist = anchor.sub(target.position).norm() - restLength; | ||
energy += 0.5 * strength * dist * dist; | ||
} | ||
return energy; | ||
}; | ||
module.exports = Spring; | ||
module.exports = Spring; |
@@ -0,7 +1,53 @@ | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
module.exports = { | ||
PhysicsEngine: require('./PhysicsEngine'), | ||
bodies: require('./bodies'), | ||
constraints: require('./constraints'), | ||
forces: require('./forces'), | ||
integrators: require('./integrators') | ||
Particle: require('./bodies/Particle'), | ||
convexBodyFactory: require('./bodies/convexBodyFactory'), | ||
Box: require('./bodies/Box'), | ||
Sphere: require('./bodies/Sphere'), | ||
Wall: require('./bodies/Wall'), | ||
Constraint: require('./constraints/Constraint'), | ||
Angle: require('./constraints/Angle'), | ||
Collision: require('./constraints/Collision'), | ||
Direction: require('./constraints/Direction'), | ||
Distance: require('./constraints/Distance'), | ||
Curve: require('./constraints/Curve'), | ||
Hinge: require('./constraints/Hinge'), | ||
BallAndSocket: require('./constraints/BallAndSocket'), | ||
Force: require('./forces/Force'), | ||
Drag: require('./forces/Drag'), | ||
RotationalDrag: require('./forces/RotationalDrag'), | ||
Gravity1D: require('./forces/Gravity1D'), | ||
Gravity3D: require('./forces/Gravity3D'), | ||
Spring: require('./forces/Spring'), | ||
RotationalSpring: require('./forces/RotationalSpring'), | ||
PhysicsEngine: require('./PhysicsEngine'), | ||
Geometry: require('./Geometry') | ||
}; |
@@ -1,274 +0,498 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
var EventHandler = require('../core/EventHandler'); | ||
'use strict'; | ||
var Particle = require('./bodies/Particle'); | ||
var Constraint = require('./constraints/Constraint'); | ||
var Force = require('./forces/Force'); | ||
var CallbackStore = require('../utilities/CallbackStore'); | ||
var Vec3 = require('../math/Vec3'); | ||
var Quaternion = require('../math/Quaternion'); | ||
var VEC_REGISTER = new Vec3(); | ||
var QUAT_REGISTER = new Quaternion(); | ||
var DELTA_REGISTER = new Vec3(); | ||
/** | ||
* Singleton PhysicsEngine object. | ||
* Manages bodies, forces, constraints. | ||
* | ||
* @class PhysicsEngine | ||
* @param {Object} options A hash of configurable options. | ||
*/ | ||
function PhysicsEngine(options) { | ||
this.options = Object.create(PhysicsEngine.DEFAULT_OPTIONS); | ||
if (options) | ||
this.setOptions(options); | ||
this._particles = []; | ||
this._bodies = []; | ||
this._agentData = {}; | ||
this._forces = []; | ||
this._constraints = []; | ||
this._buffer = 0; | ||
this._prevTime = now(); | ||
this._isSleeping = false; | ||
this._eventHandler = null; | ||
this._currAgentId = 0; | ||
this._hasBodies = false; | ||
this._eventHandler = null; | ||
this.events = new CallbackStore(); | ||
options = options || {}; | ||
/** @prop bodies The bodies currently active in the engine. */ | ||
this.bodies = []; | ||
/** @prop forces The forces currently active in the engine. */ | ||
this.forces = []; | ||
/** @prop constraints The constraints currently active in the engine. */ | ||
this.constraints = []; | ||
/** @prop step The time between frames in the engine. */ | ||
this.step = options.step || 1000/60; | ||
/** @prop iterations The number of times each constraint is solved per frame. */ | ||
this.iterations = options.iterations || 10; | ||
/** @prop _indexPool Pools of indicies to track holes in the arrays. */ | ||
this._indexPools = { | ||
bodies: [], | ||
forces: [], | ||
constraints: [] | ||
}; | ||
this._entityMaps = { | ||
bodies: {}, | ||
forces: {}, | ||
constraints: {} | ||
}; | ||
this.speed = options.speed || 1.0; | ||
this.time = 0; | ||
this.delta = 0; | ||
this.origin = options.origin || new Vec3(); | ||
this.orientation = options.orientation ? options.orientation.normalize() : new Quaternion(); | ||
this.frameDependent = options.frameDependent || false; | ||
this.transformBuffers = { | ||
position: [0, 0, 0], | ||
rotation: [0, 0, 0, 1] | ||
}; | ||
} | ||
var TIMESTEP = 17; | ||
var MIN_TIME_STEP = 1000 / 120; | ||
var MAX_TIME_STEP = 17; | ||
var now = Date.now; | ||
var _events = { | ||
start: 'start', | ||
update: 'update', | ||
end: 'end' | ||
/** | ||
* Listen for a specific event. | ||
* | ||
* @method | ||
* @param {String} key Name of the event. | ||
* @param {Function} callback Callback to register for the event. | ||
* @return {PhysicsEngine} this | ||
*/ | ||
PhysicsEngine.prototype.on = function on(key, callback) { | ||
this.events.on(key, callback); | ||
return this; | ||
}; | ||
PhysicsEngine.DEFAULT_OPTIONS = { | ||
constraintSteps: 1, | ||
sleepTolerance: 1e-7, | ||
velocityCap: undefined, | ||
angularVelocityCap: undefined | ||
/** | ||
* Stop listening for a specific event. | ||
* | ||
* @method | ||
* @param {String} key Name of the event. | ||
* @param {Function} callback Callback to deregister for the event. | ||
* @return {PhysicsEngine} this | ||
*/ | ||
PhysicsEngine.prototype.off = function off(key, callback) { | ||
this.events.off(key, callback); | ||
return this; | ||
}; | ||
PhysicsEngine.prototype.setOptions = function setOptions(opts) { | ||
for (var key in opts) | ||
if (this.options[key]) | ||
this.options[key] = opts[key]; | ||
/** | ||
* Trigger an event. | ||
* | ||
* @method | ||
* @param {String} key Name of the event. | ||
* @param {Object} payload Payload to pass to the event listeners. | ||
* @return {PhysicsEngine} this | ||
*/ | ||
PhysicsEngine.prototype.trigger = function trigger(key, payload) { | ||
this.events.trigger(key, payload); | ||
return this; | ||
}; | ||
PhysicsEngine.prototype.addBody = function addBody(body) { | ||
body._engine = this; | ||
if (body.isBody) { | ||
this._bodies.push(body); | ||
this._hasBodies = true; | ||
} else | ||
this._particles.push(body); | ||
body.on('start', this.wake.bind(this)); | ||
return body; | ||
/** | ||
* Set the origin of the world. | ||
* | ||
* @method | ||
* @chainable | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* @return {PhysicsEngine} this | ||
*/ | ||
PhysicsEngine.prototype.setOrigin = function setOrigin(x, y, z) { | ||
this.origin.set(x, y, z); | ||
return this; | ||
}; | ||
PhysicsEngine.prototype.removeBody = function removeBody(body) { | ||
var array = body.isBody ? this._bodies : this._particles; | ||
var index = array.indexOf(body); | ||
if (index > -1) { | ||
for (var agentKey in this._agentData) { | ||
if (this._agentData.hasOwnProperty(agentKey)) { | ||
this.detachFrom(this._agentData[agentKey].id, body); | ||
/** | ||
* Set the orientation of the world. | ||
* | ||
* @method | ||
* @chainable | ||
* @param {Number} w The w component. | ||
* @param {Number} x The x component. | ||
* @param {Number} y The y component. | ||
* @param {Number} z The z component. | ||
* @return {PhysicsEngine} this | ||
*/ | ||
PhysicsEngine.prototype.setOrientation = function setOrientation(w, x, y, z) { | ||
this.orientation.set(w, x, y, z).normalize(); | ||
return this; | ||
}; | ||
/** | ||
* Private helper method to store an element in a library array. | ||
* | ||
* @method | ||
* @private | ||
* @param {Object} context Object in possesion of the element arrays. | ||
* @param {Object} element The body, force, or constraint to add. | ||
* @param {String} key Where to store the element. | ||
* @return {undefined} undefined | ||
*/ | ||
function _addElement(context, element, key) { | ||
var map = context._entityMaps[key]; | ||
if (map[element._ID] == null) { | ||
var library = context[key]; | ||
var indexPool = context._indexPools[key]; | ||
if (indexPool.length) map[element._ID] = indexPool.pop(); | ||
else map[element._ID] = library.length; | ||
library[map[element._ID]] = element; | ||
} | ||
} | ||
/** | ||
* Private helper method to remove an element from a library array. | ||
* | ||
* @method | ||
* @private | ||
* @param {Object} context Object in possesion of the element arrays. | ||
* @param {Object} element The body, force, or constraint to remove. | ||
* @param {String} key Where to store the element. | ||
* @return {undefined} undefined | ||
*/ | ||
function _removeElement(context, element, key) { | ||
var map = context._entityMaps[key]; | ||
var index = map[element._ID]; | ||
if (index != null) { | ||
context._indexPools[key].push(index); | ||
context[key][index] = null; | ||
map[element._ID] = null; | ||
} | ||
} | ||
/** | ||
* Add a group of bodies, force, or constraints to the engine. | ||
* | ||
* @method | ||
* @return {PhysicsEngine} this | ||
*/ | ||
PhysicsEngine.prototype.add = function add() { | ||
for (var j = 0, lenj = arguments.length; j < lenj; j++) { | ||
var entity = arguments[j]; | ||
if (entity instanceof Array) { | ||
for (var i = 0, len = entity.length; i < len; i++) { | ||
var e = entity[i]; | ||
this.add(e); | ||
} | ||
} | ||
array.splice(index, 1); | ||
else { | ||
if (entity instanceof Particle) this.addBody(entity); | ||
else if (entity instanceof Constraint) this.addConstraint(entity); | ||
else if (entity instanceof Force) this.addForce(entity); | ||
} | ||
} | ||
if (this.getBodies().length === 0) | ||
this._hasBodies = false; | ||
return this; | ||
}; | ||
function _mapAgentArray(agent) { | ||
if (agent.applyForce) | ||
return this._forces; | ||
if (agent.applyConstraint) | ||
return this._constraints; | ||
} | ||
function _attachOne(agent, targets, source) { | ||
if (targets === undefined) | ||
targets = this.getParticlesAndBodies(); | ||
if (!(targets instanceof Array)) | ||
targets = [targets]; | ||
agent.on('change', this.wake.bind(this)); | ||
this._agentData[this._currAgentId] = { | ||
agent: agent, | ||
id: this._currAgentId, | ||
targets: targets, | ||
source: source | ||
}; | ||
_mapAgentArray.call(this, agent).push(this._currAgentId); | ||
return this._currAgentId++; | ||
} | ||
PhysicsEngine.prototype.attach = function attach(agents, targets, source) { | ||
this.wake(); | ||
if (agents instanceof Array) { | ||
var agentIDs = []; | ||
for (var i = 0; i < agents.length; i++) | ||
agentIDs[i] = _attachOne.call(this, agents[i], targets, source); | ||
return agentIDs; | ||
} else | ||
return _attachOne.call(this, agents, targets, source); | ||
}; | ||
PhysicsEngine.prototype.attachTo = function attachTo(agentID, target) { | ||
_getAgentData.call(this, agentID).targets.push(target); | ||
}; | ||
PhysicsEngine.prototype.detach = function detach(id) { | ||
var agent = this.getAgent(id); | ||
var agentArray = _mapAgentArray.call(this, agent); | ||
var index = agentArray.indexOf(id); | ||
agentArray.splice(index, 1); | ||
delete this._agentData[id]; | ||
}; | ||
PhysicsEngine.prototype.detachFrom = function detachFrom(id, target) { | ||
var boundAgent = _getAgentData.call(this, id); | ||
if (boundAgent.source === target) | ||
this.detach(id); | ||
else { | ||
var targets = boundAgent.targets; | ||
var index = targets.indexOf(target); | ||
if (index > -1) | ||
targets.splice(index, 1); | ||
/** | ||
* Remove a group of bodies, force, or constraints from the engine. | ||
* | ||
* @method | ||
* @return {PhysicsEngine} this | ||
*/ | ||
PhysicsEngine.prototype.remove = function remove() { | ||
for (var j = 0, lenj = arguments.length; j < lenj; j++) { | ||
var entity = arguments[j]; | ||
if (entity instanceof Array) { | ||
for (var i = 0, len = entity.length; i < len; i++) { | ||
var e = entity[i]; | ||
this.add(e); | ||
} | ||
} | ||
else { | ||
if (entity instanceof Particle) this.removeBody(entity); | ||
else if (entity instanceof Constraint) this.removeConstraint(entity); | ||
else if (entity instanceof Force) this.removeForce(entity); | ||
} | ||
} | ||
return this; | ||
}; | ||
PhysicsEngine.prototype.detachAll = function detachAll() { | ||
this._agentData = {}; | ||
this._forces = []; | ||
this._constraints = []; | ||
this._currAgentId = 0; | ||
/** | ||
* Begin tracking a body. | ||
* | ||
* @method | ||
* @param {Particle} body The body to track. | ||
* @return {undefined} undefined | ||
*/ | ||
PhysicsEngine.prototype.addBody = function addBody(body) { | ||
_addElement(this, body, 'bodies'); | ||
}; | ||
function _getAgentData(id) { | ||
return this._agentData[id]; | ||
} | ||
PhysicsEngine.prototype.getAgent = function getAgent(id) { | ||
return _getAgentData.call(this, id).agent; | ||
/** | ||
* Begin tracking a force. | ||
* | ||
* @method | ||
* @param {Force} force The force to track. | ||
* @return {undefined} undefined | ||
*/ | ||
PhysicsEngine.prototype.addForce = function addForce(force) { | ||
_addElement(this, force, 'forces'); | ||
}; | ||
PhysicsEngine.prototype.getParticles = function getParticles() { | ||
return this._particles; | ||
/** | ||
* Begin tracking a constraint. | ||
* | ||
* @method | ||
* @param {Constraint} constraint The constraint to track. | ||
* @return {undefined} undefined | ||
*/ | ||
PhysicsEngine.prototype.addConstraint = function addConstraint(constraint) { | ||
_addElement(this, constraint, 'constraints'); | ||
}; | ||
PhysicsEngine.prototype.getBodies = function getBodies() { | ||
return this._bodies; | ||
/** | ||
* Stop tracking a body. | ||
* | ||
* @method | ||
* @param {Particle} body The body to stop tracking. | ||
* @return {undefined} undefined | ||
*/ | ||
PhysicsEngine.prototype.removeBody = function removeBody(body) { | ||
_removeElement(this, body, 'bodies'); | ||
}; | ||
PhysicsEngine.prototype.getParticlesAndBodies = function getParticlesAndBodies() { | ||
return this.getParticles().concat(this.getBodies()); | ||
/** | ||
* Stop tracking a force. | ||
* | ||
* @method | ||
* @param {Force} force The force to stop tracking. | ||
* @return {undefined} undefined | ||
*/ | ||
PhysicsEngine.prototype.removeForce = function removeForce(force) { | ||
_removeElement(this, force, 'forces'); | ||
}; | ||
PhysicsEngine.prototype.forEachParticle = function forEachParticle(fn, dt) { | ||
var particles = this.getParticles(); | ||
for (var index = 0, len = particles.length; index < len; index++) | ||
fn.call(this, particles[index], dt); | ||
/** | ||
* Stop tracking a constraint. | ||
* | ||
* @method | ||
* @param {Constraint} constraint The constraint to stop tracking. | ||
* @return {undefined} undefined | ||
*/ | ||
PhysicsEngine.prototype.removeConstraint = function removeConstraint(constraint) { | ||
_removeElement(this, constraint, 'constraints'); | ||
}; | ||
PhysicsEngine.prototype.forEachBody = function forEachBody(fn, dt) { | ||
if (!this._hasBodies) | ||
return; | ||
var bodies = this.getBodies(); | ||
for (var index = 0, len = bodies.length; index < len; index++) | ||
fn.call(this, bodies[index], dt); | ||
/** | ||
* Update the physics system to reflect the changes since the last frame. Steps forward in increments of | ||
* PhysicsEngine.step. | ||
* | ||
* @method | ||
* @param {Number} time The time to which to update. | ||
* @return {undefined} undefined | ||
*/ | ||
PhysicsEngine.prototype.update = function update(time) { | ||
if (this.time === 0) this.time = time; | ||
var bodies = this.bodies; | ||
var forces = this.forces; | ||
var constraints = this.constraints; | ||
var frameDependent = this.frameDependent; | ||
var step = this.step; | ||
var dt = step * 0.001; | ||
var speed = this.speed; | ||
var delta = this.delta; | ||
delta += (time - this.time) * speed; | ||
this.time = time; | ||
var i, len; | ||
var force, body, constraint; | ||
while(delta > step) { | ||
this.events.trigger('prestep', time); | ||
// Update Forces on particles | ||
for (i = 0, len = forces.length; i < len; i++) { | ||
force = forces[i]; | ||
if (force === null) continue; | ||
force.update(time, dt); | ||
} | ||
// Tentatively update velocities | ||
for (i = 0, len = bodies.length; i < len; i++) { | ||
body = bodies[i]; | ||
if (body === null) continue; | ||
_integrateVelocity(body, dt); | ||
} | ||
// Prep constraints for solver | ||
for (i = 0, len = constraints.length; i < len; i++) { | ||
constraint = constraints[i]; | ||
if (constraint === null) continue; | ||
constraint.update(time, dt); | ||
} | ||
// Iteratively resolve constraints | ||
for (var j = 0, numIterations = this.iterations; j < numIterations; j++) { | ||
for (i = 0, len = constraints.length; i < len; i++) { | ||
constraint = constraints[i]; | ||
if (constraint === null) continue; | ||
constraint.resolve(time, dt); | ||
} | ||
} | ||
// Increment positions and orientations | ||
for (i = 0, len = bodies.length; i < len; i++) { | ||
body = bodies[i]; | ||
if (body === null) continue; | ||
_integratePose(body, dt); | ||
} | ||
this.events.trigger('poststep', time); | ||
if (frameDependent) delta = 0; | ||
else delta -= step; | ||
} | ||
this.delta = delta; | ||
}; | ||
PhysicsEngine.prototype.forEach = function forEach(fn, dt) { | ||
this.forEachParticle(fn, dt); | ||
this.forEachBody(fn, dt); | ||
/** | ||
* Transform the body position and rotation to world coordinates. | ||
* | ||
* @method | ||
* @param {Particle} body The body to retrieve the transform of. | ||
* @return {Object} Position and rotation of the boy, taking into account | ||
* the origin and orientation of the world. | ||
*/ | ||
PhysicsEngine.prototype.getTransform = function getTransform(body) { | ||
var o = this.origin; | ||
var oq = this.orientation; | ||
var transform = this.transformBuffers; | ||
var p = body.position; | ||
var q = body.orientation; | ||
var rot = q; | ||
var loc = p; | ||
if (oq.w !== 1) { | ||
rot = Quaternion.multiply(q, oq, QUAT_REGISTER); | ||
loc = oq.rotateVector(p, VEC_REGISTER); | ||
} | ||
transform.position[0] = o.x+loc.x; | ||
transform.position[1] = o.y+loc.y; | ||
transform.position[2] = o.z+loc.z; | ||
transform.rotation[0] = rot.x; | ||
transform.rotation[1] = rot.y; | ||
transform.rotation[2] = rot.z; | ||
transform.rotation[3] = rot.w; | ||
return transform; | ||
}; | ||
function _updateForce(index) { | ||
var boundAgent = _getAgentData.call(this, this._forces[index]); | ||
boundAgent.agent.applyForce(boundAgent.targets, boundAgent.source); | ||
/** | ||
* Update the Particle momenta based off of current incident force and torque. | ||
* | ||
* @method | ||
* @private | ||
* @param {Particle} body The body to update. | ||
* @param {Number} dt Delta time. | ||
* @return {undefined} undefined | ||
*/ | ||
function _integrateVelocity(body, dt) { | ||
body.momentum.add(Vec3.scale(body.force, dt, DELTA_REGISTER)); | ||
body.angularMomentum.add(Vec3.scale(body.torque, dt, DELTA_REGISTER)); | ||
Vec3.scale(body.momentum, body.inverseMass, body.velocity); | ||
body.inverseInertia.vectorMultiply(body.angularMomentum, body.angularVelocity); | ||
body.force.clear(); | ||
body.torque.clear(); | ||
} | ||
function _updateForces() { | ||
for (var index = this._forces.length - 1; index > -1; index--) | ||
_updateForce.call(this, index); | ||
} | ||
function _updateConstraint(index, dt) { | ||
var boundAgent = this._agentData[this._constraints[index]]; | ||
return boundAgent.agent.applyConstraint(boundAgent.targets, boundAgent.source, dt); | ||
} | ||
function _updateConstraints(dt) { | ||
var iteration = 0; | ||
while (iteration < this.options.constraintSteps) { | ||
for (var index = this._constraints.length - 1; index > -1; index--) | ||
_updateConstraint.call(this, index, dt); | ||
iteration++; | ||
/** | ||
* Update the Particle position and orientation based off current translational and angular velocities. | ||
* | ||
* @method | ||
* @private | ||
* @param {Particle} body The body to update. | ||
* @param {Number} dt Delta time. | ||
* @return {undefined} undefined | ||
*/ | ||
function _integratePose(body, dt) { | ||
if (body.restrictions !== 0) { | ||
var restrictions = body.restrictions; | ||
var x = null; | ||
var y = null; | ||
var z = null; | ||
var ax = null; | ||
var ay = null; | ||
var az = null; | ||
if (restrictions & 32) x = 0; | ||
if (restrictions & 16) y = 0; | ||
if (restrictions & 8) z = 0; | ||
if (restrictions & 4) ax = 0; | ||
if (restrictions & 2) ay = 0; | ||
if (restrictions & 1) az = 0; | ||
if (x !== null || y !== null || z !== null) body.setVelocity(x,y,z); | ||
if (ax !== null || ay !== null || az !== null) body.setAngularVelocity(ax, ay, az); | ||
} | ||
body.position.add(Vec3.scale(body.velocity, dt, DELTA_REGISTER)); | ||
var w = body.angularVelocity; | ||
var q = body.orientation; | ||
var wx = w.x; | ||
var wy = w.y; | ||
var wz = w.z; | ||
var qw = q.w; | ||
var qx = q.x; | ||
var qy = q.y; | ||
var qz = q.z; | ||
var hdt = dt * 0.5; | ||
q.w += (-wx * qx - wy * qy - wz * qz) * hdt; | ||
q.x += (wx * qw + wy * qz - wz * qy) * hdt; | ||
q.y += (wy * qw + wz * qx - wx * qz) * hdt; | ||
q.z += (wz * qw + wx * qy - wy * qx) * hdt; | ||
q.normalize(); | ||
body.updateInertia(); | ||
} | ||
function _updateVelocities(body, dt) { | ||
body.integrateVelocity(dt); | ||
if (this.options.velocityCap) | ||
body.velocity.cap(this.options.velocityCap).put(body.velocity); | ||
} | ||
function _updateAngularVelocities(body, dt) { | ||
body.integrateAngularMomentum(dt); | ||
body.updateAngularVelocity(); | ||
if (this.options.angularVelocityCap) | ||
body.angularVelocity.cap(this.options.angularVelocityCap).put(body.angularVelocity); | ||
} | ||
function _updateOrientations(body, dt) { | ||
body.integrateOrientation(dt); | ||
} | ||
function _updatePositions(body, dt) { | ||
body.integratePosition(dt); | ||
body.emit(_events.update, body); | ||
} | ||
function _integrate(dt) { | ||
_updateForces.call(this, dt); | ||
this.forEach(_updateVelocities, dt); | ||
this.forEachBody(_updateAngularVelocities, dt); | ||
_updateConstraints.call(this, dt); | ||
this.forEachBody(_updateOrientations, dt); | ||
this.forEach(_updatePositions, dt); | ||
} | ||
function _getParticlesEnergy() { | ||
var energy = 0; | ||
var particleEnergy = 0; | ||
this.forEach(function (particle) { | ||
particleEnergy = particle.getEnergy(); | ||
energy += particleEnergy; | ||
}); | ||
return energy; | ||
} | ||
function _getAgentsEnergy() { | ||
var energy = 0; | ||
for (var id in this._agentData) | ||
energy += this.getAgentEnergy(id); | ||
return energy; | ||
} | ||
PhysicsEngine.prototype.getAgentEnergy = function (agentId) { | ||
var agentData = _getAgentData.call(this, agentId); | ||
return agentData.agent.getEnergy(agentData.targets, agentData.source); | ||
}; | ||
PhysicsEngine.prototype.getEnergy = function getEnergy() { | ||
return _getParticlesEnergy.call(this) + _getAgentsEnergy.call(this); | ||
}; | ||
PhysicsEngine.prototype.step = function step() { | ||
if (this.isSleeping()) | ||
return; | ||
var currTime = now(); | ||
var dtFrame = currTime - this._prevTime; | ||
this._prevTime = currTime; | ||
if (dtFrame < MIN_TIME_STEP) | ||
return; | ||
if (dtFrame > MAX_TIME_STEP) | ||
dtFrame = MAX_TIME_STEP; | ||
_integrate.call(this, TIMESTEP); | ||
this.emit(_events.update, this); | ||
if (this.getEnergy() < this.options.sleepTolerance) | ||
this.sleep(); | ||
}; | ||
PhysicsEngine.prototype.isSleeping = function isSleeping() { | ||
return this._isSleeping; | ||
}; | ||
PhysicsEngine.prototype.isActive = function isSleeping() { | ||
return !this._isSleeping; | ||
}; | ||
PhysicsEngine.prototype.sleep = function sleep() { | ||
if (this._isSleeping) | ||
return; | ||
this.forEach(function (body) { | ||
body.sleep(); | ||
}); | ||
this.emit(_events.end, this); | ||
this._isSleeping = true; | ||
}; | ||
PhysicsEngine.prototype.wake = function wake() { | ||
if (!this._isSleeping) | ||
return; | ||
this._prevTime = now(); | ||
this.emit(_events.start, this); | ||
this._isSleeping = false; | ||
}; | ||
PhysicsEngine.prototype.emit = function emit(type, data) { | ||
if (this._eventHandler === null) | ||
return; | ||
this._eventHandler.emit(type, data); | ||
}; | ||
PhysicsEngine.prototype.on = function on(event, fn) { | ||
if (this._eventHandler === null) | ||
this._eventHandler = new EventHandler(); | ||
this._eventHandler.on(event, fn); | ||
}; | ||
module.exports = PhysicsEngine; | ||
module.exports = PhysicsEngine; |
133
README.md
@@ -1,67 +0,73 @@ | ||
Famo.us | ||
======= | ||
Famous Engine | ||
================= | ||
[![Build Status](https://travis-ci.org/Famous/famous.svg?branch=master)](https://travis-ci.org/Famous/famous) [![Dependency Status](https://david-dm.org/Famous/famous.svg)](https://david-dm.org/Famous/famous) [![devDependency Status](https://david-dm.org/Famous/famous/dev-status.svg)](https://david-dm.org/Famous/famous#info=devDependencies) | ||
[![Build Status](https://travis-ci.org/Famous/engine.svg?branch=master)](https://travis-ci.org/Famous/engine) [![Dependency Status](https://david-dm.org/Famous/engine.svg)](https://david-dm.org/Famous/engine) [![devDependency Status](https://david-dm.org/Famous/engine/dev-status.svg)](https://david-dm.org/Famous/engine#info=devDependencies) | ||
Welcome to the Famo.us GitHub repo. If you are interested in evaluating Famo.us, we are now in open beta. | ||
The Famous Engine is a free and open source JavaScript rendering engine. What makes the Famous Engine unique is its JavaScript rendering engine and 3D physics engine that gives developers the power and tools to build native quality apps and animations using pure JavaScript. It is designed to allow developers the ability to render to both DOM and WebGL in a unified API. | ||
| RESOURCE | LINK | | ||
|------------|---------| | ||
| **DOWNLOAD** | [Famo.us Starter Kit][starter-kit] | | ||
| **LEARN** | [Famo.us University][famous-university] | | ||
| **DOCS** | [Documentation][famous-docs] | | ||
| **HELP** | [IRC Channel][IRC] | | ||
| **ANGULAR INTEGRATION** | [Famo.us/Angular][famous-angular] | | ||
| **ANGULAR DOWNLOAD** | [Angular Starter Kit][famous-angular-starter-kit] | | ||
## Getting Started | ||
## About | ||
### Boilerplate | ||
Famo.us is a free and open source JavaScript platform for building mobile apps and desktop experiences. What makes Famo.us unique is its JavaScript rendering engine and 3D physics engine that gives developers the power and tools to build native quality apps and animations using pure JavaScript. Famo.us runs on iOS, Android, Kindle and Firefox devices and integrates with [Angular][famous-angular], Backbone, Meteor and Facebook React. | ||
If you have the Famous Engine included in your project, it is very easy to start getting content rendered to the screen. Below is a short example of how to get HTML content written to the screen. | ||
## Getting Started | ||
```js | ||
var FamousEngine = require('famous/core/FamousEngine'); | ||
var DOMElement = require('famous/dom-renderables/DOMElement'); | ||
###Famo.us University | ||
FamousEngine.init(); | ||
var scene = FamousEngine.createScene(); | ||
[Famo.us University][famous-university] is a free live coding classroom that teaches all levels of developers how to utilize Famo.us to build beautiful experiences on every screen. If you are new to Famo.us or to coding you will find that [Famo.us University][famous-university] is the best place to get started. | ||
var node = scene.addChild(); | ||
var domEl = new DOMElement(node, { | ||
content: 'Hello World', | ||
properties: { | ||
fontFamily: 'Arial' | ||
} | ||
}); | ||
``` | ||
### Seed Projects | ||
In this example, we use the Famous Engine to kick off the rendering process and create a scene for our application. From here, we can add nodes to our scene and use components to give them the ability to draw. | ||
There are a number of seed projects to get you started with the various ways of consuming Famo.us | ||
### Famous CLI | ||
**[Global Seed][global-seed]** | ||
The Famous CLI (Command Line Interface), allows for an easy installation of the Famous seed project which will bootstrap you with a small project containing the Famous Engine. Check out the README in the [famous-cli repository][cli-repo] on Github to learn how to install and create an account with the CLI. | ||
This project shows the easiest way to get started with Famo.us, using a version that loads on the global (window) object. This project requires zero tooling, and is ready to work out of the box. For speed and simplicity this seed project points at a version of Famo.us that lives on our CDN (content delivery network). You don't even have to do a git clone, you can get started right away simply by [downloading the zip][global-seed-download]. | ||
To get a new project, run the following commands: | ||
**[Requirejs Seed][requirejs-seed]** | ||
```sh | ||
famous create | ||
famous create <seed-project-name> | ||
``` | ||
This project can be used to get you started using Famo.us with the AMD module loading pattern via [RequireJS][requirejs]. This project is created using the latest version of our Yeoman Generator (see below), and includes an entire tooling stack neccessary to bring your product to development. | ||
From here, you can run your project to see a Famous application in action. | ||
**[Browserify Seed][browserify-seed]** | ||
```sh | ||
famous develop | ||
``` | ||
This project can be used to get you started using Famo.us with the CommonJS module loading pattern with [Browserify][browserify] and [npm][npm]. This seed project should be treated as experimental, it is for those who want to try and develop client side code using npm. | ||
### Seed Project | ||
### Yeoman Generator (*Grunt Toolbelt*) | ||
If you are looking for an easy way to get a Famous application up and running, check out our [seed project][famous-engine-seed]. This includes the FamousEngine, index.html file, preloaded CSS with friendly default values, and some boilerplate to get you started. | ||
If you would like to scaffold an app with Famo.us from the command line, install our [yeoman generator][github-generator] via npm. | ||
### npm | ||
npm install -g yo grunt-cli bower generator-famous | ||
mkdir newProject | ||
cd newProject | ||
yo famous | ||
grunt serve | ||
The Famous Engine is also available on npm. | ||
Preparing your project for distribution is then as simple as: | ||
``` | ||
npm install famous | ||
``` | ||
grunt | ||
This will add the Famous Engine to your node_modules folder to be included into your project. | ||
## Contributing | ||
Cloning this repository directly is primarily for those wishing to contribute to our codebase. Check out our [contributing instructions][contributing] to get involved. | ||
Note: cloning only provides the Famo.us folder with all Famo.us code, but it does no application scaffolding. You will additionally need to create your own index.html, and include the `famous.css` file that is included in `famous/core`. Require.js is currently a hard dependency to work off of the Famo.us head. | ||
Cloning this repository directly is primarily for those wishing to contribute to our codebase. Check out our [contributing instructions][contributing] to get involved. | ||
Note: cloning only provides the Famo.us folder with all Famo.us code, but it does no application scaffolding. You will additionally need to create your own index.html. Currently we have a dependency on glslify, a browserify transform to compile our glsl shaders. | ||
## Documentation | ||
- Guides and Examples can be found in the repo. | ||
- Rendered versions of the source code reference documentation: [docs][site-docs]. | ||
- Guides and tutorials: [guides][site-guides] | ||
@@ -71,43 +77,22 @@ ## Community | ||
- If you would like to report a bug, please check the [issues][contributing-issues] section in our [contributing instructions][contributing]. | ||
- Join us in our IRC channel #famous at irc.freenode.net. Freenode maintains this [getting started guide][irc-getting-started] for those new to IRC. | ||
- Please join us on the "famous-community" slack. | ||
- http://slack.famous.org/signup to sign up | ||
- http://slack.famous.org/ to log in | ||
- For contributors, read more instructions in [CONTRIBUTING.md][contributing-issues]. | ||
## Licensing information | ||
- Famo.us' client-side development package is licensed under the Mozilla public license version 2.0. More information can be found at [Mozilla][mpl]. | ||
- Mozilla also maintains an [MPL-2.0 FAQ][mpl-faq] that should answer most questions you may have about the license. | ||
- Contact license@famo.us for further inquiries. | ||
- The Famous rendering engine is licensed under the MIT license | ||
- Contact licensing@famo.us for further inquiries. | ||
Copyright (c) 2015 Famous Industries, Inc. | ||
[famous-site]: http://famo.us | ||
[starter-kit]: http://code.famo.us/famous-starter-kit/famous-starter-kit.zip?source=repo | ||
[famous-university]: https://famo.us/university | ||
[famous-help]: https://famo.us/help | ||
[famous-docs]: http://famo.us/docs | ||
[famous-angular]: http://famo.us/integrations/angular/ | ||
[famous-angular-starter-kit]: http://code.famo.us/famous-angular/latest/famous-angular-starter-kit.zip?source=repo | ||
[IRC]: http://webchat.freenode.net/?channels=famous | ||
[mpl]: http://www.mozilla.org/MPL/2.0/ | ||
[mpl-faq]: http://www.mozilla.org/MPL/2.0/FAQ.html | ||
[site-install]: http://famo.us/install | ||
[github-generator]: http://github.com/Famous/generator-famous.git | ||
[site-guides]: http://famo.us/guides | ||
[site-docs]: http://famo.us/docs | ||
[site-university]: http://famo.us/university | ||
[famous-site]: http://famous.org | ||
[famous-docs]: http://famous.org/docs | ||
[site-install]: http://famous.org/get-started.html | ||
[site-guides]: http://famous.org/learn | ||
[site-docs]: http://famous.org/ | ||
[cli-repo]: http://github.com/Famous/famous-cli | ||
[famous-organization-github]: http://github.com/Famous | ||
[github-examples]: http://github.com/Famous/examples | ||
[contributing]: https://github.com/Famous/famous/blob/master/CONTRIBUTING.md | ||
[contributing-issues]: https://github.com/Famous/famous/blob/master/CONTRIBUTING.md#issues | ||
[irc-getting-started]: http://freenode.net/using_the_network.shtml | ||
[esr-questions]: http://www.catb.org/esr/faqs/smart-questions.html | ||
[global-seed]: https://github.com/Famous/global-seed | ||
[global-seed-download]: https://github.com/Famous/global-seed/archive/master.zip | ||
[requirejs-seed]: https://github.com/Famous/requirejs-seed | ||
[browserify-seed]: https://github.com/Famous/browserify-seed/ | ||
[requirejs]: http://requirejs.org/ | ||
[famous-engine-seed]: http://github.com/Famous/engine-seed | ||
[contributing]: https://github.com/Famous/engine/blob/master/CONTRIBUTING.md | ||
[contributing-issues]: https://github.com/Famous/engine/blob/master/CONTRIBUTING.md#issues | ||
[browserify]: http://browserify.org/ | ||
[npm]: http://npmjs.org | ||
[![Analytics](https://ga-beacon.appspot.com/UA-34653957-5/famous/famous/README.md?pixel)](https://github.com/igrigorik/ga-beacon) |
@@ -0,11 +1,30 @@ | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
module.exports = { | ||
CachedMap: require('./CachedMap'), | ||
Easing: require('./Easing'), | ||
MultipleTransition: require('./MultipleTransition'), | ||
SnapTransition: require('./SnapTransition'), | ||
SpringTransition: require('./SpringTransition'), | ||
Transitionable: require('./Transitionable'), | ||
TransitionableTransform: require('./TransitionableTransform'), | ||
TweenTransition: require('./TweenTransition'), | ||
WallTransition: require('./WallTransition') | ||
Curves: require('./Curves'), | ||
Transitionable: require('./Transitionable') | ||
}; |
@@ -1,135 +0,456 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
var MultipleTransition = require('./MultipleTransition'); | ||
var TweenTransition = require('./TweenTransition'); | ||
function Transitionable(start) { | ||
this.currentAction = null; | ||
this.actionQueue = []; | ||
this.callbackQueue = []; | ||
this.state = 0; | ||
this.velocity = undefined; | ||
this._callback = undefined; | ||
this._engineInstance = null; | ||
this._currentMethod = null; | ||
this.set(start); | ||
'use strict'; | ||
var Curves = require('./Curves'); | ||
var FamousEngine = require('../core/FamousEngine'); | ||
/** | ||
* A state maintainer for a smooth transition between | ||
* numerically-specified states. Example numeric states include floats and | ||
* arrays of floats objects. | ||
* | ||
* An initial state is set with the constructor or using | ||
* {@link Transitionable#from}. Subsequent transitions consist of an | ||
* intermediate state, easing curve, duration and callback. The final state | ||
* of each transition is the initial state of the subsequent one. Calls to | ||
* {@link Transitionable#get} provide the interpolated state along the way. | ||
* | ||
* Note that there is no event loop here - calls to {@link Transitionable#get} | ||
* are the only way to find state projected to the current (or provided) | ||
* time and are the only way to trigger callbacks and mutate the internal | ||
* transition queue. | ||
* | ||
* @example | ||
* var t = new Transitionable([0, 0]); | ||
* t | ||
* .to([100, 0], 'linear', 1000) | ||
* .delay(1000) | ||
* .to([200, 0], 'outBounce', 1000); | ||
* | ||
* var div = document.createElement('div'); | ||
* div.style.background = 'blue'; | ||
* div.style.width = '100px'; | ||
* div.style.height = '100px'; | ||
* document.body.appendChild(div); | ||
* | ||
* div.addEventListener('click', function() { | ||
* t.isPaused() ? t.resume() : t.pause(); | ||
* }); | ||
* | ||
* requestAnimationFrame(function loop() { | ||
* div.style.transform = 'translateX(' + t.get()[0] + 'px)' + ' translateY(' + t.get()[1] + 'px)'; | ||
* requestAnimationFrame(loop); | ||
* }); | ||
* | ||
* @class Transitionable | ||
* @constructor | ||
* @param {Number|Array.Number} initialState initial state to transition | ||
* from - equivalent to a pursuant | ||
* invocation of | ||
* {@link Transitionable#from} | ||
*/ | ||
function Transitionable(initialState) { | ||
this._queue = []; | ||
this._from = null; | ||
this._state = null; | ||
this._startedAt = null; | ||
this._pausedAt = null; | ||
if (initialState != null) this.from(initialState); | ||
} | ||
var transitionMethods = {}; | ||
Transitionable.register = function register(methods) { | ||
var success = true; | ||
for (var method in methods) { | ||
if (!Transitionable.registerMethod(method, methods[method])) | ||
success = false; | ||
/** | ||
* Internal Clock used for determining the current time for the ongoing | ||
* transitions. | ||
* | ||
* @type {Performance|Date|Clock} | ||
*/ | ||
Transitionable.Clock = FamousEngine.getClock(); | ||
/** | ||
* Registers a transition to be pushed onto the internal queue. | ||
* | ||
* @method to | ||
* @chainable | ||
* | ||
* @param {Number|Array.Number} finalState final state to | ||
* transiton to | ||
* @param {String|Function} [curve=Curves.linear] easing function | ||
* used for | ||
* interpolating | ||
* [0, 1] | ||
* @param {Number} [duration=100] duration of | ||
* transition | ||
* @param {Function} [callback] callback function | ||
* to be called after | ||
* the transition is | ||
* complete | ||
* @param {String} [method] method used for | ||
* interpolation | ||
* (e.g. slerp) | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.to = function to(finalState, curve, duration, callback, method) { | ||
curve = curve != null && curve.constructor === String ? Curves[curve] : curve; | ||
if (this._queue.length === 0) { | ||
this._startedAt = this.constructor.Clock.now(); | ||
this._pausedAt = null; | ||
} | ||
return success; | ||
this._queue.push( | ||
finalState, | ||
curve != null ? curve : Curves.linear, | ||
duration != null ? duration : 100, | ||
callback, | ||
method | ||
); | ||
return this; | ||
}; | ||
Transitionable.registerMethod = function registerMethod(name, engineClass) { | ||
if (!(name in transitionMethods)) { | ||
transitionMethods[name] = engineClass; | ||
return true; | ||
} else | ||
return false; | ||
/** | ||
* Resets the transition queue to a stable initial state. | ||
* | ||
* @method from | ||
* @chainable | ||
* | ||
* @param {Number|Array.Number} initialState initial state to | ||
* transition from | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.from = function from(initialState) { | ||
this._state = initialState; | ||
this._from = this._sync(null, this._state); | ||
this._queue.length = 0; | ||
this._startedAt = this.constructor.Clock.now(); | ||
this._pausedAt = null; | ||
return this; | ||
}; | ||
Transitionable.unregisterMethod = function unregisterMethod(name) { | ||
if (name in transitionMethods) { | ||
delete transitionMethods[name]; | ||
return true; | ||
} else | ||
return false; | ||
/** | ||
* Delays the execution of the subsequent transition for a certain period of | ||
* time. | ||
* | ||
* @method delay | ||
* @chainable | ||
* | ||
* @param {Number} duration delay time in ms | ||
* @param {Function} [callback] Zero-argument function to call on observed | ||
* completion (t=1) | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.delay = function delay(duration, callback) { | ||
var endState = this._queue.length > 0 ? this._queue[this._queue.length - 5] : this._state; | ||
return this.to(endState, Curves.flat, duration, callback); | ||
}; | ||
function _loadNext() { | ||
if (this._callback) { | ||
var callback = this._callback; | ||
this._callback = undefined; | ||
callback(); | ||
/** | ||
* Overrides current transition. | ||
* | ||
* @method override | ||
* @chainable | ||
* | ||
* @param {Number|Array.Number} [finalState] final state to transiton to | ||
* @param {String|Function} [curve] easing function used for | ||
* interpolating [0, 1] | ||
* @param {Number} [duration] duration of transition | ||
* @param {Function} [callback] callback function to be | ||
* called after the transition | ||
* is complete | ||
* @param {String} [method] optional method used for | ||
* interpolating between the | ||
* values. Set to `slerp` for | ||
* spherical linear | ||
* interpolation. | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.override = function override(finalState, curve, duration, callback, method) { | ||
if (this._queue.length > 0) { | ||
if (finalState != null) this._queue[0] = finalState; | ||
if (curve != null) this._queue[1] = curve.constructor === String ? Curves[curve] : curve; | ||
if (duration != null) this._queue[2] = duration; | ||
if (callback != null) this._queue[3] = callback; | ||
if (method != null) this._queue[4] = method; | ||
} | ||
if (this.actionQueue.length <= 0) { | ||
this.set(this.get()); | ||
return; | ||
return this; | ||
}; | ||
/** | ||
* Used for interpolating between the start and end state of the currently | ||
* running transition | ||
* | ||
* @method _interpolate | ||
* @private | ||
* | ||
* @param {Object|Array|Number} output Where to write to (in order to avoid | ||
* object allocation and therefore GC). | ||
* @param {Object|Array|Number} from Start state of current transition. | ||
* @param {Object|Array|Number} to End state of current transition. | ||
* @param {Number} progress Progress of the current transition, | ||
* in [0, 1] | ||
* @param {String} method Method used for interpolation (e.g. | ||
* slerp) | ||
* @return {Object|Array|Number} output | ||
*/ | ||
Transitionable.prototype._interpolate = function _interpolate(output, from, to, progress, method) { | ||
if (to instanceof Object) { | ||
if (method === 'slerp') { | ||
var x, y, z, w; | ||
var qx, qy, qz, qw; | ||
var omega, cosomega, sinomega, scaleFrom, scaleTo; | ||
x = from[0]; | ||
y = from[1]; | ||
z = from[2]; | ||
w = from[3]; | ||
qx = to[0]; | ||
qy = to[1]; | ||
qz = to[2]; | ||
qw = to[3]; | ||
if (progress === 1) { | ||
output[0] = qx; | ||
output[1] = qy; | ||
output[2] = qz; | ||
output[3] = qw; | ||
return output; | ||
} | ||
cosomega = w * qw + x * qx + y * qy + z * qz; | ||
if ((1.0 - cosomega) > 1e-5) { | ||
omega = Math.acos(cosomega); | ||
sinomega = Math.sin(omega); | ||
scaleFrom = Math.sin((1.0 - progress) * omega) / sinomega; | ||
scaleTo = Math.sin(progress * omega) / sinomega; | ||
} | ||
else { | ||
scaleFrom = 1.0 - progress; | ||
scaleTo = progress; | ||
} | ||
output[0] = x * scaleFrom + qx * scaleTo; | ||
output[1] = y * scaleFrom + qy * scaleTo; | ||
output[2] = z * scaleFrom + qz * scaleTo; | ||
output[3] = w * scaleFrom + qw * scaleTo; | ||
} | ||
else if (to instanceof Array) { | ||
for (var i = 0, len = to.length; i < len; i++) { | ||
output[i] = this._interpolate(output[i], from[i], to[i], progress, method); | ||
} | ||
} | ||
else { | ||
for (var key in to) { | ||
output[key] = this._interpolate(output[key], from[key], to[key], progress, method); | ||
} | ||
} | ||
} | ||
this.currentAction = this.actionQueue.shift(); | ||
this._callback = this.callbackQueue.shift(); | ||
var method = null; | ||
var endValue = this.currentAction[0]; | ||
var transition = this.currentAction[1]; | ||
if (transition instanceof Object && transition.method) { | ||
method = transition.method; | ||
if (typeof method === 'string') | ||
method = transitionMethods[method]; | ||
} else { | ||
method = TweenTransition; | ||
else { | ||
output = from + progress * (to - from); | ||
} | ||
if (this._currentMethod !== method) { | ||
if (!(endValue instanceof Object) || method.SUPPORTS_MULTIPLE === true || endValue.length <= method.SUPPORTS_MULTIPLE) { | ||
this._engineInstance = new method(); | ||
} else { | ||
this._engineInstance = new MultipleTransition(method); | ||
return output; | ||
}; | ||
/** | ||
* Internal helper method used for synchronizing the current, absolute state of | ||
* a transition to a given output array, object literal or number. Supports | ||
* nested state objects by through recursion. | ||
* | ||
* @method _sync | ||
* @private | ||
* | ||
* @param {Number|Array|Object} output Where to write to (in order to avoid | ||
* object allocation and therefore GC). | ||
* @param {Number|Array|Object} input Input state to proxy onto the | ||
* output. | ||
* @return {Number|Array|Object} output Passed in output object. | ||
*/ | ||
Transitionable.prototype._sync = function _sync(output, input) { | ||
if (typeof input === 'number') output = input; | ||
else if (input instanceof Array) { | ||
if (output == null) output = []; | ||
for (var i = 0, len = input.length; i < len; i++) { | ||
output[i] = _sync(output[i], input[i]); | ||
} | ||
this._currentMethod = method; | ||
} | ||
this._engineInstance.reset(this.state, this.velocity); | ||
if (this.velocity !== undefined) | ||
transition.velocity = this.velocity; | ||
this._engineInstance.set(endValue, transition, _loadNext.bind(this)); | ||
} | ||
Transitionable.prototype.set = function set(endState, transition, callback) { | ||
if (!transition) { | ||
this.reset(endState); | ||
if (callback) | ||
callback(); | ||
return this; | ||
else if (input instanceof Object) { | ||
if (output == null) output = {}; | ||
for (var key in input) { | ||
output[key] = _sync(output[key], input[key]); | ||
} | ||
} | ||
var action = [ | ||
endState, | ||
transition | ||
]; | ||
this.actionQueue.push(action); | ||
this.callbackQueue.push(callback); | ||
if (!this.currentAction) | ||
_loadNext.call(this); | ||
return this; | ||
return output; | ||
}; | ||
Transitionable.prototype.reset = function reset(startState, startVelocity) { | ||
this._currentMethod = null; | ||
this._engineInstance = null; | ||
this._callback = undefined; | ||
this.state = startState; | ||
this.velocity = startVelocity; | ||
this.currentAction = null; | ||
this.actionQueue = []; | ||
this.callbackQueue = []; | ||
}; | ||
Transitionable.prototype.delay = function delay(duration, callback) { | ||
var endValue; | ||
if (this.actionQueue.length) | ||
endValue = this.actionQueue[this.actionQueue.length - 1][0]; | ||
else if (this.currentAction) | ||
endValue = this.currentAction[0]; | ||
else | ||
endValue = this.get(); | ||
return this.set(endValue, { | ||
duration: duration, | ||
curve: function () { | ||
return 0; | ||
} | ||
}, callback); | ||
}; | ||
Transitionable.prototype.get = function get(timestamp) { | ||
if (this._engineInstance) { | ||
if (this._engineInstance.getVelocity) | ||
this.velocity = this._engineInstance.getVelocity(); | ||
this.state = this._engineInstance.get(timestamp); | ||
/** | ||
* Get interpolated state of current action at provided time. If the last | ||
* action has completed, invoke its callback. | ||
* | ||
* @method get | ||
* | ||
* @param {Number=} t Evaluate the curve at a normalized version | ||
* of this time. If omitted, use current time | ||
* (Unix epoch time retrieved from Clock). | ||
* @return {Number|Array.Number} Beginning state interpolated to this point | ||
* in time. | ||
*/ | ||
Transitionable.prototype.get = function get(t) { | ||
if (this._queue.length === 0) return this._state; | ||
t = this._pausedAt ? this._pausedAt : t; | ||
t = t ? t : this.constructor.Clock.now(); | ||
var progress = (t - this._startedAt) / this._queue[2]; | ||
this._state = this._interpolate( | ||
this._state, | ||
this._from, | ||
this._queue[0], | ||
this._queue[1](progress > 1 ? 1 : progress), | ||
this._queue[4] | ||
); | ||
var state = this._state; | ||
if (progress >= 1) { | ||
this._startedAt = this._startedAt + this._queue[2]; | ||
this._from = this._sync(this._from, this._state); | ||
this._queue.shift(); | ||
this._queue.shift(); | ||
this._queue.shift(); | ||
var callback = this._queue.shift(); | ||
this._queue.shift(); | ||
if (callback) callback(); | ||
} | ||
return this.state; | ||
return progress > 1 ? this.get() : state; | ||
}; | ||
/** | ||
* Is there at least one transition pending completion? | ||
* | ||
* @method isActive | ||
* | ||
* @return {Boolean} Boolean indicating whether there is at least one pending | ||
* transition. Paused transitions are still being | ||
* considered active. | ||
*/ | ||
Transitionable.prototype.isActive = function isActive() { | ||
return !!this.currentAction; | ||
return this._queue.length > 0; | ||
}; | ||
/** | ||
* Halt transition at current state and erase all pending actions. | ||
* | ||
* @method halt | ||
* @chainable | ||
* | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.halt = function halt() { | ||
return this.set(this.get()); | ||
return this.from(this.get()); | ||
}; | ||
module.exports = Transitionable; | ||
/** | ||
* Pause transition. This will not erase any actions. | ||
* | ||
* @method pause | ||
* @chainable | ||
* | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.pause = function pause() { | ||
this._pausedAt = this.constructor.Clock.now(); | ||
return this; | ||
}; | ||
/** | ||
* Has the current action been paused? | ||
* | ||
* @method isPaused | ||
* @chainable | ||
* | ||
* @return {Boolean} if the current action has been paused | ||
*/ | ||
Transitionable.prototype.isPaused = function isPaused() { | ||
return !!this._pausedAt; | ||
}; | ||
/** | ||
* Resume a previously paused transition. | ||
* | ||
* @method resume | ||
* @chainable | ||
* | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.resume = function resume() { | ||
var diff = this._pausedAt - this._startedAt; | ||
this._startedAt = this.constructor.Clock.now() - diff; | ||
this._pausedAt = null; | ||
return this; | ||
}; | ||
/** | ||
* Cancel all transitions and reset to a stable state | ||
* | ||
* @method reset | ||
* @chainable | ||
* @deprecated Use `.from` instead! | ||
* | ||
* @param {Number|Array.Number|Object.<number, number>} start | ||
* stable state to set to | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.reset = function(start) { | ||
return this.from(start); | ||
}; | ||
/** | ||
* Add transition to end state to the queue of pending transitions. Special | ||
* Use: calling without a transition resets the object to that state with | ||
* no pending actions | ||
* | ||
* @method set | ||
* @chainable | ||
* @deprecated Use `.to` instead! | ||
* | ||
* @param {Number|FamousEngineMatrix|Array.Number|Object.<number, number>} state | ||
* end state to which we interpolate | ||
* @param {transition=} transition object of type {duration: number, curve: | ||
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be | ||
* instantaneous. | ||
* @param {function()=} callback Zero-argument function to call on observed | ||
* completion (t=1) | ||
* @return {Transitionable} this | ||
*/ | ||
Transitionable.prototype.set = function(state, transition, callback) { | ||
if (transition == null) { | ||
this.from(state); | ||
if (callback) callback(); | ||
} | ||
else { | ||
this.to(state, transition.curve, transition.duration, callback, transition.method); | ||
} | ||
return this; | ||
}; | ||
module.exports = Transitionable; |
@@ -0,5 +1,39 @@ | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
module.exports = { | ||
KeyCodes: require('./KeyCodes'), | ||
Timer: require('./Timer'), | ||
Utility: require('./Utility') | ||
CallbackStore: require('./CallbackStore'), | ||
clamp: require('./clamp'), | ||
clone: require('./clone'), | ||
Color: require('./Color'), | ||
KeyCodes: require('./KeyCodes'), | ||
keyValueToArrays: require('./keyValueToArrays'), | ||
loadURL: require('./loadURL'), | ||
ObjectManager: require('./ObjectManager'), | ||
strip: require('./strip'), | ||
vendorPrefix: require('./vendorPrefix') | ||
}; | ||
@@ -1,9 +0,34 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
/** | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Famous Industries Inc. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
'use strict'; | ||
/** | ||
* Collection to map keyboard codes in plain english | ||
* | ||
* @license MPL 2.0 | ||
* @copyright Famous Industries, Inc. 2015 | ||
* @class KeyCodes | ||
* @static | ||
*/ | ||
var KeyCodes = { | ||
module.exports = { | ||
0: 48, | ||
@@ -71,3 +96,3 @@ 1: 49, | ||
Z: 90, | ||
ENTER: 13, | ||
ENTER : 13, | ||
LEFT_ARROW: 37, | ||
@@ -81,2 +106,2 @@ RIGHT_ARROW: 39, | ||
}; | ||
module.exports = KeyCodes; | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Misc. License Issues
License(Experimental) A package's licensing information has fine-grained problems.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
1248719
1
233
0
100
33308
6
1
98
+ Addedglslify@^1.6.0
+ Addedabbrev@1.1.1(transitive)
+ Addedamdefine@1.0.1(transitive)
+ Addedast-types@0.3.38(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedcls@0.1.5(transitive)
+ Addedcommondir@0.0.1(transitive)
+ Addedconcat-stream@1.6.2(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addedcssauron@0.0.2(transitive)
+ Addedcssauron-glsl@0.0.0(transitive)
+ Addeddup@1.0.0(transitive)
+ Addedduplexer@0.0.4(transitive)
+ Addedemit-function@0.0.2(transitive)
+ Addedgl-shader-core@2.2.0(transitive)
+ Addedglsl-deparser@0.0.2(transitive)
+ Addedglsl-extract@0.0.2(transitive)
+ Addedglsl-min-stream@0.0.2(transitive)
+ Addedglsl-parser@0.0.51.0.1(transitive)
+ Addedglsl-resolve@0.0.1(transitive)
+ Addedglsl-tokenizer@0.0.81.1.1(transitive)
+ Addedglslify@1.6.1(transitive)
+ Addedglslify-stream@0.4.1(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedis-require@0.0.1(transitive)
+ Addedisarray@0.0.11.0.0(transitive)
+ Addednew-from@0.0.3(transitive)
+ Addednopt@2.2.1(transitive)
+ Addedprivate@0.1.8(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedreadable-stream@1.0.341.1.142.3.8(transitive)
+ Addedrecast@0.5.27(transitive)
+ Addedreplace-method@0.0.0(transitive)
+ Addedresolve@0.5.10.6.3(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedshortest@0.0.0(transitive)
+ Addedsleuth@0.1.1(transitive)
+ Addedsource-map@0.1.32(transitive)
+ Addedstatic-eval@0.1.10.2.4(transitive)
+ Addedstream-combiner@0.0.2(transitive)
+ Addedstring_decoder@0.10.311.1.1(transitive)
+ Addedthrough@1.1.22.3.4(transitive)
+ Addedtypedarray@0.0.6(transitive)
+ Addedutf8-stream@0.0.0(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwrap-stream@0.0.0(transitive)
+ Addedxtend@2.2.0(transitive)
- Removedcssify@^0.6.0
- Removeddeamdify@^0.1.1
- Removedcssify@0.6.0(transitive)
- Removeddeamdify@0.1.1(transitive)
- Removedestraverse@1.9.3(transitive)
- Removedsource-map@0.7.4(transitive)