Commit 6be7524b authored by rockyl's avatar rockyl

init

parents
Pipeline #129304 failed with stages
in 0 seconds
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
build
examples
node_modules
Gruntfile.js
README.md
src/p2.js
src/math/polyk.js
src/wrapper
\ No newline at end of file
{
"node" : true,
"browser" : true,
"boss" : false,
"curly": true,
"debug": false,
"devel": false,
"eqeqeq": true,
"eqnull": true,
"evil": false,
"forin": false,
"immed": true,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"plusplus": false,
"regexp": false,
"smarttabs": true,
"sub": true,
"strict": false,
"trailing" : true,
"undef": true,
"indent": 4,
"shadow" : true,
"laxcomma": true,
"globals": {
"p2": true
}
}
\ No newline at end of file
# Temp files
*~
*#
# Node Modules
node_modules/*
# File system
.DS_Store
# Sublime Text
*.sublime-project
*.sublime-workspace
var fs = require('fs');
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
browserify : {
core : {
src : ["src/p2.js"],
dest : 'build/p2.js',
options:{
bundleOptions : {
standalone : "p2"
}
}
}
},
uglify : {
build: {
src : ['build/p2.js'],
dest : 'build/p2.min.js'
},
demo: {
src : ['build/p2.renderer.js'],
dest : 'build/p2.renderer.min.js'
}
},
nodeunit: {
all: ['test/**/*.js'],
},
jshint: {
all: ['src/**/*.js'],
options:{
jshintrc: '.jshintrc',
force: true // Do not fail the task
}
},
watch: {
options: {
nospawn: false
},
source: {
files: 'src/**/*',
tasks: [
'default'
]
},
renderer: {
files: 'demos/js/*Renderer.js',
tasks: [
'concat:renderer'
]
},
test: {
files: ['src/**/*', 'test/**/*'],
tasks: [
'test'
]
},
},
concat: {
renderer: {
src: ['demos/js/pixi.js', 'demos/js/dat.gui.js', 'demos/js/Renderer.js', 'demos/js/WebGLRenderer.js'],
dest: 'build/p2.renderer.js',
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-nodeunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['test','jshint','browserify','concat','uglify','addLicense','requireJsFix']);
grunt.registerTask('test', ['nodeunit']);
// Not sure what flag Browserify needs to do this. Fixing it manually for now.
grunt.registerTask('requireJsFix','Modifies the browserify bundle so it works with RequireJS',function(){
['build/p2.js', 'build/p2.min.js'].forEach(function (path){
var text = fs.readFileSync(path).toString();
text = text.replace('define.amd', 'false'); // This makes the bundle skip using define() from RequireJS
fs.writeFileSync(path, text);
});
});
grunt.registerTask('addLicense','Adds the LICENSE to the top of the built files',function(){
var text = fs.readFileSync("LICENSE").toString();
var dev = fs.readFileSync("build/p2.js").toString();
var min = fs.readFileSync("build/p2.min.js").toString();
fs.writeFileSync("build/p2.js",text+"\n"+dev);
fs.writeFileSync("build/p2.min.js",text+"\n"+min);
});
};
/**
* The MIT License (MIT)
*
* Copyright (c) 2015 p2.js authors
*
* 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.
*/
\ No newline at end of file
p2.js
=====
2D rigid body physics engine written in JavaScript. Includes collision detection, contacts, friction, restitution, motors, springs, advanced constraints and various shape types.
[Demos](#demos) | [Examples](#examples) | [Documentation](http://schteppe.github.io/p2.js/docs/) | [Download](https://raw.github.com/schteppe/p2.js/master/build/p2.js) | [CDN](http://cdnjs.com/libraries/p2.js) | [Wiki](https://github.com/schteppe/p2.js/wiki)
### Featured projects using p2.js
* [Google I/O 2015 Experiment](http://www.chromeexperiments.com/detail/io-2015-experiment) by Instrument
* [PixiLights, a Christmas Experiment](http://christmasexperiments.com/experiments/11) by Mat Groves
* [More...](https://github.com/schteppe/p2.js/wiki/Projects-using-p2.js)
### Demos
These demos use the p2 Demo framework, which provides rendering and interactivity. Use mouse/touch to throw or create objects. Use the right menu (or console!) to tweak parameters. Or just check the source to see how to programmatically build the current scene using p2.
* [Buoyancy](http://schteppe.github.io/p2.js/demos/buoyancy.html)
* [Car](http://schteppe.github.io/p2.js/demos/car.html)
* [CCD](http://schteppe.github.io/p2.js/demos/ccd.html)
* [Circle container](http://schteppe.github.io/p2.js/demos/circles.html)
* [Collision tests](http://schteppe.github.io/p2.js/demos/collisions.html)
* [Compound objects](http://schteppe.github.io/p2.js/demos/compound.html)
* [Concave objects](http://schteppe.github.io/p2.js/demos/concave.html)
* [Constraints](http://schteppe.github.io/p2.js/demos/constraints.html)
* [DistanceConstraint](http://schteppe.github.io/p2.js/demos/distanceConstraint.html)
* [Fixed rotation](http://schteppe.github.io/p2.js/demos/fixedRotation.html)
* [Fixed XY](http://schteppe.github.io/p2.js/demos/fixedXY.html)
* [Friction](http://schteppe.github.io/p2.js/demos/friction.html)
* [Gear constraint](http://schteppe.github.io/p2.js/demos/gearConstraint.html)
* [Heightfield](http://schteppe.github.io/p2.js/demos/heightfield.html)
* [Island solver](http://schteppe.github.io/p2.js/demos/islandSolver.html)
* [Kinematic body](http://schteppe.github.io/p2.js/demos/kinematic.html)
* [Lock constraint](http://schteppe.github.io/p2.js/demos/lock.html)
* [Piston](http://schteppe.github.io/p2.js/demos/piston.html)
* [Prismatic constraint](http://schteppe.github.io/p2.js/demos/prismatic.html)
* [Ragdoll](http://schteppe.github.io/p2.js/demos/ragdoll.html)
* [Sensor](http://schteppe.github.io/p2.js/demos/removeSensor.html)
* [Restitution](http://schteppe.github.io/p2.js/demos/restitution.html)
* [Sleep](http://schteppe.github.io/p2.js/demos/sleep.html)
* [Segway](http://schteppe.github.io/p2.js/demos/segway.html)
* [Sleep](http://schteppe.github.io/p2.js/demos/sleep.html)
* [Springs](http://schteppe.github.io/p2.js/demos/springs.html)
* [Surface velocity](http://schteppe.github.io/p2.js/demos/surfaceVelocity.html)
* [Suspension](http://schteppe.github.io/p2.js/demos/suspension.html)
* [Tearable constraints](http://schteppe.github.io/p2.js/demos/tearable.html)
* [TopDownVehicle](http://schteppe.github.io/p2.js/demos/topDownVehicle.html)
### Examples
Examples showing how to use p2.js with your favorite renderer.
* [Canvas: Asteroids game](http://schteppe.github.io/p2.js/examples/canvas/asteroids.html)
* [Canvas: Box on plane](http://schteppe.github.io/p2.js/examples/canvas/box.html)
* [Canvas: Character demo](http://schteppe.github.io/p2.js/examples/canvas/character.html)
* [Canvas: Circle on plane](http://schteppe.github.io/p2.js/examples/canvas/circle.html)
* [Canvas: Interpolation](http://schteppe.github.io/p2.js/examples/canvas/interpolation.html)
* [Canvas: Mousejoint](http://schteppe.github.io/p2.js/examples/canvas/mouseJoint.html)
* [Canvas: Raycasting](http://schteppe.github.io/p2.js/examples/canvas/raycasting.html)
* [Canvas: Rayreflect](http://schteppe.github.io/p2.js/examples/canvas/rayreflect.html)
* [Canvas: Sensors](http://schteppe.github.io/p2.js/examples/canvas/sensors.html)
* [Canvas: Sensors 2](http://schteppe.github.io/p2.js/examples/canvas/sensors2.html)
* [Pixi.js: Box on plane](http://schteppe.github.io/p2.js/examples/pixijs/box.html)
### Sample code
The following example uses the [World](http://schteppe.github.io/p2.js/docs/classes/World.html), [Circle](http://schteppe.github.io/p2.js/docs/classes/Circle.html), [Body](http://schteppe.github.io/p2.js/docs/classes/Body.html) and [Plane](http://schteppe.github.io/p2.js/docs/classes/Plane.html) classes to set up a simple physics scene with a ball on a plane.
```js
// Create a physics world, where bodies and constraints live
var world = new p2.World({
gravity:[0, -9.82]
});
// Create an empty dynamic body
var circleBody = new p2.Body({
mass: 5,
position: [0, 10]
});
// Add a circle shape to the body.
var circleShape = new p2.Circle({ radius: 1 });
circleBody.addShape(circleShape);
// ...and add the body to the world.
// If we don't add it to the world, it won't be simulated.
world.addBody(circleBody);
// Create an infinite ground plane.
var groundBody = new p2.Body({
mass: 0 // Setting mass to 0 makes the body static
});
var groundShape = new p2.Plane();
groundBody.addShape(groundShape);
world.addBody(groundBody);
// To get the trajectories of the bodies,
// we must step the world forward in time.
// This is done using a fixed time step size.
var timeStep = 1 / 60; // seconds
// The "Game loop". Could be replaced by, for example, requestAnimationFrame.
setInterval(function(){
// The step method moves the bodies forward in time.
world.step(timeStep);
// Print the circle position to console.
// Could be replaced by a render call.
console.log("Circle y position: " + circleBody.position[1]);
}, 1000 * timeStep);
```
### Install
##### Browser
Download either [p2.js](build/p2.js) or the minified [p2.min.js](build/p2.min.js) and include the script in your HTML:
```html
<script src="p2.js" type="text/javascript"></script>
```
If you would like to use ordinary ```Array``` instead of ```Float32Array```, define ```P2_ARRAY_TYPE``` globally before loading the library.
```html
<script type="text/javascript">P2_ARRAY_TYPE = Array;</script>
<script src="p2.js" type="text/javascript"></script>
```
##### Node.js
```
npm install p2
```
Then require it like so:
```js
var p2 = require('p2');
```
### Supported collision pairs
| | Circle | Plane | Box | Convex | Particle | Line | Capsule | Heightfield | Ray |
| :--------------------------------------------------------------------------: |:------:|:-----:|:---------:|:------:|:--------:|:------:|:-------:|:-----------:|:------:|
| [Circle](http://schteppe.github.io/p2.js/docs/classes/Circle.html) | Yes | - | - | - | - | - | - | - | - |
| [Plane](http://schteppe.github.io/p2.js/docs/classes/Plane.html) | Yes | - | - | - | - | - | - | - | - |
| [Box](http://schteppe.github.io/p2.js/docs/classes/Box.html) | Yes | Yes | Yes | - | - | - | - | - | - |
| [Convex](http://schteppe.github.io/p2.js/docs/classes/Convex.html) | Yes | Yes | Yes | Yes | - | - | - | - | - |
| [Particle](http://schteppe.github.io/p2.js/docs/classes/Particle.html) | Yes | Yes | Yes | Yes | - | - | - | - | - |
| [Line](http://schteppe.github.io/p2.js/docs/classes/Line.html) | Yes | Yes | (todo) | (todo) | - | - | - | - | - |
| [Capsule](http://schteppe.github.io/p2.js/docs/classes/Capsule.html) | Yes | Yes | Yes | Yes | Yes | (todo) | Yes | - | - |
| [Heightfield](http://schteppe.github.io/p2.js/docs/classes/Heightfield.html) | Yes | - | Yes | Yes | (todo) | (todo) | (todo) | - | - |
| [Ray](http://schteppe.github.io/p2.js/docs/classes/Ray.html) | Yes | Yes | Yes | Yes | - | Yes | Yes | Yes | - |
Note that concave polygon shapes can be created using [Body.fromPolygon](http://schteppe.github.io/p2.js/docs/classes/Body.html#method_fromPolygon).
### Unit testing
Tests are written for [Nodeunit](https://github.com/caolan/nodeunit). Run the tests with the command ```grunt test```.
### Contribute
Make sure you have git, [Node.js](http://nodejs.org), NPM and [grunt](http://gruntjs.com/) installed.
```
git clone https://github.com/schteppe/p2.js.git; # Clone the repo
cd p2.js;
npm install; # Install dependencies
# (make changes to source)
grunt; # Builds build/p2.js and build/p2.min.js
```
The most recent commits are currently pushed to the ```master``` branch. Thanks for contributing!
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
{
"name": "p2",
"version": "0.7.1",
"description": "A JavaScript 2D physics engine.",
"author": "Stefan Hedman <schteppe@gmail.com> (http://steffe.se)",
"keywords": [
"p2.js",
"p2",
"physics",
"engine",
"2d"
],
"main": "./build/p2.js",
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/schteppe/p2.js.git"
},
"bugs": {
"url": "https://github.com/schteppe/p2.js/issues"
},
"licenses": [
{
"type": "MIT"
}
],
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-jshint": "^0.11.2",
"grunt-contrib-nodeunit": "^0.4.1",
"grunt-contrib-uglify": "~0.4.0",
"grunt-contrib-watch": "~0.5.0",
"grunt-browserify": "~2.0.1",
"grunt-contrib-concat": "^0.4.0"
},
"dependencies": {
"poly-decomp": "0.1.1"
}
}
var vec2 = require('../math/vec2')
, Utils = require('../utils/Utils');
module.exports = AABB;
/**
* Axis aligned bounding box class.
* @class AABB
* @constructor
* @param {Object} [options]
* @param {Array} [options.upperBound]
* @param {Array} [options.lowerBound]
*/
function AABB(options){
/**
* The lower bound of the bounding box.
* @property lowerBound
* @type {Array}
*/
this.lowerBound = vec2.create();
if(options && options.lowerBound){
vec2.copy(this.lowerBound, options.lowerBound);
}
/**
* The upper bound of the bounding box.
* @property upperBound
* @type {Array}
*/
this.upperBound = vec2.create();
if(options && options.upperBound){
vec2.copy(this.upperBound, options.upperBound);
}
}
var tmp = vec2.create();
/**
* Set the AABB bounds from a set of points, transformed by the given position and angle.
* @method setFromPoints
* @param {Array} points An array of vec2's.
* @param {Array} position
* @param {number} angle
* @param {number} skinSize Some margin to be added to the AABB.
*/
AABB.prototype.setFromPoints = function(points, position, angle, skinSize){
var l = this.lowerBound,
u = this.upperBound;
if(typeof(angle) !== "number"){
angle = 0;
}
// Set to the first point
if(angle !== 0){
vec2.rotate(l, points[0], angle);
} else {
vec2.copy(l, points[0]);
}
vec2.copy(u, l);
// Compute cosines and sines just once
var cosAngle = Math.cos(angle),
sinAngle = Math.sin(angle);
for(var i = 1; i<points.length; i++){
var p = points[i];
if(angle !== 0){
var x = p[0],
y = p[1];
tmp[0] = cosAngle * x -sinAngle * y;
tmp[1] = sinAngle * x +cosAngle * y;
p = tmp;
}
for(var j=0; j<2; j++){
if(p[j] > u[j]){
u[j] = p[j];
}
if(p[j] < l[j]){
l[j] = p[j];
}
}
}
// Add offset
if(position){
vec2.add(this.lowerBound, this.lowerBound, position);
vec2.add(this.upperBound, this.upperBound, position);
}
if(skinSize){
this.lowerBound[0] -= skinSize;
this.lowerBound[1] -= skinSize;
this.upperBound[0] += skinSize;
this.upperBound[1] += skinSize;
}
};
/**
* Copy bounds from an AABB to this AABB
* @method copy
* @param {AABB} aabb
*/
AABB.prototype.copy = function(aabb){
vec2.copy(this.lowerBound, aabb.lowerBound);
vec2.copy(this.upperBound, aabb.upperBound);
};
/**
* Extend this AABB so that it covers the given AABB too.
* @method extend
* @param {AABB} aabb
*/
AABB.prototype.extend = function(aabb){
// Loop over x and y
var i = 2;
while(i--){
// Extend lower bound
var l = aabb.lowerBound[i];
if(this.lowerBound[i] > l){
this.lowerBound[i] = l;
}
// Upper
var u = aabb.upperBound[i];
if(this.upperBound[i] < u){
this.upperBound[i] = u;
}
}
};
/**
* Returns true if the given AABB overlaps this AABB.
* @method overlaps
* @param {AABB} aabb
* @return {Boolean}
*/
AABB.prototype.overlaps = function(aabb){
var l1 = this.lowerBound,
u1 = this.upperBound,
l2 = aabb.lowerBound,
u2 = aabb.upperBound;
// l2 u2
// |---------|
// |--------|
// l1 u1
return ((l2[0] <= u1[0] && u1[0] <= u2[0]) || (l1[0] <= u2[0] && u2[0] <= u1[0])) &&
((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1]));
};
/**
* @method containsPoint
* @param {Array} point
* @return {boolean}
*/
AABB.prototype.containsPoint = function(point){
var l = this.lowerBound,
u = this.upperBound;
return l[0] <= point[0] && point[0] <= u[0] && l[1] <= point[1] && point[1] <= u[1];
};
/**
* Check if the AABB is hit by a ray.
* @method overlapsRay
* @param {Ray} ray
* @return {number} -1 if no hit, a number between 0 and 1 if hit.
*/
AABB.prototype.overlapsRay = function(ray){
var t = 0;
// ray.direction is unit direction vector of ray
var dirFracX = 1 / ray.direction[0];
var dirFracY = 1 / ray.direction[1];
// this.lowerBound is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
var t1 = (this.lowerBound[0] - ray.from[0]) * dirFracX;
var t2 = (this.upperBound[0] - ray.from[0]) * dirFracX;
var t3 = (this.lowerBound[1] - ray.from[1]) * dirFracY;
var t4 = (this.upperBound[1] - ray.from[1]) * dirFracY;
var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)));
var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)));
// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
if (tmax < 0){
//t = tmax;
return -1;
}
// if tmin > tmax, ray doesn't intersect AABB
if (tmin > tmax){
//t = tmax;
return -1;
}
return tmin;
};
\ No newline at end of file
var vec2 = require('../math/vec2');
var Body = require('../objects/Body');
module.exports = Broadphase;
/**
* Base class for broadphase implementations.
* @class Broadphase
* @constructor
*/
function Broadphase(type){
this.type = type;
/**
* The resulting overlapping pairs. Will be filled with results during .getCollisionPairs().
* @property result
* @type {Array}
*/
this.result = [];
/**
* The world to search for collision pairs in. To change it, use .setWorld()
* @property world
* @type {World}
* @readOnly
*/
this.world = null;
/**
* The bounding volume type to use in the broadphase algorithms. Should be set to Broadphase.AABB or Broadphase.BOUNDING_CIRCLE.
* @property {Number} boundingVolumeType
*/
this.boundingVolumeType = Broadphase.AABB;
}
/**
* Axis aligned bounding box type.
* @static
* @property {Number} AABB
*/
Broadphase.AABB = 1;
/**
* Bounding circle type.
* @static
* @property {Number} BOUNDING_CIRCLE
*/
Broadphase.BOUNDING_CIRCLE = 2;
/**
* Set the world that we are searching for collision pairs in.
* @method setWorld
* @param {World} world
*/
Broadphase.prototype.setWorld = function(world){
this.world = world;
};
/**
* Get all potential intersecting body pairs.
* @method getCollisionPairs
* @param {World} world The world to search in.
* @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d).
*/
Broadphase.prototype.getCollisionPairs = function(world){};
var dist = vec2.create();
/**
* Check whether the bounding radius of two bodies overlap.
* @method boundingRadiusCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
Broadphase.boundingRadiusCheck = function(bodyA, bodyB){
vec2.sub(dist, bodyA.position, bodyB.position);
var d2 = vec2.squaredLength(dist),
r = bodyA.boundingRadius + bodyB.boundingRadius;
return d2 <= r*r;
};
/**
* Check whether the bounding radius of two bodies overlap.
* @method boundingRadiusCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
Broadphase.aabbCheck = function(bodyA, bodyB){
return bodyA.getAABB().overlaps(bodyB.getAABB());
};
/**
* Check whether the bounding radius of two bodies overlap.
* @method boundingRadiusCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
Broadphase.prototype.boundingVolumeCheck = function(bodyA, bodyB){
var result;
switch(this.boundingVolumeType){
case Broadphase.BOUNDING_CIRCLE:
result = Broadphase.boundingRadiusCheck(bodyA,bodyB);
break;
case Broadphase.AABB:
result = Broadphase.aabbCheck(bodyA,bodyB);
break;
default:
throw new Error('Bounding volume type not recognized: '+this.boundingVolumeType);
}
return result;
};
/**
* Check whether two bodies are allowed to collide at all.
* @method canCollide
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
Broadphase.canCollide = function(bodyA, bodyB){
var KINEMATIC = Body.KINEMATIC;
var STATIC = Body.STATIC;
// Cannot collide static bodies
if(bodyA.type === STATIC && bodyB.type === STATIC){
return false;
}
// Cannot collide static vs kinematic bodies
if( (bodyA.type === KINEMATIC && bodyB.type === STATIC) ||
(bodyA.type === STATIC && bodyB.type === KINEMATIC)){
return false;
}
// Cannot collide kinematic vs kinematic
if(bodyA.type === KINEMATIC && bodyB.type === KINEMATIC){
return false;
}
// Cannot collide both sleeping bodies
if(bodyA.sleepState === Body.SLEEPING && bodyB.sleepState === Body.SLEEPING){
return false;
}
// Cannot collide if one is static and the other is sleeping
if( (bodyA.sleepState === Body.SLEEPING && bodyB.type === STATIC) ||
(bodyB.sleepState === Body.SLEEPING && bodyA.type === STATIC)){
return false;
}
return true;
};
Broadphase.NAIVE = 1;
Broadphase.SAP = 2;
var Circle = require('../shapes/Circle'),
Plane = require('../shapes/Plane'),
Shape = require('../shapes/Shape'),
Particle = require('../shapes/Particle'),
Broadphase = require('../collision/Broadphase'),
vec2 = require('../math/vec2');
module.exports = NaiveBroadphase;
/**
* Naive broadphase implementation. Does N^2 tests.
*
* @class NaiveBroadphase
* @constructor
* @extends Broadphase
*/
function NaiveBroadphase(){
Broadphase.call(this, Broadphase.NAIVE);
}
NaiveBroadphase.prototype = new Broadphase();
NaiveBroadphase.prototype.constructor = NaiveBroadphase;
/**
* Get the colliding pairs
* @method getCollisionPairs
* @param {World} world
* @return {Array}
*/
NaiveBroadphase.prototype.getCollisionPairs = function(world){
var bodies = world.bodies,
result = this.result;
result.length = 0;
for(var i=0, Ncolliding=bodies.length; i!==Ncolliding; i++){
var bi = bodies[i];
for(var j=0; j<i; j++){
var bj = bodies[j];
if(Broadphase.canCollide(bi,bj) && this.boundingVolumeCheck(bi,bj)){
result.push(bi,bj);
}
}
}
return result;
};
/**
* Returns all the bodies within an AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
NaiveBroadphase.prototype.aabbQuery = function(world, aabb, result){
result = result || [];
var bodies = world.bodies;
for(var i = 0; i < bodies.length; i++){
var b = bodies[i];
if(b.aabbNeedsUpdate){
b.updateAABB();
}
if(b.aabb.overlaps(aabb)){
result.push(b);
}
}
return result;
};
\ No newline at end of file
This diff is collapsed.
module.exports = Ray;
var vec2 = require('../math/vec2');
var RaycastResult = require('../collision/RaycastResult');
var Shape = require('../shapes/Shape');
var AABB = require('../collision/AABB');
/**
* A line with a start and end point that is used to intersect shapes. For an example, see {{#crossLink "World/raycast:method"}}World.raycast{{/crossLink}}
* @class Ray
* @constructor
* @param {object} [options]
* @param {array} [options.from]
* @param {array} [options.to]
* @param {boolean} [options.checkCollisionResponse=true]
* @param {boolean} [options.skipBackfaces=false]
* @param {number} [options.collisionMask=-1]
* @param {number} [options.collisionGroup=-1]
* @param {number} [options.mode=Ray.ANY]
* @param {number} [options.callback]
*/
function Ray(options){
options = options || {};
/**
* Ray start point.
* @property {array} from
*/
this.from = options.from ? vec2.fromValues(options.from[0], options.from[1]) : vec2.create();
/**
* Ray end point
* @property {array} to
*/
this.to = options.to ? vec2.fromValues(options.to[0], options.to[1]) : vec2.create();
/**
* Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes.
* @property {Boolean} checkCollisionResponse
*/
this.checkCollisionResponse = options.checkCollisionResponse !== undefined ? options.checkCollisionResponse : true;
/**
* If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.
* @property {Boolean} skipBackfaces
*/
this.skipBackfaces = !!options.skipBackfaces;
/**
* @property {number} collisionMask
* @default -1
*/
this.collisionMask = options.collisionMask !== undefined ? options.collisionMask : -1;
/**
* @property {number} collisionGroup
* @default -1
*/
this.collisionGroup = options.collisionGroup !== undefined ? options.collisionGroup : -1;
/**
* The intersection mode. Should be {{#crossLink "Ray/ANY:property"}}Ray.ANY{{/crossLink}}, {{#crossLink "Ray/ALL:property"}}Ray.ALL{{/crossLink}} or {{#crossLink "Ray/CLOSEST:property"}}Ray.CLOSEST{{/crossLink}}.
* @property {number} mode
*/
this.mode = options.mode !== undefined ? options.mode : Ray.ANY;
/**
* Current, user-provided result callback. Will be used if mode is Ray.ALL.
* @property {Function} callback
*/
this.callback = options.callback || function(result){};
/**
* @readOnly
* @property {array} direction
*/
this.direction = vec2.create();
/**
* Length of the ray
* @readOnly
* @property {number} length
*/
this.length = 1;
this.update();
}
Ray.prototype.constructor = Ray;
/**
* This raycasting mode will make the Ray traverse through all intersection points and only return the closest one.
* @static
* @property {Number} CLOSEST
*/
Ray.CLOSEST = 1;
/**
* This raycasting mode will make the Ray stop when it finds the first intersection point.
* @static
* @property {Number} ANY
*/
Ray.ANY = 2;
/**
* This raycasting mode will traverse all intersection points and executes a callback for each one.
* @static
* @property {Number} ALL
*/
Ray.ALL = 4;
/**
* Should be called if you change the from or to point.
* @method update
*/
Ray.prototype.update = function(){
// Update .direction and .length
var d = this.direction;
vec2.sub(d, this.to, this.from);
this.length = vec2.length(d);
vec2.normalize(d, d);
};
/**
* @method intersectBodies
* @param {Array} bodies An array of Body objects.
*/
Ray.prototype.intersectBodies = function (result, bodies) {
for (var i = 0, l = bodies.length; !result.shouldStop(this) && i < l; i++) {
var body = bodies[i];
var aabb = body.getAABB();
if(aabb.overlapsRay(this) >= 0 || aabb.containsPoint(this.from)){
this.intersectBody(result, body);
}
}
};
var intersectBody_worldPosition = vec2.create();
/**
* Shoot a ray at a body, get back information about the hit.
* @method intersectBody
* @private
* @param {Body} body
*/
Ray.prototype.intersectBody = function (result, body) {
var checkCollisionResponse = this.checkCollisionResponse;
if(checkCollisionResponse && !body.collisionResponse){
return;
}
var worldPosition = intersectBody_worldPosition;
for (var i = 0, N = body.shapes.length; i < N; i++) {
var shape = body.shapes[i];
if(checkCollisionResponse && !shape.collisionResponse){
continue; // Skip
}
if((this.collisionGroup & shape.collisionMask) === 0 || (shape.collisionGroup & this.collisionMask) === 0){
continue;
}
// Get world angle and position of the shape
vec2.rotate(worldPosition, shape.position, body.angle);
vec2.add(worldPosition, worldPosition, body.position);
var worldAngle = shape.angle + body.angle;
this.intersectShape(
result,
shape,
worldAngle,
worldPosition,
body
);
if(result.shouldStop(this)){
break;
}
}
};
/**
* @method intersectShape
* @private
* @param {Shape} shape
* @param {number} angle
* @param {array} position
* @param {Body} body
*/
Ray.prototype.intersectShape = function(result, shape, angle, position, body){
var from = this.from;
// Checking radius
var distance = distanceFromIntersectionSquared(from, this.direction, position);
if (distance > shape.boundingRadius * shape.boundingRadius) {
return;
}
this._currentBody = body;
this._currentShape = shape;
shape.raycast(result, this, position, angle);
this._currentBody = this._currentShape = null;
};
/**
* Get the AABB of the ray.
* @method getAABB
* @param {AABB} aabb
*/
Ray.prototype.getAABB = function(result){
var to = this.to;
var from = this.from;
vec2.set(
result.lowerBound,
Math.min(to[0], from[0]),
Math.min(to[1], from[1])
);
vec2.set(
result.upperBound,
Math.max(to[0], from[0]),
Math.max(to[1], from[1])
);
};
var hitPointWorld = vec2.create();
/**
* @method reportIntersection
* @private
* @param {number} fraction
* @param {array} normal
* @param {number} [faceIndex=-1]
* @return {boolean} True if the intersections should continue
*/
Ray.prototype.reportIntersection = function(result, fraction, normal, faceIndex){
var from = this.from;
var to = this.to;
var shape = this._currentShape;
var body = this._currentBody;
// Skip back faces?
if(this.skipBackfaces && vec2.dot(normal, this.direction) > 0){
return;
}
switch(this.mode){
case Ray.ALL:
result.set(
normal,
shape,
body,
fraction,
faceIndex
);
this.callback(result);
break;
case Ray.CLOSEST:
// Store if closer than current closest
if(fraction < result.fraction || !result.hasHit()){
result.set(
normal,
shape,
body,
fraction,
faceIndex
);
}
break;
case Ray.ANY:
// Report and stop.
result.set(
normal,
shape,
body,
fraction,
faceIndex
);
break;
}
};
var v0 = vec2.create(),
intersect = vec2.create();
function distanceFromIntersectionSquared(from, direction, position) {
// v0 is vector from from to position
vec2.sub(v0, position, from);
var dot = vec2.dot(v0, direction);
// intersect = direction * dot + from
vec2.scale(intersect, direction, dot);
vec2.add(intersect, intersect, from);
return vec2.squaredDistance(position, intersect);
}
var vec2 = require('../math/vec2');
var Ray = require('../collision/Ray');
module.exports = RaycastResult;
/**
* Storage for Ray casting hit data.
* @class RaycastResult
* @constructor
*/
function RaycastResult(){
/**
* The normal of the hit, oriented in world space.
* @property {array} normal
*/
this.normal = vec2.create();
/**
* The hit shape, or null.
* @property {Shape} shape
*/
this.shape = null;
/**
* The hit body, or null.
* @property {Body} body
*/
this.body = null;
/**
* The index of the hit triangle, if the hit shape was indexable.
* @property {number} faceIndex
* @default -1
*/
this.faceIndex = -1;
/**
* Distance to the hit, as a fraction. 0 is at the "from" point, 1 is at the "to" point. Will be set to -1 if there was no hit yet.
* @property {number} fraction
* @default -1
*/
this.fraction = -1;
/**
* If the ray should stop traversing.
* @readonly
* @property {Boolean} isStopped
*/
this.isStopped = false;
}
/**
* Reset all result data. Must be done before re-using the result object.
* @method reset
*/
RaycastResult.prototype.reset = function () {
vec2.set(this.normal, 0, 0);
this.shape = null;
this.body = null;
this.faceIndex = -1;
this.fraction = -1;
this.isStopped = false;
};
/**
* Get the distance to the hit point.
* @method getHitDistance
* @param {Ray} ray
*/
RaycastResult.prototype.getHitDistance = function (ray) {
return vec2.distance(ray.from, ray.to) * this.fraction;
};
/**
* Returns true if the ray hit something since the last reset().
* @method hasHit
*/
RaycastResult.prototype.hasHit = function () {
return this.fraction !== -1;
};
/**
* Get world hit point.
* @method getHitPoint
* @param {array} out
* @param {Ray} ray
*/
RaycastResult.prototype.getHitPoint = function (out, ray) {
vec2.lerp(out, ray.from, ray.to, this.fraction);
};
/**
* Can be called while iterating over hits to stop searching for hit points.
* @method stop
*/
RaycastResult.prototype.stop = function(){
this.isStopped = true;
};
/**
* @method shouldStop
* @private
* @param {Ray} ray
* @return {boolean}
*/
RaycastResult.prototype.shouldStop = function(ray){
return this.isStopped || (this.fraction !== -1 && ray.mode === Ray.ANY);
};
/**
* @method set
* @private
* @param {array} normal
* @param {Shape} shape
* @param {Body} body
* @param {number} fraction
*/
RaycastResult.prototype.set = function(
normal,
shape,
body,
fraction,
faceIndex
){
vec2.copy(this.normal, normal);
this.shape = shape;
this.body = body;
this.fraction = fraction;
this.faceIndex = faceIndex;
};
\ No newline at end of file
var Utils = require('../utils/Utils')
, Broadphase = require('../collision/Broadphase');
module.exports = SAPBroadphase;
/**
* Sweep and prune broadphase along one axis.
*
* @class SAPBroadphase
* @constructor
* @extends Broadphase
*/
function SAPBroadphase(){
Broadphase.call(this,Broadphase.SAP);
/**
* List of bodies currently in the broadphase.
* @property axisList
* @type {Array}
*/
this.axisList = [];
/**
* The axis to sort along. 0 means x-axis and 1 y-axis. If your bodies are more spread out over the X axis, set axisIndex to 0, and you will gain some performance.
* @property axisIndex
* @type {Number}
*/
this.axisIndex = 0;
var that = this;
this._addBodyHandler = function(e){
that.axisList.push(e.body);
};
this._removeBodyHandler = function(e){
// Remove from list
var idx = that.axisList.indexOf(e.body);
if(idx !== -1){
that.axisList.splice(idx,1);
}
};
}
SAPBroadphase.prototype = new Broadphase();
SAPBroadphase.prototype.constructor = SAPBroadphase;
/**
* Change the world
* @method setWorld
* @param {World} world
*/
SAPBroadphase.prototype.setWorld = function(world){
// Clear the old axis array
this.axisList.length = 0;
// Add all bodies from the new world
Utils.appendArray(this.axisList, world.bodies);
// Remove old handlers, if any
world
.off("addBody",this._addBodyHandler)
.off("removeBody",this._removeBodyHandler);
// Add handlers to update the list of bodies.
world.on("addBody",this._addBodyHandler).on("removeBody",this._removeBodyHandler);
this.world = world;
};
/**
* Sorts bodies along an axis.
* @method sortAxisList
* @param {Array} a
* @param {number} axisIndex
* @return {Array}
*/
SAPBroadphase.sortAxisList = function(a, axisIndex){
axisIndex = axisIndex|0;
for(var i=1,l=a.length; i<l; i++) {
var v = a[i];
for(var j=i - 1;j>=0;j--) {
if(a[j].aabb.lowerBound[axisIndex] <= v.aabb.lowerBound[axisIndex]){
break;
}
a[j+1] = a[j];
}
a[j+1] = v;
}
return a;
};
SAPBroadphase.prototype.sortList = function(){
var bodies = this.axisList,
axisIndex = this.axisIndex;
// Sort the lists
SAPBroadphase.sortAxisList(bodies, axisIndex);
};
/**
* Get the colliding pairs
* @method getCollisionPairs
* @param {World} world
* @return {Array}
*/
SAPBroadphase.prototype.getCollisionPairs = function(world){
var bodies = this.axisList,
result = this.result,
axisIndex = this.axisIndex;
result.length = 0;
// Update all AABBs if needed
var l = bodies.length;
while(l--){
var b = bodies[l];
if(b.aabbNeedsUpdate){
b.updateAABB();
}
}
// Sort the lists
this.sortList();
// Look through the X list
for(var i=0, N=bodies.length|0; i!==N; i++){
var bi = bodies[i];
for(var j=i+1; j<N; j++){
var bj = bodies[j];
// Bounds overlap?
var overlaps = (bj.aabb.lowerBound[axisIndex] <= bi.aabb.upperBound[axisIndex]);
if(!overlaps){
break;
}
if(Broadphase.canCollide(bi,bj) && this.boundingVolumeCheck(bi,bj)){
result.push(bi,bj);
}
}
}
return result;
};
/**
* Returns all the bodies within an AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
SAPBroadphase.prototype.aabbQuery = function(world, aabb, result){
result = result || [];
this.sortList();
var axisIndex = this.axisIndex;
var axis = 'x';
if(axisIndex === 1){ axis = 'y'; }
if(axisIndex === 2){ axis = 'z'; }
var axisList = this.axisList;
var lower = aabb.lowerBound[axis];
var upper = aabb.upperBound[axis];
for(var i = 0; i < axisList.length; i++){
var b = axisList[i];
if(b.aabbNeedsUpdate){
b.updateAABB();
}
if(b.aabb.overlaps(aabb)){
result.push(b);
}
}
return result;
};
\ No newline at end of file
module.exports = Constraint;
var Utils = require('../utils/Utils');
/**
* Base constraint class.
*
* @class Constraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Number} type
* @param {Object} [options]
* @param {Object} [options.collideConnected=true]
*/
function Constraint(bodyA, bodyB, type, options){
/**
* The type of constraint. May be one of Constraint.DISTANCE, Constraint.GEAR, Constraint.LOCK, Constraint.PRISMATIC or Constraint.REVOLUTE.
* @property {number} type
*/
this.type = type;
options = Utils.defaults(options,{
collideConnected : true,
wakeUpBodies : true,
});
/**
* Equations to be solved in this constraint
*
* @property equations
* @type {Array}
*/
this.equations = [];
/**
* First body participating in the constraint.
* @property bodyA
* @type {Body}
*/
this.bodyA = bodyA;
/**
* Second body participating in the constraint.
* @property bodyB
* @type {Body}
*/
this.bodyB = bodyB;
/**
* Set to true if you want the connected bodies to collide.
* @property collideConnected
* @type {Boolean}
* @default true
*/
this.collideConnected = options.collideConnected;
// Wake up bodies when connected
if(options.wakeUpBodies){
if(bodyA){
bodyA.wakeUp();
}
if(bodyB){
bodyB.wakeUp();
}
}
}
/**
* Updates the internal constraint parameters before solve.
* @method update
*/
Constraint.prototype.update = function(){
throw new Error("method update() not implmemented in this Constraint subclass!");
};
/**
* @static
* @property {number} DISTANCE
*/
Constraint.DISTANCE = 1;
/**
* @static
* @property {number} GEAR
*/
Constraint.GEAR = 2;
/**
* @static
* @property {number} LOCK
*/
Constraint.LOCK = 3;
/**
* @static
* @property {number} PRISMATIC
*/
Constraint.PRISMATIC = 4;
/**
* @static
* @property {number} REVOLUTE
*/
Constraint.REVOLUTE = 5;
/**
* Set stiffness for this constraint.
* @method setStiffness
* @param {Number} stiffness
*/
Constraint.prototype.setStiffness = function(stiffness){
var eqs = this.equations;
for(var i=0; i !== eqs.length; i++){
var eq = eqs[i];
eq.stiffness = stiffness;
eq.needsUpdate = true;
}
};
/**
* Set relaxation for this constraint.
* @method setRelaxation
* @param {Number} relaxation
*/
Constraint.prototype.setRelaxation = function(relaxation){
var eqs = this.equations;
for(var i=0; i !== eqs.length; i++){
var eq = eqs[i];
eq.relaxation = relaxation;
eq.needsUpdate = true;
}
};
var Constraint = require('./Constraint')
, Equation = require('../equations/Equation')
, vec2 = require('../math/vec2')
, Utils = require('../utils/Utils');
module.exports = DistanceConstraint;
/**
* Constraint that tries to keep the distance between two bodies constant.
*
* @class DistanceConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {object} [options]
* @param {number} [options.distance] The distance to keep between the anchor points. Defaults to the current distance between the bodies.
* @param {Array} [options.localAnchorA] The anchor point for bodyA, defined locally in bodyA frame. Defaults to [0,0].
* @param {Array} [options.localAnchorB] The anchor point for bodyB, defined locally in bodyB frame. Defaults to [0,0].
* @param {object} [options.maxForce=Number.MAX_VALUE] Maximum force to apply.
* @extends Constraint
*
* @example
* // If distance is not given as an option, then the current distance between the bodies is used.
* // In this example, the bodies will be constrained to have a distance of 2 between their centers.
* var bodyA = new Body({ mass: 1, position: [-1, 0] });
* var bodyB = new Body({ mass: 1, position: [1, 0] });
* var constraint = new DistanceConstraint(bodyA, bodyB);
* world.addConstraint(constraint);
*
* @example
* // Manually set the distance and anchors
* var constraint = new DistanceConstraint(bodyA, bodyB, {
* distance: 1, // Distance to keep between the points
* localAnchorA: [1, 0], // Point on bodyA
* localAnchorB: [-1, 0] // Point on bodyB
* });
* world.addConstraint(constraint);
*/
function DistanceConstraint(bodyA,bodyB,options){
options = Utils.defaults(options,{
localAnchorA:[0,0],
localAnchorB:[0,0]
});
Constraint.call(this,bodyA,bodyB,Constraint.DISTANCE,options);
/**
* Local anchor in body A.
* @property localAnchorA
* @type {Array}
*/
this.localAnchorA = vec2.fromValues(options.localAnchorA[0], options.localAnchorA[1]);
/**
* Local anchor in body B.
* @property localAnchorB
* @type {Array}
*/
this.localAnchorB = vec2.fromValues(options.localAnchorB[0], options.localAnchorB[1]);
var localAnchorA = this.localAnchorA;
var localAnchorB = this.localAnchorB;
/**
* The distance to keep.
* @property distance
* @type {Number}
*/
this.distance = 0;
if(typeof(options.distance) === 'number'){
this.distance = options.distance;
} else {
// Use the current world distance between the world anchor points.
var worldAnchorA = vec2.create(),
worldAnchorB = vec2.create(),
r = vec2.create();
// Transform local anchors to world
vec2.rotate(worldAnchorA, localAnchorA, bodyA.angle);
vec2.rotate(worldAnchorB, localAnchorB, bodyB.angle);
vec2.add(r, bodyB.position, worldAnchorB);
vec2.sub(r, r, worldAnchorA);
vec2.sub(r, r, bodyA.position);
this.distance = vec2.length(r);
}
var maxForce;
if(typeof(options.maxForce)==="undefined" ){
maxForce = Number.MAX_VALUE;
} else {
maxForce = options.maxForce;
}
var normal = new Equation(bodyA,bodyB,-maxForce,maxForce); // Just in the normal direction
this.equations = [ normal ];
/**
* Max force to apply.
* @property {number} maxForce
*/
this.maxForce = maxForce;
// g = (xi - xj).dot(n)
// dg/dt = (vi - vj).dot(n) = G*W = [n 0 -n 0] * [vi wi vj wj]'
// ...and if we were to include offset points:
// g =
// (xj + rj - xi - ri).dot(n) - distance
//
// dg/dt =
// (vj + wj x rj - vi - wi x ri).dot(n) =
// { term 2 is near zero } =
// [-n -ri x n n rj x n] * [vi wi vj wj]' =
// G * W
//
// => G = [-n -rixn n rjxn]
var r = vec2.create();
var ri = vec2.create(); // worldAnchorA
var rj = vec2.create(); // worldAnchorB
var that = this;
normal.computeGq = function(){
var bodyA = this.bodyA,
bodyB = this.bodyB,
xi = bodyA.position,
xj = bodyB.position;
// Transform local anchors to world
vec2.rotate(ri, localAnchorA, bodyA.angle);
vec2.rotate(rj, localAnchorB, bodyB.angle);
vec2.add(r, xj, rj);
vec2.sub(r, r, ri);
vec2.sub(r, r, xi);
//vec2.sub(r, bodyB.position, bodyA.position);
return vec2.length(r) - that.distance;
};
// Make the contact constraint bilateral
this.setMaxForce(maxForce);
/**
* If the upper limit is enabled or not.
* @property {Boolean} upperLimitEnabled
*/
this.upperLimitEnabled = false;
/**
* The upper constraint limit.
* @property {number} upperLimit
*/
this.upperLimit = 1;
/**
* If the lower limit is enabled or not.
* @property {Boolean} lowerLimitEnabled
*/
this.lowerLimitEnabled = false;
/**
* The lower constraint limit.
* @property {number} lowerLimit
*/
this.lowerLimit = 0;
/**
* Current constraint position. This is equal to the current distance between the world anchor points.
* @property {number} position
*/
this.position = 0;
}
DistanceConstraint.prototype = new Constraint();
DistanceConstraint.prototype.constructor = DistanceConstraint;
/**
* Update the constraint equations. Should be done if any of the bodies changed position, before solving.
* @method update
*/
var n = vec2.create();
var ri = vec2.create(); // worldAnchorA
var rj = vec2.create(); // worldAnchorB
DistanceConstraint.prototype.update = function(){
var normal = this.equations[0],
bodyA = this.bodyA,
bodyB = this.bodyB,
distance = this.distance,
xi = bodyA.position,
xj = bodyB.position,
normalEquation = this.equations[0],
G = normal.G;
// Transform local anchors to world
vec2.rotate(ri, this.localAnchorA, bodyA.angle);
vec2.rotate(rj, this.localAnchorB, bodyB.angle);
// Get world anchor points and normal
vec2.add(n, xj, rj);
vec2.sub(n, n, ri);
vec2.sub(n, n, xi);
this.position = vec2.length(n);
var violating = false;
if(this.upperLimitEnabled){
if(this.position > this.upperLimit){
normalEquation.maxForce = 0;
normalEquation.minForce = -this.maxForce;
this.distance = this.upperLimit;
violating = true;
}
}
if(this.lowerLimitEnabled){
if(this.position < this.lowerLimit){
normalEquation.maxForce = this.maxForce;
normalEquation.minForce = 0;
this.distance = this.lowerLimit;
violating = true;
}
}
if((this.lowerLimitEnabled || this.upperLimitEnabled) && !violating){
// No constraint needed.
normalEquation.enabled = false;
return;
}
normalEquation.enabled = true;
vec2.normalize(n,n);
// Caluclate cross products
var rixn = vec2.crossLength(ri, n),
rjxn = vec2.crossLength(rj, n);
// G = [-n -rixn n rjxn]
G[0] = -n[0];
G[1] = -n[1];
G[2] = -rixn;
G[3] = n[0];
G[4] = n[1];
G[5] = rjxn;
};
/**
* Set the max force to be used
* @method setMaxForce
* @param {Number} maxForce
*/
DistanceConstraint.prototype.setMaxForce = function(maxForce){
var normal = this.equations[0];
normal.minForce = -maxForce;
normal.maxForce = maxForce;
};
/**
* Get the max force
* @method getMaxForce
* @return {Number}
*/
DistanceConstraint.prototype.getMaxForce = function(){
var normal = this.equations[0];
return normal.maxForce;
};
var Constraint = require('./Constraint')
, Equation = require('../equations/Equation')
, AngleLockEquation = require('../equations/AngleLockEquation')
, vec2 = require('../math/vec2');
module.exports = GearConstraint;
/**
* Constrains the angle of two bodies to each other to be equal. If a gear ratio is not one, the angle of bodyA must be a multiple of the angle of bodyB.
* @class GearConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Object} [options]
* @param {Number} [options.angle=0] Relative angle between the bodies. Will be set to the current angle between the bodies (the gear ratio is accounted for).
* @param {Number} [options.ratio=1] Gear ratio.
* @param {Number} [options.maxTorque] Maximum torque to apply.
* @extends Constraint
*
* @example
* var constraint = new GearConstraint(bodyA, bodyB);
* world.addConstraint(constraint);
*
* @example
* var constraint = new GearConstraint(bodyA, bodyB, {
* ratio: 2,
* maxTorque: 1000
* });
* world.addConstraint(constraint);
*/
function GearConstraint(bodyA, bodyB, options){
options = options || {};
Constraint.call(this, bodyA, bodyB, Constraint.GEAR, options);
/**
* The gear ratio.
* @property ratio
* @type {Number}
*/
this.ratio = options.ratio !== undefined ? options.ratio : 1;
/**
* The relative angle
* @property angle
* @type {Number}
*/
this.angle = options.angle !== undefined ? options.angle : bodyB.angle - this.ratio * bodyA.angle;
// Send same parameters to the equation
options.angle = this.angle;
options.ratio = this.ratio;
this.equations = [
new AngleLockEquation(bodyA,bodyB,options),
];
// Set max torque
if(options.maxTorque !== undefined){
this.setMaxTorque(options.maxTorque);
}
}
GearConstraint.prototype = new Constraint();
GearConstraint.prototype.constructor = GearConstraint;
GearConstraint.prototype.update = function(){
var eq = this.equations[0];
if(eq.ratio !== this.ratio){
eq.setRatio(this.ratio);
}
eq.angle = this.angle;
};
/**
* Set the max torque for the constraint.
* @method setMaxTorque
* @param {Number} torque
*/
GearConstraint.prototype.setMaxTorque = function(torque){
this.equations[0].setMaxTorque(torque);
};
/**
* Get the max torque for the constraint.
* @method getMaxTorque
* @return {Number}
*/
GearConstraint.prototype.getMaxTorque = function(torque){
return this.equations[0].maxForce;
};
\ No newline at end of file
var Constraint = require('./Constraint')
, vec2 = require('../math/vec2')
, Equation = require('../equations/Equation');
module.exports = LockConstraint;
/**
* Locks the relative position and rotation between two bodies.
*
* @class LockConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Object} [options]
* @param {Array} [options.localOffsetB] The offset of bodyB in bodyA's frame. If not given the offset is computed from current positions.
* @param {number} [options.localAngleB] The angle of bodyB in bodyA's frame. If not given, the angle is computed from current angles.
* @param {number} [options.maxForce]
* @extends Constraint
*
* @example
* // Locks the relative position and rotation between bodyA and bodyB
* var constraint = new LockConstraint(bodyA, bodyB);
* world.addConstraint(constraint);
*/
function LockConstraint(bodyA, bodyB, options){
options = options || {};
Constraint.call(this,bodyA,bodyB,Constraint.LOCK,options);
var maxForce = ( typeof(options.maxForce)==="undefined" ? Number.MAX_VALUE : options.maxForce );
var localAngleB = options.localAngleB || 0;
// Use 3 equations:
// gx = (xj - xi - l) * xhat = 0
// gy = (xj - xi - l) * yhat = 0
// gr = (xi - xj + r) * that = 0
//
// ...where:
// l is the localOffsetB vector rotated to world in bodyA frame
// r is the same vector but reversed and rotated from bodyB frame
// xhat, yhat are world axis vectors
// that is the tangent of r
//
// For the first two constraints, we get
// G*W = (vj - vi - ldot ) * xhat
// = (vj - vi - wi x l) * xhat
//
// Since (wi x l) * xhat = (l x xhat) * wi, we get
// G*W = [ -1 0 (-l x xhat) 1 0 0] * [vi wi vj wj]
//
// The last constraint gives
// GW = (vi - vj + wj x r) * that
// = [ that 0 -that (r x t) ]
var x = new Equation(bodyA,bodyB,-maxForce,maxForce),
y = new Equation(bodyA,bodyB,-maxForce,maxForce),
rot = new Equation(bodyA,bodyB,-maxForce,maxForce);
var l = vec2.create(),
g = vec2.create(),
that = this;
x.computeGq = function(){
vec2.rotate(l, that.localOffsetB, bodyA.angle);
vec2.sub(g, bodyB.position, bodyA.position);
vec2.sub(g, g, l);
return g[0];
};
y.computeGq = function(){
vec2.rotate(l, that.localOffsetB, bodyA.angle);
vec2.sub(g, bodyB.position, bodyA.position);
vec2.sub(g, g, l);
return g[1];
};
var r = vec2.create(),
t = vec2.create();
rot.computeGq = function(){
vec2.rotate(r, that.localOffsetB, bodyB.angle - that.localAngleB);
vec2.scale(r,r,-1);
vec2.sub(g,bodyA.position,bodyB.position);
vec2.add(g,g,r);
vec2.rotate(t,r,-Math.PI/2);
vec2.normalize(t,t);
return vec2.dot(g,t);
};
/**
* The offset of bodyB in bodyA's frame.
* @property {Array} localOffsetB
*/
this.localOffsetB = vec2.create();
if(options.localOffsetB){
vec2.copy(this.localOffsetB, options.localOffsetB);
} else {
// Construct from current positions
vec2.sub(this.localOffsetB, bodyB.position, bodyA.position);
vec2.rotate(this.localOffsetB, this.localOffsetB, -bodyA.angle);
}
/**
* The offset angle of bodyB in bodyA's frame.
* @property {Number} localAngleB
*/
this.localAngleB = 0;
if(typeof(options.localAngleB) === 'number'){
this.localAngleB = options.localAngleB;
} else {
// Construct
this.localAngleB = bodyB.angle - bodyA.angle;
}
this.equations.push(x, y, rot);
this.setMaxForce(maxForce);
}
LockConstraint.prototype = new Constraint();
LockConstraint.prototype.constructor = LockConstraint;
/**
* Set the maximum force to be applied.
* @method setMaxForce
* @param {Number} force
*/
LockConstraint.prototype.setMaxForce = function(force){
var eqs = this.equations;
for(var i=0; i<this.equations.length; i++){
eqs[i].maxForce = force;
eqs[i].minForce = -force;
}
};
/**
* Get the max force.
* @method getMaxForce
* @return {Number}
*/
LockConstraint.prototype.getMaxForce = function(){
return this.equations[0].maxForce;
};
var l = vec2.create();
var r = vec2.create();
var t = vec2.create();
var xAxis = vec2.fromValues(1,0);
var yAxis = vec2.fromValues(0,1);
LockConstraint.prototype.update = function(){
var x = this.equations[0],
y = this.equations[1],
rot = this.equations[2],
bodyA = this.bodyA,
bodyB = this.bodyB;
vec2.rotate(l,this.localOffsetB,bodyA.angle);
vec2.rotate(r,this.localOffsetB,bodyB.angle - this.localAngleB);
vec2.scale(r,r,-1);
vec2.rotate(t,r,Math.PI/2);
vec2.normalize(t,t);
x.G[0] = -1;
x.G[1] = 0;
x.G[2] = -vec2.crossLength(l,xAxis);
x.G[3] = 1;
y.G[0] = 0;
y.G[1] = -1;
y.G[2] = -vec2.crossLength(l,yAxis);
y.G[4] = 1;
rot.G[0] = -t[0];
rot.G[1] = -t[1];
rot.G[3] = t[0];
rot.G[4] = t[1];
rot.G[5] = vec2.crossLength(r,t);
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
var NaiveBroadphase = require(__dirname + '/../../src/collision/NaiveBroadphase');
var Broadphase = require(__dirname + '/../../src/collision/Broadphase');
exports.construct = function(test){
var broadphase = new NaiveBroadphase();
test.equal(broadphase.type, Broadphase.NAIVE);
test.done();
};
exports.getCollisionPairs = function(test){
// STUB
test.done();
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment