2016-02-12 16:00:36 +00:00
/ * *
* 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 .
* /
! function ( e ) { if ( "object" == typeof exports ) module . exports = e ( ) ; else if ( "function" == typeof define && false ) define ( e ) ; else { var f ; "undefined" != typeof window ? f = window : "undefined" != typeof global ? f = global : "undefined" != typeof self && ( f = self ) , f . p2 = e ( ) } } ( function ( ) { var define , module , exports ; return ( function e ( t , n , r ) { function s ( o , u ) { if ( ! n [ o ] ) { if ( ! t [ o ] ) { var a = typeof require == "function" && require ; if ( ! u && a ) return a ( o , ! 0 ) ; if ( i ) return i ( o , ! 0 ) ; throw new Error ( "Cannot find module '" + o + "'" ) } var f = n [ o ] = { exports : { } } ; t [ o ] [ 0 ] . call ( f . exports , function ( e ) { var n = t [ o ] [ 1 ] [ e ] ; return s ( n ? n : e ) } , f , f . exports , e , t , n , r ) } return n [ o ] . exports } var i = typeof require == "function" && require ; for ( var o = 0 ; o < r . length ; o ++ ) s ( r [ o ] ) ; return s } ) ( { 1 : [ function ( _dereq _ , module , exports ) {
var Scalar = _dereq _ ( './Scalar' ) ;
module . exports = Line ;
/ * *
* Container for line - related functions
* @ class Line
* /
function Line ( ) { } ;
/ * *
* Compute the intersection between two lines .
* @ static
* @ method lineInt
* @ param { Array } l1 Line vector 1
* @ param { Array } l2 Line vector 2
* @ param { Number } precision Precision to use when checking if the lines are parallel
* @ return { Array } The intersection point .
* /
Line . lineInt = function ( l1 , l2 , precision ) {
precision = precision || 0 ;
var i = [ 0 , 0 ] ; // point
var a1 , b1 , c1 , a2 , b2 , c2 , det ; // scalars
a1 = l1 [ 1 ] [ 1 ] - l1 [ 0 ] [ 1 ] ;
b1 = l1 [ 0 ] [ 0 ] - l1 [ 1 ] [ 0 ] ;
c1 = a1 * l1 [ 0 ] [ 0 ] + b1 * l1 [ 0 ] [ 1 ] ;
a2 = l2 [ 1 ] [ 1 ] - l2 [ 0 ] [ 1 ] ;
b2 = l2 [ 0 ] [ 0 ] - l2 [ 1 ] [ 0 ] ;
c2 = a2 * l2 [ 0 ] [ 0 ] + b2 * l2 [ 0 ] [ 1 ] ;
det = a1 * b2 - a2 * b1 ;
if ( ! Scalar . eq ( det , 0 , precision ) ) { // lines are not parallel
i [ 0 ] = ( b2 * c1 - b1 * c2 ) / det ;
i [ 1 ] = ( a1 * c2 - a2 * c1 ) / det ;
}
return i ;
} ;
/ * *
* Checks if two line segments intersects .
* @ method segmentsIntersect
* @ param { Array } p1 The start vertex of the first line segment .
* @ param { Array } p2 The end vertex of the first line segment .
* @ param { Array } q1 The start vertex of the second line segment .
* @ param { Array } q2 The end vertex of the second line segment .
* @ return { Boolean } True if the two line segments intersect
* /
Line . segmentsIntersect = function ( p1 , p2 , q1 , q2 ) {
var dx = p2 [ 0 ] - p1 [ 0 ] ;
var dy = p2 [ 1 ] - p1 [ 1 ] ;
var da = q2 [ 0 ] - q1 [ 0 ] ;
var db = q2 [ 1 ] - q1 [ 1 ] ;
// segments are parallel
if ( da * dy - db * dx == 0 )
return false ;
var s = ( dx * ( q1 [ 1 ] - p1 [ 1 ] ) + dy * ( p1 [ 0 ] - q1 [ 0 ] ) ) / ( da * dy - db * dx )
var t = ( da * ( p1 [ 1 ] - q1 [ 1 ] ) + db * ( q1 [ 0 ] - p1 [ 0 ] ) ) / ( db * dx - da * dy )
return ( s >= 0 && s <= 1 && t >= 0 && t <= 1 ) ;
} ;
} , { "./Scalar" : 4 } ] , 2 : [ function ( _dereq _ , module , exports ) {
module . exports = Point ;
/ * *
* Point related functions
* @ class Point
* /
function Point ( ) { } ;
/ * *
* Get the area of a triangle spanned by the three given points . Note that the area will be negative if the points are not given in counter - clockwise order .
* @ static
* @ method area
* @ param { Array } a
* @ param { Array } b
* @ param { Array } c
* @ return { Number }
* /
Point . area = function ( a , b , c ) {
return ( ( ( b [ 0 ] - a [ 0 ] ) * ( c [ 1 ] - a [ 1 ] ) ) - ( ( c [ 0 ] - a [ 0 ] ) * ( b [ 1 ] - a [ 1 ] ) ) ) ;
} ;
Point . left = function ( a , b , c ) {
return Point . area ( a , b , c ) > 0 ;
} ;
Point . leftOn = function ( a , b , c ) {
return Point . area ( a , b , c ) >= 0 ;
} ;
Point . right = function ( a , b , c ) {
return Point . area ( a , b , c ) < 0 ;
} ;
Point . rightOn = function ( a , b , c ) {
return Point . area ( a , b , c ) <= 0 ;
} ;
var tmpPoint1 = [ ] ,
tmpPoint2 = [ ] ;
/ * *
* Check if three points are collinear
* @ method collinear
* @ param { Array } a
* @ param { Array } b
* @ param { Array } c
* @ param { Number } [ thresholdAngle = 0 ] Threshold angle to use when comparing the vectors . The function will return true if the angle between the resulting vectors is less than this value . Use zero for max precision .
* @ return { Boolean }
* /
Point . collinear = function ( a , b , c , thresholdAngle ) {
if ( ! thresholdAngle )
return Point . area ( a , b , c ) == 0 ;
else {
var ab = tmpPoint1 ,
bc = tmpPoint2 ;
ab [ 0 ] = b [ 0 ] - a [ 0 ] ;
ab [ 1 ] = b [ 1 ] - a [ 1 ] ;
bc [ 0 ] = c [ 0 ] - b [ 0 ] ;
bc [ 1 ] = c [ 1 ] - b [ 1 ] ;
var dot = ab [ 0 ] * bc [ 0 ] + ab [ 1 ] * bc [ 1 ] ,
magA = Math . sqrt ( ab [ 0 ] * ab [ 0 ] + ab [ 1 ] * ab [ 1 ] ) ,
magB = Math . sqrt ( bc [ 0 ] * bc [ 0 ] + bc [ 1 ] * bc [ 1 ] ) ,
angle = Math . acos ( dot / ( magA * magB ) ) ;
return angle < thresholdAngle ;
}
} ;
Point . sqdist = function ( a , b ) {
var dx = b [ 0 ] - a [ 0 ] ;
var dy = b [ 1 ] - a [ 1 ] ;
return dx * dx + dy * dy ;
} ;
} , { } ] , 3 : [ function ( _dereq _ , module , exports ) {
var Line = _dereq _ ( "./Line" )
, Point = _dereq _ ( "./Point" )
, Scalar = _dereq _ ( "./Scalar" )
module . exports = Polygon ;
/ * *
* Polygon class .
* @ class Polygon
* @ constructor
* /
function Polygon ( ) {
/ * *
* Vertices that this polygon consists of . An array of array of numbers , example : [ [ 0 , 0 ] , [ 1 , 0 ] , . . ]
* @ property vertices
* @ type { Array }
* /
this . vertices = [ ] ;
}
/ * *
* Get a vertex at position i . It does not matter if i is out of bounds , this function will just cycle .
* @ method at
* @ param { Number } i
* @ return { Array }
* /
Polygon . prototype . at = function ( i ) {
var v = this . vertices ,
s = v . length ;
return v [ i < 0 ? i % s + s : i % s ] ;
} ;
/ * *
* Get first vertex
* @ method first
* @ return { Array }
* /
Polygon . prototype . first = function ( ) {
return this . vertices [ 0 ] ;
} ;
/ * *
* Get last vertex
* @ method last
* @ return { Array }
* /
Polygon . prototype . last = function ( ) {
return this . vertices [ this . vertices . length - 1 ] ;
} ;
/ * *
* Clear the polygon data
* @ method clear
* @ return { Array }
* /
Polygon . prototype . clear = function ( ) {
this . vertices . length = 0 ;
} ;
/ * *
* Append points "from" to "to" - 1 from an other polygon "poly" onto this one .
* @ method append
* @ param { Polygon } poly The polygon to get points from .
* @ param { Number } from The vertex index in "poly" .
* @ param { Number } to The end vertex index in "poly" . Note that this vertex is NOT included when appending .
* @ return { Array }
* /
Polygon . prototype . append = function ( poly , from , to ) {
if ( typeof ( from ) == "undefined" ) throw new Error ( "From is not given!" ) ;
if ( typeof ( to ) == "undefined" ) throw new Error ( "To is not given!" ) ;
if ( to - 1 < from ) throw new Error ( "lol1" ) ;
if ( to > poly . vertices . length ) throw new Error ( "lol2" ) ;
if ( from < 0 ) throw new Error ( "lol3" ) ;
for ( var i = from ; i < to ; i ++ ) {
this . vertices . push ( poly . vertices [ i ] ) ;
}
} ;
/ * *
* Make sure that the polygon vertices are ordered counter - clockwise .
* @ method makeCCW
* /
Polygon . prototype . makeCCW = function ( ) {
var br = 0 ,
v = this . vertices ;
// find bottom right point
for ( var i = 1 ; i < this . vertices . length ; ++ i ) {
if ( v [ i ] [ 1 ] < v [ br ] [ 1 ] || ( v [ i ] [ 1 ] == v [ br ] [ 1 ] && v [ i ] [ 0 ] > v [ br ] [ 0 ] ) ) {
br = i ;
}
}
// reverse poly if clockwise
if ( ! Point . left ( this . at ( br - 1 ) , this . at ( br ) , this . at ( br + 1 ) ) ) {
this . reverse ( ) ;
}
} ;
/ * *
* Reverse the vertices in the polygon
* @ method reverse
* /
Polygon . prototype . reverse = function ( ) {
var tmp = [ ] ;
for ( var i = 0 , N = this . vertices . length ; i !== N ; i ++ ) {
tmp . push ( this . vertices . pop ( ) ) ;
}
this . vertices = tmp ;
} ;
/ * *
* Check if a point in the polygon is a reflex point
* @ method isReflex
* @ param { Number } i
* @ return { Boolean }
* /
Polygon . prototype . isReflex = function ( i ) {
return Point . right ( this . at ( i - 1 ) , this . at ( i ) , this . at ( i + 1 ) ) ;
} ;
var tmpLine1 = [ ] ,
tmpLine2 = [ ] ;
/ * *
* Check if two vertices in the polygon can see each other
* @ method canSee
* @ param { Number } a Vertex index 1
* @ param { Number } b Vertex index 2
* @ return { Boolean }
* /
Polygon . prototype . canSee = function ( a , b ) {
var p , dist , l1 = tmpLine1 , l2 = tmpLine2 ;
if ( Point . leftOn ( this . at ( a + 1 ) , this . at ( a ) , this . at ( b ) ) && Point . rightOn ( this . at ( a - 1 ) , this . at ( a ) , this . at ( b ) ) ) {
return false ;
}
dist = Point . sqdist ( this . at ( a ) , this . at ( b ) ) ;
for ( var i = 0 ; i !== this . vertices . length ; ++ i ) { // for each edge
if ( ( i + 1 ) % this . vertices . length === a || i === a ) // ignore incident edges
continue ;
if ( Point . leftOn ( this . at ( a ) , this . at ( b ) , this . at ( i + 1 ) ) && Point . rightOn ( this . at ( a ) , this . at ( b ) , this . at ( i ) ) ) { // if diag intersects an edge
l1 [ 0 ] = this . at ( a ) ;
l1 [ 1 ] = this . at ( b ) ;
l2 [ 0 ] = this . at ( i ) ;
l2 [ 1 ] = this . at ( i + 1 ) ;
p = Line . lineInt ( l1 , l2 ) ;
if ( Point . sqdist ( this . at ( a ) , p ) < dist ) { // if edge is blocking visibility to b
return false ;
}
}
}
return true ;
} ;
/ * *
* Copy the polygon from vertex i to vertex j .
* @ method copy
* @ param { Number } i
* @ param { Number } j
* @ param { Polygon } [ targetPoly ] Optional target polygon to save in .
* @ return { Polygon } The resulting copy .
* /
Polygon . prototype . copy = function ( i , j , targetPoly ) {
var p = targetPoly || new Polygon ( ) ;
p . clear ( ) ;
if ( i < j ) {
// Insert all vertices from i to j
for ( var k = i ; k <= j ; k ++ )
p . vertices . push ( this . vertices [ k ] ) ;
} else {
// Insert vertices 0 to j
for ( var k = 0 ; k <= j ; k ++ )
p . vertices . push ( this . vertices [ k ] ) ;
// Insert vertices i to end
for ( var k = i ; k < this . vertices . length ; k ++ )
p . vertices . push ( this . vertices [ k ] ) ;
}
return p ;
} ;
/ * *
* Decomposes the polygon into convex pieces . Returns a list of edges [ [ p1 , p2 ] , [ p2 , p3 ] , ... ] that cuts the polygon .
* Note that this algorithm has complexity O ( N ^ 4 ) and will be very slow for polygons with many vertices .
* @ method getCutEdges
* @ return { Array }
* /
Polygon . prototype . getCutEdges = function ( ) {
var min = [ ] , tmp1 = [ ] , tmp2 = [ ] , tmpPoly = new Polygon ( ) ;
var nDiags = Number . MAX _VALUE ;
for ( var i = 0 ; i < this . vertices . length ; ++ i ) {
if ( this . isReflex ( i ) ) {
for ( var j = 0 ; j < this . vertices . length ; ++ j ) {
if ( this . canSee ( i , j ) ) {
tmp1 = this . copy ( i , j , tmpPoly ) . getCutEdges ( ) ;
tmp2 = this . copy ( j , i , tmpPoly ) . getCutEdges ( ) ;
for ( var k = 0 ; k < tmp2 . length ; k ++ )
tmp1 . push ( tmp2 [ k ] ) ;
if ( tmp1 . length < nDiags ) {
min = tmp1 ;
nDiags = tmp1 . length ;
min . push ( [ this . at ( i ) , this . at ( j ) ] ) ;
}
}
}
}
}
return min ;
} ;
/ * *
* Decomposes the polygon into one or more convex sub - Polygons .
* @ method decomp
* @ return { Array } An array or Polygon objects .
* /
Polygon . prototype . decomp = function ( ) {
var edges = this . getCutEdges ( ) ;
if ( edges . length > 0 )
return this . slice ( edges ) ;
else
return [ this ] ;
} ;
/ * *
* Slices the polygon given one or more cut edges . If given one , this function will return two polygons ( false on failure ) . If many , an array of polygons .
* @ method slice
* @ param { Array } cutEdges A list of edges , as returned by . getCutEdges ( )
* @ return { Array }
* /
Polygon . prototype . slice = function ( cutEdges ) {
if ( cutEdges . length == 0 ) return [ this ] ;
if ( cutEdges instanceof Array && cutEdges . length && cutEdges [ 0 ] instanceof Array && cutEdges [ 0 ] . length == 2 && cutEdges [ 0 ] [ 0 ] instanceof Array ) {
var polys = [ this ] ;
for ( var i = 0 ; i < cutEdges . length ; i ++ ) {
var cutEdge = cutEdges [ i ] ;
// Cut all polys
for ( var j = 0 ; j < polys . length ; j ++ ) {
var poly = polys [ j ] ;
var result = poly . slice ( cutEdge ) ;
if ( result ) {
// Found poly! Cut and quit
polys . splice ( j , 1 ) ;
polys . push ( result [ 0 ] , result [ 1 ] ) ;
break ;
}
}
}
return polys ;
} else {
// Was given one edge
var cutEdge = cutEdges ;
var i = this . vertices . indexOf ( cutEdge [ 0 ] ) ;
var j = this . vertices . indexOf ( cutEdge [ 1 ] ) ;
if ( i != - 1 && j != - 1 ) {
return [ this . copy ( i , j ) ,
this . copy ( j , i ) ] ;
} else {
return false ;
}
}
} ;
/ * *
* Checks that the line segments of this polygon do not intersect each other .
* @ method isSimple
* @ param { Array } path An array of vertices e . g . [ [ 0 , 0 ] , [ 0 , 1 ] , ... ]
* @ return { Boolean }
* @ todo Should it check all segments with all others ?
* /
Polygon . prototype . isSimple = function ( ) {
var path = this . vertices ;
// Check
for ( var i = 0 ; i < path . length - 1 ; i ++ ) {
for ( var j = 0 ; j < i - 1 ; j ++ ) {
if ( Line . segmentsIntersect ( path [ i ] , path [ i + 1 ] , path [ j ] , path [ j + 1 ] ) ) {
return false ;
}
}
}
// Check the segment between the last and the first point to all others
for ( var i = 1 ; i < path . length - 2 ; i ++ ) {
if ( Line . segmentsIntersect ( path [ 0 ] , path [ path . length - 1 ] , path [ i ] , path [ i + 1 ] ) ) {
return false ;
}
}
return true ;
} ;
function getIntersectionPoint ( p1 , p2 , q1 , q2 , delta ) {
delta = delta || 0 ;
var a1 = p2 [ 1 ] - p1 [ 1 ] ;
var b1 = p1 [ 0 ] - p2 [ 0 ] ;
var c1 = ( a1 * p1 [ 0 ] ) + ( b1 * p1 [ 1 ] ) ;
var a2 = q2 [ 1 ] - q1 [ 1 ] ;
var b2 = q1 [ 0 ] - q2 [ 0 ] ;
var c2 = ( a2 * q1 [ 0 ] ) + ( b2 * q1 [ 1 ] ) ;
var det = ( a1 * b2 ) - ( a2 * b1 ) ;
if ( ! Scalar . eq ( det , 0 , delta ) )
return [ ( ( b2 * c1 ) - ( b1 * c2 ) ) / det , ( ( a1 * c2 ) - ( a2 * c1 ) ) / det ]
else
return [ 0 , 0 ]
}
/ * *
* Quickly decompose the Polygon into convex sub - polygons .
* @ method quickDecomp
* @ param { Array } result
* @ param { Array } [ reflexVertices ]
* @ param { Array } [ steinerPoints ]
* @ param { Number } [ delta ]
* @ param { Number } [ maxlevel ]
* @ param { Number } [ level ]
* @ return { Array }
* /
Polygon . prototype . quickDecomp = function ( result , reflexVertices , steinerPoints , delta , maxlevel , level ) {
maxlevel = maxlevel || 100 ;
level = level || 0 ;
delta = delta || 25 ;
result = typeof ( result ) != "undefined" ? result : [ ] ;
reflexVertices = reflexVertices || [ ] ;
steinerPoints = steinerPoints || [ ] ;
var upperInt = [ 0 , 0 ] , lowerInt = [ 0 , 0 ] , p = [ 0 , 0 ] ; // Points
var upperDist = 0 , lowerDist = 0 , d = 0 , closestDist = 0 ; // scalars
var upperIndex = 0 , lowerIndex = 0 , closestIndex = 0 ; // Integers
var lowerPoly = new Polygon ( ) , upperPoly = new Polygon ( ) ; // polygons
var poly = this ,
v = this . vertices ;
if ( v . length < 3 ) return result ;
level ++ ;
if ( level > maxlevel ) {
console . warn ( "quickDecomp: max level (" + maxlevel + ") reached." ) ;
return result ;
}
for ( var i = 0 ; i < this . vertices . length ; ++ i ) {
if ( poly . isReflex ( i ) ) {
reflexVertices . push ( poly . vertices [ i ] ) ;
upperDist = lowerDist = Number . MAX _VALUE ;
for ( var j = 0 ; j < this . vertices . length ; ++ j ) {
if ( Point . left ( poly . at ( i - 1 ) , poly . at ( i ) , poly . at ( j ) )
&& Point . rightOn ( poly . at ( i - 1 ) , poly . at ( i ) , poly . at ( j - 1 ) ) ) { // if line intersects with an edge
p = getIntersectionPoint ( poly . at ( i - 1 ) , poly . at ( i ) , poly . at ( j ) , poly . at ( j - 1 ) ) ; // find the point of intersection
if ( Point . right ( poly . at ( i + 1 ) , poly . at ( i ) , p ) ) { // make sure it's inside the poly
d = Point . sqdist ( poly . vertices [ i ] , p ) ;
if ( d < lowerDist ) { // keep only the closest intersection
lowerDist = d ;
lowerInt = p ;
lowerIndex = j ;
}
}
}
if ( Point . left ( poly . at ( i + 1 ) , poly . at ( i ) , poly . at ( j + 1 ) )
&& Point . rightOn ( poly . at ( i + 1 ) , poly . at ( i ) , poly . at ( j ) ) ) {
p = getIntersectionPoint ( poly . at ( i + 1 ) , poly . at ( i ) , poly . at ( j ) , poly . at ( j + 1 ) ) ;
if ( Point . left ( poly . at ( i - 1 ) , poly . at ( i ) , p ) ) {
d = Point . sqdist ( poly . vertices [ i ] , p ) ;
if ( d < upperDist ) {
upperDist = d ;
upperInt = p ;
upperIndex = j ;
}
}
}
}
// if there are no vertices to connect to, choose a point in the middle
if ( lowerIndex == ( upperIndex + 1 ) % this . vertices . length ) {
//console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")");
p [ 0 ] = ( lowerInt [ 0 ] + upperInt [ 0 ] ) / 2 ;
p [ 1 ] = ( lowerInt [ 1 ] + upperInt [ 1 ] ) / 2 ;
steinerPoints . push ( p ) ;
if ( i < upperIndex ) {
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1);
lowerPoly . append ( poly , i , upperIndex + 1 ) ;
lowerPoly . vertices . push ( p ) ;
upperPoly . vertices . push ( p ) ;
if ( lowerIndex != 0 ) {
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end());
upperPoly . append ( poly , lowerIndex , poly . vertices . length ) ;
}
//upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1);
upperPoly . append ( poly , 0 , i + 1 ) ;
} else {
if ( i != 0 ) {
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end());
lowerPoly . append ( poly , i , poly . vertices . length ) ;
}
//lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1);
lowerPoly . append ( poly , 0 , upperIndex + 1 ) ;
lowerPoly . vertices . push ( p ) ;
upperPoly . vertices . push ( p ) ;
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1);
upperPoly . append ( poly , lowerIndex , i + 1 ) ;
}
} else {
// connect to the closest point within the triangle
//console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n");
if ( lowerIndex > upperIndex ) {
upperIndex += this . vertices . length ;
}
closestDist = Number . MAX _VALUE ;
if ( upperIndex < lowerIndex ) {
return result ;
}
for ( var j = lowerIndex ; j <= upperIndex ; ++ j ) {
if ( Point . leftOn ( poly . at ( i - 1 ) , poly . at ( i ) , poly . at ( j ) )
&& Point . rightOn ( poly . at ( i + 1 ) , poly . at ( i ) , poly . at ( j ) ) ) {
d = Point . sqdist ( poly . at ( i ) , poly . at ( j ) ) ;
if ( d < closestDist ) {
closestDist = d ;
closestIndex = j % this . vertices . length ;
}
}
}
if ( i < closestIndex ) {
lowerPoly . append ( poly , i , closestIndex + 1 ) ;
if ( closestIndex != 0 ) {
upperPoly . append ( poly , closestIndex , v . length ) ;
}
upperPoly . append ( poly , 0 , i + 1 ) ;
} else {
if ( i != 0 ) {
lowerPoly . append ( poly , i , v . length ) ;
}
lowerPoly . append ( poly , 0 , closestIndex + 1 ) ;
upperPoly . append ( poly , closestIndex , i + 1 ) ;
}
}
// solve smallest poly first
if ( lowerPoly . vertices . length < upperPoly . vertices . length ) {
lowerPoly . quickDecomp ( result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
upperPoly . quickDecomp ( result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
} else {
upperPoly . quickDecomp ( result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
lowerPoly . quickDecomp ( result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
}
return result ;
}
}
result . push ( this ) ;
return result ;
} ;
/ * *
* Remove collinear points in the polygon .
* @ method removeCollinearPoints
* @ param { Number } [ precision ] The threshold angle to use when determining whether two edges are collinear . Use zero for finest precision .
* @ return { Number } The number of points removed
* /
Polygon . prototype . removeCollinearPoints = function ( precision ) {
var num = 0 ;
for ( var i = this . vertices . length - 1 ; this . vertices . length > 3 && i >= 0 ; -- i ) {
if ( Point . collinear ( this . at ( i - 1 ) , this . at ( i ) , this . at ( i + 1 ) , precision ) ) {
// Remove the middle point
this . vertices . splice ( i % this . vertices . length , 1 ) ;
i -- ; // Jump one point forward. Otherwise we may get a chain removal
num ++ ;
}
}
return num ;
} ;
} , { "./Line" : 1 , "./Point" : 2 , "./Scalar" : 4 } ] , 4 : [ function ( _dereq _ , module , exports ) {
module . exports = Scalar ;
/ * *
* Scalar functions
* @ class Scalar
* /
function Scalar ( ) { }
/ * *
* Check if two scalars are equal
* @ static
* @ method eq
* @ param { Number } a
* @ param { Number } b
* @ param { Number } [ precision ]
* @ return { Boolean }
* /
Scalar . eq = function ( a , b , precision ) {
precision = precision || 0 ;
return Math . abs ( a - b ) < precision ;
} ;
} , { } ] , 5 : [ function ( _dereq _ , module , exports ) {
module . exports = {
Polygon : _dereq _ ( "./Polygon" ) ,
Point : _dereq _ ( "./Point" ) ,
} ;
} , { "./Point" : 2 , "./Polygon" : 3 } ] , 6 : [ function ( _dereq _ , module , exports ) {
module . exports = {
"name" : "p2" ,
2016-09-21 22:22:08 +00:00
"version" : "0.7.1" ,
2016-02-12 16:00:36 +00:00
"description" : "A JavaScript 2D physics engine." ,
"author" : "Stefan Hedman <schteppe@gmail.com> (http://steffe.se)" ,
"keywords" : [
"p2.js" ,
"p2" ,
"physics" ,
"engine" ,
"2d"
] ,
"main" : "./src/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" : {
2016-09-21 22:22:08 +00:00
"poly-decomp" : "0.1.1"
2016-02-12 16:00:36 +00:00
}
}
} , { } ] , 7 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' )
, Utils = _dereq _ ( '../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 ;
} ;
} , { "../math/vec2" : 30 , "../utils/Utils" : 57 } ] , 8 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' ) ;
var Body = _dereq _ ( '../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 ;
} , { "../math/vec2" : 30 , "../objects/Body" : 31 } ] , 9 : [ function ( _dereq _ , module , exports ) {
var Circle = _dereq _ ( '../shapes/Circle' ) ,
Plane = _dereq _ ( '../shapes/Plane' ) ,
Shape = _dereq _ ( '../shapes/Shape' ) ,
Particle = _dereq _ ( '../shapes/Particle' ) ,
Broadphase = _dereq _ ( '../collision/Broadphase' ) ,
vec2 = _dereq _ ( '../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 ;
} ;
} , { "../collision/Broadphase" : 8 , "../math/vec2" : 30 , "../shapes/Circle" : 39 , "../shapes/Particle" : 43 , "../shapes/Plane" : 44 , "../shapes/Shape" : 45 } ] , 10 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' )
, sub = vec2 . sub
, add = vec2 . add
, dot = vec2 . dot
, Utils = _dereq _ ( '../utils/Utils' )
, ContactEquationPool = _dereq _ ( '../utils/ContactEquationPool' )
, FrictionEquationPool = _dereq _ ( '../utils/FrictionEquationPool' )
, TupleDictionary = _dereq _ ( '../utils/TupleDictionary' )
, Equation = _dereq _ ( '../equations/Equation' )
, ContactEquation = _dereq _ ( '../equations/ContactEquation' )
, FrictionEquation = _dereq _ ( '../equations/FrictionEquation' )
, Circle = _dereq _ ( '../shapes/Circle' )
, Convex = _dereq _ ( '../shapes/Convex' )
, Shape = _dereq _ ( '../shapes/Shape' )
, Body = _dereq _ ( '../objects/Body' )
, Box = _dereq _ ( '../shapes/Box' ) ;
module . exports = Narrowphase ;
// Temp things
var yAxis = vec2 . fromValues ( 0 , 1 ) ;
var tmp1 = vec2 . fromValues ( 0 , 0 )
, tmp2 = vec2 . fromValues ( 0 , 0 )
, tmp3 = vec2 . fromValues ( 0 , 0 )
, tmp4 = vec2 . fromValues ( 0 , 0 )
, tmp5 = vec2 . fromValues ( 0 , 0 )
, tmp6 = vec2 . fromValues ( 0 , 0 )
, tmp7 = vec2 . fromValues ( 0 , 0 )
, tmp8 = vec2 . fromValues ( 0 , 0 )
, tmp9 = vec2 . fromValues ( 0 , 0 )
, tmp10 = vec2 . fromValues ( 0 , 0 )
, tmp11 = vec2 . fromValues ( 0 , 0 )
, tmp12 = vec2 . fromValues ( 0 , 0 )
, tmp13 = vec2 . fromValues ( 0 , 0 )
, tmp14 = vec2 . fromValues ( 0 , 0 )
, tmp15 = vec2 . fromValues ( 0 , 0 )
, tmp16 = vec2 . fromValues ( 0 , 0 )
, tmp17 = vec2 . fromValues ( 0 , 0 )
, tmp18 = vec2 . fromValues ( 0 , 0 )
, tmpArray = [ ] ;
/ * *
* Narrowphase . Creates contacts and friction given shapes and transforms .
* @ class Narrowphase
* @ constructor
* /
function Narrowphase ( ) {
/ * *
* @ property contactEquations
* @ type { Array }
* /
this . contactEquations = [ ] ;
/ * *
* @ property frictionEquations
* @ type { Array }
* /
this . frictionEquations = [ ] ;
/ * *
* Whether to make friction equations in the upcoming contacts .
* @ property enableFriction
* @ type { Boolean }
* /
this . enableFriction = true ;
/ * *
* Whether to make equations enabled in upcoming contacts .
* @ property enabledEquations
* @ type { Boolean }
* /
this . enabledEquations = true ;
/ * *
* The friction slip force to use when creating friction equations .
* @ property slipForce
* @ type { Number }
* /
this . slipForce = 10.0 ;
/ * *
* The friction value to use in the upcoming friction equations .
* @ property frictionCoefficient
* @ type { Number }
* /
this . frictionCoefficient = 0.3 ;
/ * *
* Will be the . relativeVelocity in each produced FrictionEquation .
* @ property { Number } surfaceVelocity
* /
this . surfaceVelocity = 0 ;
/ * *
* Keeps track of the allocated ContactEquations .
* @ property { ContactEquationPool } contactEquationPool
*
* @ example
*
* // Allocate a few equations before starting the simulation.
* // This way, no contact objects need to be created on the fly in the game loop.
* world . narrowphase . contactEquationPool . resize ( 1024 ) ;
* world . narrowphase . frictionEquationPool . resize ( 1024 ) ;
* /
this . contactEquationPool = new ContactEquationPool ( { size : 32 } ) ;
/ * *
* Keeps track of the allocated ContactEquations .
* @ property { FrictionEquationPool } frictionEquationPool
* /
this . frictionEquationPool = new FrictionEquationPool ( { size : 64 } ) ;
/ * *
* The restitution value to use in the next contact equations .
* @ property restitution
* @ type { Number }
* /
this . restitution = 0 ;
/ * *
* The stiffness value to use in the next contact equations .
* @ property { Number } stiffness
* /
this . stiffness = Equation . DEFAULT _STIFFNESS ;
/ * *
* The stiffness value to use in the next contact equations .
* @ property { Number } stiffness
* /
this . relaxation = Equation . DEFAULT _RELAXATION ;
/ * *
* The stiffness value to use in the next friction equations .
* @ property frictionStiffness
* @ type { Number }
* /
this . frictionStiffness = Equation . DEFAULT _STIFFNESS ;
/ * *
* The relaxation value to use in the next friction equations .
* @ property frictionRelaxation
* @ type { Number }
* /
this . frictionRelaxation = Equation . DEFAULT _RELAXATION ;
/ * *
* Enable reduction of friction equations . If disabled , a box on a plane will generate 2 contact equations and 2 friction equations . If enabled , there will be only one friction equation . Same kind of simplifications are made for all collision types .
* @ property enableFrictionReduction
* @ type { Boolean }
* @ deprecated This flag will be removed when the feature is stable enough .
* @ default true
* /
this . enableFrictionReduction = true ;
/ * *
* Keeps track of the colliding bodies last step .
* @ private
* @ property collidingBodiesLastStep
* @ type { TupleDictionary }
* /
this . collidingBodiesLastStep = new TupleDictionary ( ) ;
/ * *
* Contact skin size value to use in the next contact equations .
* @ property { Number } contactSkinSize
* @ default 0.01
* /
this . contactSkinSize = 0.01 ;
}
var bodiesOverlap _shapePositionA = vec2 . create ( ) ;
var bodiesOverlap _shapePositionB = vec2 . create ( ) ;
/ * *
* @ method bodiesOverlap
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ return { Boolean }
* @ todo shape world transforms are wrong
* /
Narrowphase . prototype . bodiesOverlap = function ( bodyA , bodyB ) {
var shapePositionA = bodiesOverlap _shapePositionA ;
var shapePositionB = bodiesOverlap _shapePositionB ;
// Loop over all shapes of bodyA
for ( var k = 0 , Nshapesi = bodyA . shapes . length ; k !== Nshapesi ; k ++ ) {
var shapeA = bodyA . shapes [ k ] ;
bodyA . toWorldFrame ( shapePositionA , shapeA . position ) ;
// All shapes of body j
for ( var l = 0 , Nshapesj = bodyB . shapes . length ; l !== Nshapesj ; l ++ ) {
var shapeB = bodyB . shapes [ l ] ;
bodyB . toWorldFrame ( shapePositionB , shapeB . position ) ;
if ( this [ shapeA . type | shapeB . type ] (
bodyA ,
shapeA ,
shapePositionA ,
shapeA . angle + bodyA . angle ,
bodyB ,
shapeB ,
shapePositionB ,
shapeB . angle + bodyB . angle ,
true
) ) {
return true ;
}
}
}
return false ;
} ;
/ * *
* Check if the bodies were in contact since the last reset ( ) .
* @ method collidedLastStep
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ return { Boolean }
* /
Narrowphase . prototype . collidedLastStep = function ( bodyA , bodyB ) {
var id1 = bodyA . id | 0 ,
id2 = bodyB . id | 0 ;
return ! ! this . collidingBodiesLastStep . get ( id1 , id2 ) ;
} ;
/ * *
* Throws away the old equations and gets ready to create new
* @ method reset
* /
Narrowphase . prototype . reset = function ( ) {
this . collidingBodiesLastStep . reset ( ) ;
var eqs = this . contactEquations ;
var l = eqs . length ;
while ( l -- ) {
var eq = eqs [ l ] ,
id1 = eq . bodyA . id ,
id2 = eq . bodyB . id ;
this . collidingBodiesLastStep . set ( id1 , id2 , true ) ;
}
var ce = this . contactEquations ,
fe = this . frictionEquations ;
for ( var i = 0 ; i < ce . length ; i ++ ) {
this . contactEquationPool . release ( ce [ i ] ) ;
}
for ( var i = 0 ; i < fe . length ; i ++ ) {
this . frictionEquationPool . release ( fe [ i ] ) ;
}
// Reset
this . contactEquations . length = this . frictionEquations . length = 0 ;
} ;
/ * *
* Creates a ContactEquation , either by reusing an existing object or creating a new one .
* @ method createContactEquation
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ return { ContactEquation }
* /
Narrowphase . prototype . createContactEquation = function ( bodyA , bodyB , shapeA , shapeB ) {
var c = this . contactEquationPool . get ( ) ;
c . bodyA = bodyA ;
c . bodyB = bodyB ;
c . shapeA = shapeA ;
c . shapeB = shapeB ;
c . restitution = this . restitution ;
c . firstImpact = ! this . collidedLastStep ( bodyA , bodyB ) ;
c . stiffness = this . stiffness ;
c . relaxation = this . relaxation ;
c . needsUpdate = true ;
c . enabled = this . enabledEquations ;
c . offset = this . contactSkinSize ;
return c ;
} ;
/ * *
* Creates a FrictionEquation , either by reusing an existing object or creating a new one .
* @ method createFrictionEquation
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ return { FrictionEquation }
* /
Narrowphase . prototype . createFrictionEquation = function ( bodyA , bodyB , shapeA , shapeB ) {
var c = this . frictionEquationPool . get ( ) ;
c . bodyA = bodyA ;
c . bodyB = bodyB ;
c . shapeA = shapeA ;
c . shapeB = shapeB ;
c . setSlipForce ( this . slipForce ) ;
c . frictionCoefficient = this . frictionCoefficient ;
c . relativeVelocity = this . surfaceVelocity ;
c . enabled = this . enabledEquations ;
c . needsUpdate = true ;
c . stiffness = this . frictionStiffness ;
c . relaxation = this . frictionRelaxation ;
c . contactEquations . length = 0 ;
return c ;
} ;
/ * *
* Creates a FrictionEquation given the data in the ContactEquation . Uses same offset vectors ri and rj , but the tangent vector will be constructed from the collision normal .
* @ method createFrictionFromContact
* @ param { ContactEquation } contactEquation
* @ return { FrictionEquation }
* /
Narrowphase . prototype . createFrictionFromContact = function ( c ) {
var eq = this . createFrictionEquation ( c . bodyA , c . bodyB , c . shapeA , c . shapeB ) ;
vec2 . copy ( eq . contactPointA , c . contactPointA ) ;
vec2 . copy ( eq . contactPointB , c . contactPointB ) ;
vec2 . rotate90cw ( eq . t , c . normalA ) ;
eq . contactEquations . push ( c ) ;
return eq ;
} ;
// Take the average N latest contact point on the plane.
Narrowphase . prototype . createFrictionFromAverage = function ( numContacts ) {
var c = this . contactEquations [ this . contactEquations . length - 1 ] ;
var eq = this . createFrictionEquation ( c . bodyA , c . bodyB , c . shapeA , c . shapeB ) ;
var bodyA = c . bodyA ;
var bodyB = c . bodyB ;
vec2 . set ( eq . contactPointA , 0 , 0 ) ;
vec2 . set ( eq . contactPointB , 0 , 0 ) ;
vec2 . set ( eq . t , 0 , 0 ) ;
for ( var i = 0 ; i !== numContacts ; i ++ ) {
c = this . contactEquations [ this . contactEquations . length - 1 - i ] ;
if ( c . bodyA === bodyA ) {
vec2 . add ( eq . t , eq . t , c . normalA ) ;
vec2 . add ( eq . contactPointA , eq . contactPointA , c . contactPointA ) ;
vec2 . add ( eq . contactPointB , eq . contactPointB , c . contactPointB ) ;
} else {
vec2 . sub ( eq . t , eq . t , c . normalA ) ;
vec2 . add ( eq . contactPointA , eq . contactPointA , c . contactPointB ) ;
vec2 . add ( eq . contactPointB , eq . contactPointB , c . contactPointA ) ;
}
eq . contactEquations . push ( c ) ;
}
var invNumContacts = 1 / numContacts ;
vec2 . scale ( eq . contactPointA , eq . contactPointA , invNumContacts ) ;
vec2 . scale ( eq . contactPointB , eq . contactPointB , invNumContacts ) ;
vec2 . normalize ( eq . t , eq . t ) ;
vec2 . rotate90cw ( eq . t , eq . t ) ;
return eq ;
} ;
/ * *
* Convex / line narrowphase
* @ method convexLine
* @ param { Body } convexBody
* @ param { Convex } convexShape
* @ param { Array } convexOffset
* @ param { Number } convexAngle
* @ param { Body } lineBody
* @ param { Line } lineShape
* @ param { Array } lineOffset
* @ param { Number } lineAngle
* @ param { boolean } justTest
* @ todo Implement me !
* /
Narrowphase . prototype [ Shape . LINE | Shape . CONVEX ] =
Narrowphase . prototype . convexLine = function (
convexBody ,
convexShape ,
convexOffset ,
convexAngle ,
lineBody ,
lineShape ,
lineOffset ,
lineAngle ,
justTest
) {
// TODO
if ( justTest ) {
return false ;
} else {
return 0 ;
}
} ;
/ * *
* Line / box narrowphase
* @ method lineBox
* @ param { Body } lineBody
* @ param { Line } lineShape
* @ param { Array } lineOffset
* @ param { Number } lineAngle
* @ param { Body } boxBody
* @ param { Box } boxShape
* @ param { Array } boxOffset
* @ param { Number } boxAngle
* @ param { Boolean } justTest
* @ todo Implement me !
* /
Narrowphase . prototype [ Shape . LINE | Shape . BOX ] =
Narrowphase . prototype . lineBox = function (
lineBody ,
lineShape ,
lineOffset ,
lineAngle ,
boxBody ,
boxShape ,
boxOffset ,
boxAngle ,
justTest
) {
// TODO
if ( justTest ) {
return false ;
} else {
return 0 ;
}
} ;
function setConvexToCapsuleShapeMiddle ( convexShape , capsuleShape ) {
vec2 . set ( convexShape . vertices [ 0 ] , - capsuleShape . length * 0.5 , - capsuleShape . radius ) ;
vec2 . set ( convexShape . vertices [ 1 ] , capsuleShape . length * 0.5 , - capsuleShape . radius ) ;
vec2 . set ( convexShape . vertices [ 2 ] , capsuleShape . length * 0.5 , capsuleShape . radius ) ;
vec2 . set ( convexShape . vertices [ 3 ] , - capsuleShape . length * 0.5 , capsuleShape . radius ) ;
}
var convexCapsule _tempRect = new Box ( { width : 1 , height : 1 } ) ,
convexCapsule _tempVec = vec2 . create ( ) ;
/ * *
* Convex / capsule narrowphase
* @ method convexCapsule
* @ param { Body } convexBody
* @ param { Convex } convexShape
* @ param { Array } convexPosition
* @ param { Number } convexAngle
* @ param { Body } capsuleBody
* @ param { Capsule } capsuleShape
* @ param { Array } capsulePosition
* @ param { Number } capsuleAngle
* /
Narrowphase . prototype [ Shape . CAPSULE | Shape . CONVEX ] =
Narrowphase . prototype [ Shape . CAPSULE | Shape . BOX ] =
Narrowphase . prototype . convexCapsule = function (
convexBody ,
convexShape ,
convexPosition ,
convexAngle ,
capsuleBody ,
capsuleShape ,
capsulePosition ,
capsuleAngle ,
justTest
) {
// Check the circles
// Add offsets!
var circlePos = convexCapsule _tempVec ;
vec2 . set ( circlePos , capsuleShape . length / 2 , 0 ) ;
vec2 . rotate ( circlePos , circlePos , capsuleAngle ) ;
vec2 . add ( circlePos , circlePos , capsulePosition ) ;
var result1 = this . circleConvex ( capsuleBody , capsuleShape , circlePos , capsuleAngle , convexBody , convexShape , convexPosition , convexAngle , justTest , capsuleShape . radius ) ;
vec2 . set ( circlePos , - capsuleShape . length / 2 , 0 ) ;
vec2 . rotate ( circlePos , circlePos , capsuleAngle ) ;
vec2 . add ( circlePos , circlePos , capsulePosition ) ;
var result2 = this . circleConvex ( capsuleBody , capsuleShape , circlePos , capsuleAngle , convexBody , convexShape , convexPosition , convexAngle , justTest , capsuleShape . radius ) ;
if ( justTest && ( result1 || result2 ) ) {
return true ;
}
// Check center rect
var r = convexCapsule _tempRect ;
setConvexToCapsuleShapeMiddle ( r , capsuleShape ) ;
var result = this . convexConvex ( convexBody , convexShape , convexPosition , convexAngle , capsuleBody , r , capsulePosition , capsuleAngle , justTest ) ;
return result + result1 + result2 ;
} ;
/ * *
* Capsule / line narrowphase
* @ method lineCapsule
* @ param { Body } lineBody
* @ param { Line } lineShape
* @ param { Array } linePosition
* @ param { Number } lineAngle
* @ param { Body } capsuleBody
* @ param { Capsule } capsuleShape
* @ param { Array } capsulePosition
* @ param { Number } capsuleAngle
* @ todo Implement me !
* /
Narrowphase . prototype [ Shape . CAPSULE | Shape . LINE ] =
Narrowphase . prototype . lineCapsule = function (
lineBody ,
lineShape ,
linePosition ,
lineAngle ,
capsuleBody ,
capsuleShape ,
capsulePosition ,
capsuleAngle ,
justTest
) {
// TODO
if ( justTest ) {
return false ;
} else {
return 0 ;
}
} ;
var capsuleCapsule _tempVec1 = vec2 . create ( ) ;
var capsuleCapsule _tempVec2 = vec2 . create ( ) ;
var capsuleCapsule _tempRect1 = new Box ( { width : 1 , height : 1 } ) ;
/ * *
* Capsule / capsule narrowphase
* @ method capsuleCapsule
* @ param { Body } bi
* @ param { Capsule } si
* @ param { Array } xi
* @ param { Number } ai
* @ param { Body } bj
* @ param { Capsule } sj
* @ param { Array } xj
* @ param { Number } aj
* /
Narrowphase . prototype [ Shape . CAPSULE | Shape . CAPSULE ] =
Narrowphase . prototype . capsuleCapsule = function ( bi , si , xi , ai , bj , sj , xj , aj , justTest ) {
var enableFrictionBefore ;
// Check the circles
// Add offsets!
var circlePosi = capsuleCapsule _tempVec1 ,
circlePosj = capsuleCapsule _tempVec2 ;
var numContacts = 0 ;
// Need 4 circle checks, between all
for ( var i = 0 ; i < 2 ; i ++ ) {
vec2 . set ( circlePosi , ( i === 0 ? - 1 : 1 ) * si . length / 2 , 0 ) ;
vec2 . rotate ( circlePosi , circlePosi , ai ) ;
vec2 . add ( circlePosi , circlePosi , xi ) ;
for ( var j = 0 ; j < 2 ; j ++ ) {
vec2 . set ( circlePosj , ( j === 0 ? - 1 : 1 ) * sj . length / 2 , 0 ) ;
vec2 . rotate ( circlePosj , circlePosj , aj ) ;
vec2 . add ( circlePosj , circlePosj , xj ) ;
// Temporarily turn off friction
if ( this . enableFrictionReduction ) {
enableFrictionBefore = this . enableFriction ;
this . enableFriction = false ;
}
var result = this . circleCircle ( bi , si , circlePosi , ai , bj , sj , circlePosj , aj , justTest , si . radius , sj . radius ) ;
if ( this . enableFrictionReduction ) {
this . enableFriction = enableFrictionBefore ;
}
if ( justTest && result ) {
return true ;
}
numContacts += result ;
}
}
if ( this . enableFrictionReduction ) {
// Temporarily turn off friction
enableFrictionBefore = this . enableFriction ;
this . enableFriction = false ;
}
// Check circles against the center boxs
var rect = capsuleCapsule _tempRect1 ;
setConvexToCapsuleShapeMiddle ( rect , si ) ;
var result1 = this . convexCapsule ( bi , rect , xi , ai , bj , sj , xj , aj , justTest ) ;
if ( this . enableFrictionReduction ) {
this . enableFriction = enableFrictionBefore ;
}
if ( justTest && result1 ) {
return true ;
}
numContacts += result1 ;
if ( this . enableFrictionReduction ) {
// Temporarily turn off friction
var enableFrictionBefore = this . enableFriction ;
this . enableFriction = false ;
}
setConvexToCapsuleShapeMiddle ( rect , sj ) ;
var result2 = this . convexCapsule ( bj , rect , xj , aj , bi , si , xi , ai , justTest ) ;
if ( this . enableFrictionReduction ) {
this . enableFriction = enableFrictionBefore ;
}
if ( justTest && result2 ) {
return true ;
}
numContacts += result2 ;
if ( this . enableFrictionReduction ) {
if ( numContacts && this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromAverage ( numContacts ) ) ;
}
}
return numContacts ;
} ;
/ * *
* Line / line narrowphase
* @ method lineLine
* @ param { Body } bodyA
* @ param { Line } shapeA
* @ param { Array } positionA
* @ param { Number } angleA
* @ param { Body } bodyB
* @ param { Line } shapeB
* @ param { Array } positionB
* @ param { Number } angleB
* @ todo Implement me !
* /
Narrowphase . prototype [ Shape . LINE | Shape . LINE ] =
Narrowphase . prototype . lineLine = function (
bodyA ,
shapeA ,
positionA ,
angleA ,
bodyB ,
shapeB ,
positionB ,
angleB ,
justTest
) {
// TODO
if ( justTest ) {
return false ;
} else {
return 0 ;
}
} ;
/ * *
* Plane / line Narrowphase
* @ method planeLine
* @ param { Body } planeBody
* @ param { Plane } planeShape
* @ param { Array } planeOffset
* @ param { Number } planeAngle
* @ param { Body } lineBody
* @ param { Line } lineShape
* @ param { Array } lineOffset
* @ param { Number } lineAngle
* /
Narrowphase . prototype [ Shape . PLANE | Shape . LINE ] =
Narrowphase . prototype . planeLine = function ( planeBody , planeShape , planeOffset , planeAngle ,
lineBody , lineShape , lineOffset , lineAngle , justTest ) {
var worldVertex0 = tmp1 ,
worldVertex1 = tmp2 ,
worldVertex01 = tmp3 ,
worldVertex11 = tmp4 ,
worldEdge = tmp5 ,
worldEdgeUnit = tmp6 ,
dist = tmp7 ,
worldNormal = tmp8 ,
worldTangent = tmp9 ,
verts = tmpArray ,
numContacts = 0 ;
// Get start and end points
vec2 . set ( worldVertex0 , - lineShape . length / 2 , 0 ) ;
vec2 . set ( worldVertex1 , lineShape . length / 2 , 0 ) ;
// Not sure why we have to use worldVertex*1 here, but it won't work otherwise. Tired.
vec2 . rotate ( worldVertex01 , worldVertex0 , lineAngle ) ;
vec2 . rotate ( worldVertex11 , worldVertex1 , lineAngle ) ;
add ( worldVertex01 , worldVertex01 , lineOffset ) ;
add ( worldVertex11 , worldVertex11 , lineOffset ) ;
vec2 . copy ( worldVertex0 , worldVertex01 ) ;
vec2 . copy ( worldVertex1 , worldVertex11 ) ;
// Get vector along the line
sub ( worldEdge , worldVertex1 , worldVertex0 ) ;
vec2 . normalize ( worldEdgeUnit , worldEdge ) ;
// Get tangent to the edge.
vec2 . rotate90cw ( worldTangent , worldEdgeUnit ) ;
vec2 . rotate ( worldNormal , yAxis , planeAngle ) ;
// Check line ends
verts [ 0 ] = worldVertex0 ;
verts [ 1 ] = worldVertex1 ;
for ( var i = 0 ; i < verts . length ; i ++ ) {
var v = verts [ i ] ;
sub ( dist , v , planeOffset ) ;
var d = dot ( dist , worldNormal ) ;
if ( d < 0 ) {
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( planeBody , lineBody , planeShape , lineShape ) ;
numContacts ++ ;
vec2 . copy ( c . normalA , worldNormal ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
// distance vector along plane normal
vec2 . scale ( dist , worldNormal , d ) ;
// Vector from plane center to contact
sub ( c . contactPointA , v , dist ) ;
sub ( c . contactPointA , c . contactPointA , planeBody . position ) ;
// From line center to contact
sub ( c . contactPointB , v , lineOffset ) ;
add ( c . contactPointB , c . contactPointB , lineOffset ) ;
sub ( c . contactPointB , c . contactPointB , lineBody . position ) ;
this . contactEquations . push ( c ) ;
if ( ! this . enableFrictionReduction ) {
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
}
}
}
if ( justTest ) {
return false ;
}
if ( ! this . enableFrictionReduction ) {
if ( numContacts && this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromAverage ( numContacts ) ) ;
}
}
return numContacts ;
} ;
Narrowphase . prototype [ Shape . PARTICLE | Shape . CAPSULE ] =
Narrowphase . prototype . particleCapsule = function (
particleBody ,
particleShape ,
particlePosition ,
particleAngle ,
capsuleBody ,
capsuleShape ,
capsulePosition ,
capsuleAngle ,
justTest
) {
return this . circleLine ( particleBody , particleShape , particlePosition , particleAngle , capsuleBody , capsuleShape , capsulePosition , capsuleAngle , justTest , capsuleShape . radius , 0 ) ;
} ;
/ * *
* Circle / line Narrowphase
* @ method circleLine
* @ param { Body } circleBody
* @ param { Circle } circleShape
* @ param { Array } circleOffset
* @ param { Number } circleAngle
* @ param { Body } lineBody
* @ param { Line } lineShape
* @ param { Array } lineOffset
* @ param { Number } lineAngle
* @ param { Boolean } justTest If set to true , this function will return the result ( intersection or not ) without adding equations .
* @ param { Number } lineRadius Radius to add to the line . Can be used to test Capsules .
* @ param { Number } circleRadius If set , this value overrides the circle shape radius .
* /
Narrowphase . prototype [ Shape . CIRCLE | Shape . LINE ] =
Narrowphase . prototype . circleLine = function (
circleBody ,
circleShape ,
circleOffset ,
circleAngle ,
lineBody ,
lineShape ,
lineOffset ,
lineAngle ,
justTest ,
lineRadius ,
circleRadius
) {
var lineRadius = lineRadius || 0 ,
circleRadius = typeof ( circleRadius ) !== "undefined" ? circleRadius : circleShape . radius ,
orthoDist = tmp1 ,
lineToCircleOrthoUnit = tmp2 ,
projectedPoint = tmp3 ,
centerDist = tmp4 ,
worldTangent = tmp5 ,
worldEdge = tmp6 ,
worldEdgeUnit = tmp7 ,
worldVertex0 = tmp8 ,
worldVertex1 = tmp9 ,
worldVertex01 = tmp10 ,
worldVertex11 = tmp11 ,
dist = tmp12 ,
lineToCircle = tmp13 ,
lineEndToLineRadius = tmp14 ,
verts = tmpArray ;
// Get start and end points
vec2 . set ( worldVertex0 , - lineShape . length / 2 , 0 ) ;
vec2 . set ( worldVertex1 , lineShape . length / 2 , 0 ) ;
// Not sure why we have to use worldVertex*1 here, but it won't work otherwise. Tired.
vec2 . rotate ( worldVertex01 , worldVertex0 , lineAngle ) ;
vec2 . rotate ( worldVertex11 , worldVertex1 , lineAngle ) ;
add ( worldVertex01 , worldVertex01 , lineOffset ) ;
add ( worldVertex11 , worldVertex11 , lineOffset ) ;
vec2 . copy ( worldVertex0 , worldVertex01 ) ;
vec2 . copy ( worldVertex1 , worldVertex11 ) ;
// Get vector along the line
sub ( worldEdge , worldVertex1 , worldVertex0 ) ;
vec2 . normalize ( worldEdgeUnit , worldEdge ) ;
// Get tangent to the edge.
vec2 . rotate90cw ( worldTangent , worldEdgeUnit ) ;
// Check distance from the plane spanned by the edge vs the circle
sub ( dist , circleOffset , worldVertex0 ) ;
var d = dot ( dist , worldTangent ) ; // Distance from center of line to circle center
sub ( centerDist , worldVertex0 , lineOffset ) ;
sub ( lineToCircle , circleOffset , lineOffset ) ;
var radiusSum = circleRadius + lineRadius ;
if ( Math . abs ( d ) < radiusSum ) {
// Now project the circle onto the edge
vec2 . scale ( orthoDist , worldTangent , d ) ;
sub ( projectedPoint , circleOffset , orthoDist ) ;
// Add the missing line radius
vec2 . scale ( lineToCircleOrthoUnit , worldTangent , dot ( worldTangent , lineToCircle ) ) ;
vec2 . normalize ( lineToCircleOrthoUnit , lineToCircleOrthoUnit ) ;
vec2 . scale ( lineToCircleOrthoUnit , lineToCircleOrthoUnit , lineRadius ) ;
add ( projectedPoint , projectedPoint , lineToCircleOrthoUnit ) ;
// Check if the point is within the edge span
var pos = dot ( worldEdgeUnit , projectedPoint ) ;
var pos0 = dot ( worldEdgeUnit , worldVertex0 ) ;
var pos1 = dot ( worldEdgeUnit , worldVertex1 ) ;
if ( pos > pos0 && pos < pos1 ) {
// We got contact!
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( circleBody , lineBody , circleShape , lineShape ) ;
vec2 . scale ( c . normalA , orthoDist , - 1 ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
vec2 . scale ( c . contactPointA , c . normalA , circleRadius ) ;
add ( c . contactPointA , c . contactPointA , circleOffset ) ;
sub ( c . contactPointA , c . contactPointA , circleBody . position ) ;
sub ( c . contactPointB , projectedPoint , lineOffset ) ;
add ( c . contactPointB , c . contactPointB , lineOffset ) ;
sub ( c . contactPointB , c . contactPointB , lineBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
}
}
// Add corner
verts [ 0 ] = worldVertex0 ;
verts [ 1 ] = worldVertex1 ;
for ( var i = 0 ; i < verts . length ; i ++ ) {
var v = verts [ i ] ;
sub ( dist , v , circleOffset ) ;
if ( vec2 . squaredLength ( dist ) < Math . pow ( radiusSum , 2 ) ) {
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( circleBody , lineBody , circleShape , lineShape ) ;
vec2 . copy ( c . normalA , dist ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
// Vector from circle to contact point is the normal times the circle radius
vec2 . scale ( c . contactPointA , c . normalA , circleRadius ) ;
add ( c . contactPointA , c . contactPointA , circleOffset ) ;
sub ( c . contactPointA , c . contactPointA , circleBody . position ) ;
sub ( c . contactPointB , v , lineOffset ) ;
vec2 . scale ( lineEndToLineRadius , c . normalA , - lineRadius ) ;
add ( c . contactPointB , c . contactPointB , lineEndToLineRadius ) ;
add ( c . contactPointB , c . contactPointB , lineOffset ) ;
sub ( c . contactPointB , c . contactPointB , lineBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
}
}
return 0 ;
} ;
/ * *
* Circle / capsule Narrowphase
* @ method circleCapsule
* @ param { Body } bi
* @ param { Circle } si
* @ param { Array } xi
* @ param { Number } ai
* @ param { Body } bj
* @ param { Line } sj
* @ param { Array } xj
* @ param { Number } aj
* /
Narrowphase . prototype [ Shape . CIRCLE | Shape . CAPSULE ] =
Narrowphase . prototype . circleCapsule = function ( bi , si , xi , ai , bj , sj , xj , aj , justTest ) {
return this . circleLine ( bi , si , xi , ai , bj , sj , xj , aj , justTest , sj . radius ) ;
} ;
/ * *
* Circle / convex Narrowphase .
* @ method circleConvex
* @ param { Body } circleBody
* @ param { Circle } circleShape
* @ param { Array } circleOffset
* @ param { Number } circleAngle
* @ param { Body } convexBody
* @ param { Convex } convexShape
* @ param { Array } convexOffset
* @ param { Number } convexAngle
* @ param { Boolean } justTest
* @ param { Number } circleRadius
* /
Narrowphase . prototype [ Shape . CIRCLE | Shape . CONVEX ] =
Narrowphase . prototype [ Shape . CIRCLE | Shape . BOX ] =
Narrowphase . prototype . circleConvex = function (
circleBody ,
circleShape ,
circleOffset ,
circleAngle ,
convexBody ,
convexShape ,
convexOffset ,
convexAngle ,
justTest ,
circleRadius
) {
var circleRadius = typeof ( circleRadius ) === "number" ? circleRadius : circleShape . radius ;
var worldVertex0 = tmp1 ,
worldVertex1 = tmp2 ,
worldEdge = tmp3 ,
worldEdgeUnit = tmp4 ,
worldNormal = tmp5 ,
centerDist = tmp6 ,
convexToCircle = tmp7 ,
orthoDist = tmp8 ,
projectedPoint = tmp9 ,
dist = tmp10 ,
worldVertex = tmp11 ,
closestEdge = - 1 ,
closestEdgeDistance = null ,
closestEdgeOrthoDist = tmp12 ,
closestEdgeProjectedPoint = tmp13 ,
candidate = tmp14 ,
candidateDist = tmp15 ,
minCandidate = tmp16 ,
found = false ,
minCandidateDistance = Number . MAX _VALUE ;
var numReported = 0 ;
// New algorithm:
// 1. Check so center of circle is not inside the polygon. If it is, this wont work...
// 2. For each edge
// 2. 1. Get point on circle that is closest to the edge (scale normal with -radius)
// 2. 2. Check if point is inside.
var verts = convexShape . vertices ;
// Check all edges first
for ( var i = 0 ; i !== verts . length + 1 ; i ++ ) {
var v0 = verts [ i % verts . length ] ,
v1 = verts [ ( i + 1 ) % verts . length ] ;
vec2 . rotate ( worldVertex0 , v0 , convexAngle ) ;
vec2 . rotate ( worldVertex1 , v1 , convexAngle ) ;
add ( worldVertex0 , worldVertex0 , convexOffset ) ;
add ( worldVertex1 , worldVertex1 , convexOffset ) ;
sub ( worldEdge , worldVertex1 , worldVertex0 ) ;
vec2 . normalize ( worldEdgeUnit , worldEdge ) ;
// Get tangent to the edge. Points out of the Convex
vec2 . rotate90cw ( worldNormal , worldEdgeUnit ) ;
// Get point on circle, closest to the polygon
vec2 . scale ( candidate , worldNormal , - circleShape . radius ) ;
add ( candidate , candidate , circleOffset ) ;
if ( pointInConvex ( candidate , convexShape , convexOffset , convexAngle ) ) {
vec2 . sub ( candidateDist , worldVertex0 , candidate ) ;
var candidateDistance = Math . abs ( vec2 . dot ( candidateDist , worldNormal ) ) ;
if ( candidateDistance < minCandidateDistance ) {
vec2 . copy ( minCandidate , candidate ) ;
minCandidateDistance = candidateDistance ;
vec2 . scale ( closestEdgeProjectedPoint , worldNormal , candidateDistance ) ;
vec2 . add ( closestEdgeProjectedPoint , closestEdgeProjectedPoint , candidate ) ;
found = true ;
}
}
}
if ( found ) {
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( circleBody , convexBody , circleShape , convexShape ) ;
vec2 . sub ( c . normalA , minCandidate , circleOffset ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
vec2 . scale ( c . contactPointA , c . normalA , circleRadius ) ;
add ( c . contactPointA , c . contactPointA , circleOffset ) ;
sub ( c . contactPointA , c . contactPointA , circleBody . position ) ;
sub ( c . contactPointB , closestEdgeProjectedPoint , convexOffset ) ;
add ( c . contactPointB , c . contactPointB , convexOffset ) ;
sub ( c . contactPointB , c . contactPointB , convexBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
}
// Check all vertices
if ( circleRadius > 0 ) {
for ( var i = 0 ; i < verts . length ; i ++ ) {
var localVertex = verts [ i ] ;
vec2 . rotate ( worldVertex , localVertex , convexAngle ) ;
add ( worldVertex , worldVertex , convexOffset ) ;
sub ( dist , worldVertex , circleOffset ) ;
if ( vec2 . squaredLength ( dist ) < Math . pow ( circleRadius , 2 ) ) {
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( circleBody , convexBody , circleShape , convexShape ) ;
vec2 . copy ( c . normalA , dist ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
// Vector from circle to contact point is the normal times the circle radius
vec2 . scale ( c . contactPointA , c . normalA , circleRadius ) ;
add ( c . contactPointA , c . contactPointA , circleOffset ) ;
sub ( c . contactPointA , c . contactPointA , circleBody . position ) ;
sub ( c . contactPointB , worldVertex , convexOffset ) ;
add ( c . contactPointB , c . contactPointB , convexOffset ) ;
sub ( c . contactPointB , c . contactPointB , convexBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
}
}
}
return 0 ;
} ;
var pic _worldVertex0 = vec2 . create ( ) ,
pic _worldVertex1 = vec2 . create ( ) ,
pic _r0 = vec2 . create ( ) ,
pic _r1 = vec2 . create ( ) ;
/ *
* Check if a point is in a polygon
* /
function pointInConvex ( worldPoint , convexShape , convexOffset , convexAngle ) {
var worldVertex0 = pic _worldVertex0 ,
worldVertex1 = pic _worldVertex1 ,
r0 = pic _r0 ,
r1 = pic _r1 ,
point = worldPoint ,
verts = convexShape . vertices ,
lastCross = null ;
for ( var i = 0 ; i !== verts . length + 1 ; i ++ ) {
var v0 = verts [ i % verts . length ] ,
v1 = verts [ ( i + 1 ) % verts . length ] ;
// Transform vertices to world
// @todo The point should be transformed to local coordinates in the convex, no need to transform each vertex
vec2 . rotate ( worldVertex0 , v0 , convexAngle ) ;
vec2 . rotate ( worldVertex1 , v1 , convexAngle ) ;
add ( worldVertex0 , worldVertex0 , convexOffset ) ;
add ( worldVertex1 , worldVertex1 , convexOffset ) ;
sub ( r0 , worldVertex0 , point ) ;
sub ( r1 , worldVertex1 , point ) ;
var cross = vec2 . crossLength ( r0 , r1 ) ;
if ( lastCross === null ) {
lastCross = cross ;
}
// If we got a different sign of the distance vector, the point is out of the polygon
if ( cross * lastCross <= 0 ) {
return false ;
}
lastCross = cross ;
}
return true ;
}
/ * *
* Particle / convex Narrowphase
* @ method particleConvex
* @ param { Body } particleBody
* @ param { Particle } particleShape
* @ param { Array } particleOffset
* @ param { Number } particleAngle
* @ param { Body } convexBody
* @ param { Convex } convexShape
* @ param { Array } convexOffset
* @ param { Number } convexAngle
* @ param { Boolean } justTest
* @ todo use pointInConvex and code more similar to circleConvex
* @ todo don ' t transform each vertex , but transform the particle position to convex - local instead
* /
Narrowphase . prototype [ Shape . PARTICLE | Shape . CONVEX ] =
Narrowphase . prototype [ Shape . PARTICLE | Shape . BOX ] =
Narrowphase . prototype . particleConvex = function (
particleBody ,
particleShape ,
particleOffset ,
particleAngle ,
convexBody ,
convexShape ,
convexOffset ,
convexAngle ,
justTest
) {
var worldVertex0 = tmp1 ,
worldVertex1 = tmp2 ,
worldEdge = tmp3 ,
worldEdgeUnit = tmp4 ,
worldTangent = tmp5 ,
centerDist = tmp6 ,
convexToparticle = tmp7 ,
orthoDist = tmp8 ,
projectedPoint = tmp9 ,
dist = tmp10 ,
worldVertex = tmp11 ,
closestEdge = - 1 ,
closestEdgeDistance = null ,
closestEdgeOrthoDist = tmp12 ,
closestEdgeProjectedPoint = tmp13 ,
r0 = tmp14 , // vector from particle to vertex0
r1 = tmp15 ,
localPoint = tmp16 ,
candidateDist = tmp17 ,
minEdgeNormal = tmp18 ,
minCandidateDistance = Number . MAX _VALUE ;
var numReported = 0 ,
found = false ,
verts = convexShape . vertices ;
// Check if the particle is in the polygon at all
if ( ! pointInConvex ( particleOffset , convexShape , convexOffset , convexAngle ) ) {
return 0 ;
}
if ( justTest ) {
return true ;
}
// Check edges first
var lastCross = null ;
for ( var i = 0 ; i !== verts . length + 1 ; i ++ ) {
var v0 = verts [ i % verts . length ] ,
v1 = verts [ ( i + 1 ) % verts . length ] ;
// Transform vertices to world
vec2 . rotate ( worldVertex0 , v0 , convexAngle ) ;
vec2 . rotate ( worldVertex1 , v1 , convexAngle ) ;
add ( worldVertex0 , worldVertex0 , convexOffset ) ;
add ( worldVertex1 , worldVertex1 , convexOffset ) ;
// Get world edge
sub ( worldEdge , worldVertex1 , worldVertex0 ) ;
vec2 . normalize ( worldEdgeUnit , worldEdge ) ;
// Get tangent to the edge. Points out of the Convex
vec2 . rotate90cw ( worldTangent , worldEdgeUnit ) ;
// Check distance from the infinite line (spanned by the edge) to the particle
sub ( dist , particleOffset , worldVertex0 ) ;
var d = dot ( dist , worldTangent ) ;
sub ( centerDist , worldVertex0 , convexOffset ) ;
sub ( convexToparticle , particleOffset , convexOffset ) ;
vec2 . sub ( candidateDist , worldVertex0 , particleOffset ) ;
var candidateDistance = Math . abs ( vec2 . dot ( candidateDist , worldTangent ) ) ;
if ( candidateDistance < minCandidateDistance ) {
minCandidateDistance = candidateDistance ;
vec2 . scale ( closestEdgeProjectedPoint , worldTangent , candidateDistance ) ;
vec2 . add ( closestEdgeProjectedPoint , closestEdgeProjectedPoint , particleOffset ) ;
vec2 . copy ( minEdgeNormal , worldTangent ) ;
found = true ;
}
}
if ( found ) {
var c = this . createContactEquation ( particleBody , convexBody , particleShape , convexShape ) ;
vec2 . scale ( c . normalA , minEdgeNormal , - 1 ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
// Particle has no extent to the contact point
vec2 . set ( c . contactPointA , 0 , 0 ) ;
add ( c . contactPointA , c . contactPointA , particleOffset ) ;
sub ( c . contactPointA , c . contactPointA , particleBody . position ) ;
// From convex center to point
sub ( c . contactPointB , closestEdgeProjectedPoint , convexOffset ) ;
add ( c . contactPointB , c . contactPointB , convexOffset ) ;
sub ( c . contactPointB , c . contactPointB , convexBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
}
return 0 ;
} ;
/ * *
* Circle / circle Narrowphase
* @ method circleCircle
* @ param { Body } bodyA
* @ param { Circle } shapeA
* @ param { Array } offsetA
* @ param { Number } angleA
* @ param { Body } bodyB
* @ param { Circle } shapeB
* @ param { Array } offsetB
* @ param { Number } angleB
* @ param { Boolean } justTest
* @ param { Number } [ radiusA ] Optional radius to use for shapeA
* @ param { Number } [ radiusB ] Optional radius to use for shapeB
* /
Narrowphase . prototype [ Shape . CIRCLE ] =
Narrowphase . prototype . circleCircle = function (
bodyA ,
shapeA ,
offsetA ,
angleA ,
bodyB ,
shapeB ,
offsetB ,
angleB ,
justTest ,
radiusA ,
radiusB
) {
var dist = tmp1 ,
radiusA = radiusA || shapeA . radius ,
radiusB = radiusB || shapeB . radius ;
sub ( dist , offsetA , offsetB ) ;
var r = radiusA + radiusB ;
if ( vec2 . squaredLength ( dist ) > Math . pow ( r , 2 ) ) {
return 0 ;
}
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( bodyA , bodyB , shapeA , shapeB ) ;
sub ( c . normalA , offsetB , offsetA ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
vec2 . scale ( c . contactPointA , c . normalA , radiusA ) ;
vec2 . scale ( c . contactPointB , c . normalA , - radiusB ) ;
add ( c . contactPointA , c . contactPointA , offsetA ) ;
sub ( c . contactPointA , c . contactPointA , bodyA . position ) ;
add ( c . contactPointB , c . contactPointB , offsetB ) ;
sub ( c . contactPointB , c . contactPointB , bodyB . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
} ;
/ * *
* Plane / Convex Narrowphase
* @ method planeConvex
* @ param { Body } planeBody
* @ param { Plane } planeShape
* @ param { Array } planeOffset
* @ param { Number } planeAngle
* @ param { Body } convexBody
* @ param { Convex } convexShape
* @ param { Array } convexOffset
* @ param { Number } convexAngle
* @ param { Boolean } justTest
* /
Narrowphase . prototype [ Shape . PLANE | Shape . CONVEX ] =
Narrowphase . prototype [ Shape . PLANE | Shape . BOX ] =
Narrowphase . prototype . planeConvex = function (
planeBody ,
planeShape ,
planeOffset ,
planeAngle ,
convexBody ,
convexShape ,
convexOffset ,
convexAngle ,
justTest
) {
var worldVertex = tmp1 ,
worldNormal = tmp2 ,
dist = tmp3 ;
var numReported = 0 ;
vec2 . rotate ( worldNormal , yAxis , planeAngle ) ;
for ( var i = 0 ; i !== convexShape . vertices . length ; i ++ ) {
var v = convexShape . vertices [ i ] ;
vec2 . rotate ( worldVertex , v , convexAngle ) ;
add ( worldVertex , worldVertex , convexOffset ) ;
sub ( dist , worldVertex , planeOffset ) ;
if ( dot ( dist , worldNormal ) <= 0 ) {
if ( justTest ) {
return true ;
}
// Found vertex
numReported ++ ;
var c = this . createContactEquation ( planeBody , convexBody , planeShape , convexShape ) ;
sub ( dist , worldVertex , planeOffset ) ;
vec2 . copy ( c . normalA , worldNormal ) ;
var d = dot ( dist , c . normalA ) ;
vec2 . scale ( dist , c . normalA , d ) ;
// rj is from convex center to contact
sub ( c . contactPointB , worldVertex , convexBody . position ) ;
// ri is from plane center to contact
sub ( c . contactPointA , worldVertex , dist ) ;
sub ( c . contactPointA , c . contactPointA , planeBody . position ) ;
this . contactEquations . push ( c ) ;
if ( ! this . enableFrictionReduction ) {
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
}
}
}
if ( this . enableFrictionReduction ) {
if ( this . enableFriction && numReported ) {
this . frictionEquations . push ( this . createFrictionFromAverage ( numReported ) ) ;
}
}
return numReported ;
} ;
/ * *
* Narrowphase for particle vs plane
* @ method particlePlane
* @ param { Body } particleBody
* @ param { Particle } particleShape
* @ param { Array } particleOffset
* @ param { Number } particleAngle
* @ param { Body } planeBody
* @ param { Plane } planeShape
* @ param { Array } planeOffset
* @ param { Number } planeAngle
* @ param { Boolean } justTest
* /
Narrowphase . prototype [ Shape . PARTICLE | Shape . PLANE ] =
Narrowphase . prototype . particlePlane = function (
particleBody ,
particleShape ,
particleOffset ,
particleAngle ,
planeBody ,
planeShape ,
planeOffset ,
planeAngle ,
justTest
) {
var dist = tmp1 ,
worldNormal = tmp2 ;
planeAngle = planeAngle || 0 ;
sub ( dist , particleOffset , planeOffset ) ;
vec2 . rotate ( worldNormal , yAxis , planeAngle ) ;
var d = dot ( dist , worldNormal ) ;
if ( d > 0 ) {
return 0 ;
}
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( planeBody , particleBody , planeShape , particleShape ) ;
vec2 . copy ( c . normalA , worldNormal ) ;
vec2 . scale ( dist , c . normalA , d ) ;
// dist is now the distance vector in the normal direction
// ri is the particle position projected down onto the plane, from the plane center
sub ( c . contactPointA , particleOffset , dist ) ;
sub ( c . contactPointA , c . contactPointA , planeBody . position ) ;
// rj is from the body center to the particle center
sub ( c . contactPointB , particleOffset , particleBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
} ;
/ * *
* Circle / Particle Narrowphase
* @ method circleParticle
* @ param { Body } circleBody
* @ param { Circle } circleShape
* @ param { Array } circleOffset
* @ param { Number } circleAngle
* @ param { Body } particleBody
* @ param { Particle } particleShape
* @ param { Array } particleOffset
* @ param { Number } particleAngle
* @ param { Boolean } justTest
* /
Narrowphase . prototype [ Shape . CIRCLE | Shape . PARTICLE ] =
Narrowphase . prototype . circleParticle = function (
circleBody ,
circleShape ,
circleOffset ,
circleAngle ,
particleBody ,
particleShape ,
particleOffset ,
particleAngle ,
justTest
) {
var dist = tmp1 ;
sub ( dist , particleOffset , circleOffset ) ;
if ( vec2 . squaredLength ( dist ) > Math . pow ( circleShape . radius , 2 ) ) {
return 0 ;
}
if ( justTest ) {
return true ;
}
var c = this . createContactEquation ( circleBody , particleBody , circleShape , particleShape ) ;
vec2 . copy ( c . normalA , dist ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
// Vector from circle to contact point is the normal times the circle radius
vec2 . scale ( c . contactPointA , c . normalA , circleShape . radius ) ;
add ( c . contactPointA , c . contactPointA , circleOffset ) ;
sub ( c . contactPointA , c . contactPointA , circleBody . position ) ;
// Vector from particle center to contact point is zero
sub ( c . contactPointB , particleOffset , particleBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
return 1 ;
} ;
var planeCapsule _tmpCircle = new Circle ( { radius : 1 } ) ,
planeCapsule _tmp1 = vec2 . create ( ) ,
planeCapsule _tmp2 = vec2 . create ( ) ,
planeCapsule _tmp3 = vec2 . create ( ) ;
/ * *
* @ method planeCapsule
* @ param { Body } planeBody
* @ param { Circle } planeShape
* @ param { Array } planeOffset
* @ param { Number } planeAngle
* @ param { Body } capsuleBody
* @ param { Particle } capsuleShape
* @ param { Array } capsuleOffset
* @ param { Number } capsuleAngle
* @ param { Boolean } justTest
* /
Narrowphase . prototype [ Shape . PLANE | Shape . CAPSULE ] =
Narrowphase . prototype . planeCapsule = function (
planeBody ,
planeShape ,
planeOffset ,
planeAngle ,
capsuleBody ,
capsuleShape ,
capsuleOffset ,
capsuleAngle ,
justTest
) {
var end1 = planeCapsule _tmp1 ,
end2 = planeCapsule _tmp2 ,
circle = planeCapsule _tmpCircle ,
dst = planeCapsule _tmp3 ;
// Compute world end positions
vec2 . set ( end1 , - capsuleShape . length / 2 , 0 ) ;
vec2 . rotate ( end1 , end1 , capsuleAngle ) ;
add ( end1 , end1 , capsuleOffset ) ;
vec2 . set ( end2 , capsuleShape . length / 2 , 0 ) ;
vec2 . rotate ( end2 , end2 , capsuleAngle ) ;
add ( end2 , end2 , capsuleOffset ) ;
circle . radius = capsuleShape . radius ;
var enableFrictionBefore ;
// Temporarily turn off friction
if ( this . enableFrictionReduction ) {
enableFrictionBefore = this . enableFriction ;
this . enableFriction = false ;
}
// Do Narrowphase as two circles
var numContacts1 = this . circlePlane ( capsuleBody , circle , end1 , 0 , planeBody , planeShape , planeOffset , planeAngle , justTest ) ,
numContacts2 = this . circlePlane ( capsuleBody , circle , end2 , 0 , planeBody , planeShape , planeOffset , planeAngle , justTest ) ;
// Restore friction
if ( this . enableFrictionReduction ) {
this . enableFriction = enableFrictionBefore ;
}
if ( justTest ) {
return numContacts1 || numContacts2 ;
} else {
var numTotal = numContacts1 + numContacts2 ;
if ( this . enableFrictionReduction ) {
if ( numTotal ) {
this . frictionEquations . push ( this . createFrictionFromAverage ( numTotal ) ) ;
}
}
return numTotal ;
}
} ;
/ * *
* Creates ContactEquations and FrictionEquations for a collision .
* @ method circlePlane
* @ param { Body } bi The first body that should be connected to the equations .
* @ param { Circle } si The circle shape participating in the collision .
* @ param { Array } xi Extra offset to take into account for the Shape , in addition to the one in circleBody . position . Will * not * be rotated by circleBody . angle ( maybe it should , for sake of homogenity ? ) . Set to null if none .
* @ param { Body } bj The second body that should be connected to the equations .
* @ param { Plane } sj The Plane shape that is participating
* @ param { Array } xj Extra offset for the plane shape .
* @ param { Number } aj Extra angle to apply to the plane
* /
Narrowphase . prototype [ Shape . CIRCLE | Shape . PLANE ] =
Narrowphase . prototype . circlePlane = function ( bi , si , xi , ai , bj , sj , xj , aj , justTest ) {
var circleBody = bi ,
circleShape = si ,
circleOffset = xi , // Offset from body center, rotated!
planeBody = bj ,
shapeB = sj ,
planeOffset = xj ,
planeAngle = aj ;
planeAngle = planeAngle || 0 ;
// Vector from plane to circle
var planeToCircle = tmp1 ,
worldNormal = tmp2 ,
temp = tmp3 ;
sub ( planeToCircle , circleOffset , planeOffset ) ;
// World plane normal
vec2 . rotate ( worldNormal , yAxis , planeAngle ) ;
// Normal direction distance
var d = dot ( worldNormal , planeToCircle ) ;
if ( d > circleShape . radius ) {
return 0 ; // No overlap. Abort.
}
if ( justTest ) {
return true ;
}
// Create contact
var contact = this . createContactEquation ( planeBody , circleBody , sj , si ) ;
// ni is the plane world normal
vec2 . copy ( contact . normalA , worldNormal ) ;
// rj is the vector from circle center to the contact point
vec2 . scale ( contact . contactPointB , contact . normalA , - circleShape . radius ) ;
add ( contact . contactPointB , contact . contactPointB , circleOffset ) ;
sub ( contact . contactPointB , contact . contactPointB , circleBody . position ) ;
// ri is the distance from plane center to contact.
vec2 . scale ( temp , contact . normalA , d ) ;
sub ( contact . contactPointA , planeToCircle , temp ) ; // Subtract normal distance vector from the distance vector
add ( contact . contactPointA , contact . contactPointA , planeOffset ) ;
sub ( contact . contactPointA , contact . contactPointA , planeBody . position ) ;
this . contactEquations . push ( contact ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( contact ) ) ;
}
return 1 ;
} ;
/ * *
* Convex / convex Narrowphase . See < a href = "http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/" > this article < / a > f o r m o r e i n f o .
* @ method convexConvex
* @ param { Body } bi
* @ param { Convex } si
* @ param { Array } xi
* @ param { Number } ai
* @ param { Body } bj
* @ param { Convex } sj
* @ param { Array } xj
* @ param { Number } aj
* /
Narrowphase . prototype [ Shape . CONVEX ] =
Narrowphase . prototype [ Shape . CONVEX | Shape . BOX ] =
Narrowphase . prototype [ Shape . BOX ] =
Narrowphase . prototype . convexConvex = function ( bi , si , xi , ai , bj , sj , xj , aj , justTest , precision ) {
var sepAxis = tmp1 ,
worldPoint = tmp2 ,
worldPoint0 = tmp3 ,
worldPoint1 = tmp4 ,
worldEdge = tmp5 ,
projected = tmp6 ,
penetrationVec = tmp7 ,
dist = tmp8 ,
worldNormal = tmp9 ,
numContacts = 0 ,
precision = typeof ( precision ) === 'number' ? precision : 0 ;
var found = Narrowphase . findSeparatingAxis ( si , xi , ai , sj , xj , aj , sepAxis ) ;
if ( ! found ) {
return 0 ;
}
// Make sure the separating axis is directed from shape i to shape j
sub ( dist , xj , xi ) ;
if ( dot ( sepAxis , dist ) > 0 ) {
vec2 . scale ( sepAxis , sepAxis , - 1 ) ;
}
// Find edges with normals closest to the separating axis
var closestEdge1 = Narrowphase . getClosestEdge ( si , ai , sepAxis , true ) , // Flipped axis
closestEdge2 = Narrowphase . getClosestEdge ( sj , aj , sepAxis ) ;
if ( closestEdge1 === - 1 || closestEdge2 === - 1 ) {
return 0 ;
}
// Loop over the shapes
for ( var k = 0 ; k < 2 ; k ++ ) {
var closestEdgeA = closestEdge1 ,
closestEdgeB = closestEdge2 ,
shapeA = si , shapeB = sj ,
offsetA = xi , offsetB = xj ,
angleA = ai , angleB = aj ,
bodyA = bi , bodyB = bj ;
if ( k === 0 ) {
// Swap!
var tmp ;
tmp = closestEdgeA ;
closestEdgeA = closestEdgeB ;
closestEdgeB = tmp ;
tmp = shapeA ;
shapeA = shapeB ;
shapeB = tmp ;
tmp = offsetA ;
offsetA = offsetB ;
offsetB = tmp ;
tmp = angleA ;
angleA = angleB ;
angleB = tmp ;
tmp = bodyA ;
bodyA = bodyB ;
bodyB = tmp ;
}
// Loop over 2 points in convex B
for ( var j = closestEdgeB ; j < closestEdgeB + 2 ; j ++ ) {
// Get world point
var v = shapeB . vertices [ ( j + shapeB . vertices . length ) % shapeB . vertices . length ] ;
vec2 . rotate ( worldPoint , v , angleB ) ;
add ( worldPoint , worldPoint , offsetB ) ;
var insideNumEdges = 0 ;
// Loop over the 3 closest edges in convex A
for ( var i = closestEdgeA - 1 ; i < closestEdgeA + 2 ; i ++ ) {
var v0 = shapeA . vertices [ ( i + shapeA . vertices . length ) % shapeA . vertices . length ] ,
v1 = shapeA . vertices [ ( i + 1 + shapeA . vertices . length ) % shapeA . vertices . length ] ;
// Construct the edge
vec2 . rotate ( worldPoint0 , v0 , angleA ) ;
vec2 . rotate ( worldPoint1 , v1 , angleA ) ;
add ( worldPoint0 , worldPoint0 , offsetA ) ;
add ( worldPoint1 , worldPoint1 , offsetA ) ;
sub ( worldEdge , worldPoint1 , worldPoint0 ) ;
vec2 . rotate90cw ( worldNormal , worldEdge ) ; // Normal points out of convex 1
vec2 . normalize ( worldNormal , worldNormal ) ;
sub ( dist , worldPoint , worldPoint0 ) ;
var d = dot ( worldNormal , dist ) ;
if ( ( i === closestEdgeA && d <= precision ) || ( i !== closestEdgeA && d <= 0 ) ) {
insideNumEdges ++ ;
}
}
if ( insideNumEdges >= 3 ) {
if ( justTest ) {
return true ;
}
// worldPoint was on the "inside" side of each of the 3 checked edges.
// Project it to the center edge and use the projection direction as normal
// Create contact
var c = this . createContactEquation ( bodyA , bodyB , shapeA , shapeB ) ;
numContacts ++ ;
// Get center edge from body A
var v0 = shapeA . vertices [ ( closestEdgeA ) % shapeA . vertices . length ] ,
v1 = shapeA . vertices [ ( closestEdgeA + 1 ) % shapeA . vertices . length ] ;
// Construct the edge
vec2 . rotate ( worldPoint0 , v0 , angleA ) ;
vec2 . rotate ( worldPoint1 , v1 , angleA ) ;
add ( worldPoint0 , worldPoint0 , offsetA ) ;
add ( worldPoint1 , worldPoint1 , offsetA ) ;
sub ( worldEdge , worldPoint1 , worldPoint0 ) ;
vec2 . rotate90cw ( c . normalA , worldEdge ) ; // Normal points out of convex A
vec2 . normalize ( c . normalA , c . normalA ) ;
sub ( dist , worldPoint , worldPoint0 ) ; // From edge point to the penetrating point
var d = dot ( c . normalA , dist ) ; // Penetration
vec2 . scale ( penetrationVec , c . normalA , d ) ; // Vector penetration
sub ( c . contactPointA , worldPoint , offsetA ) ;
sub ( c . contactPointA , c . contactPointA , penetrationVec ) ;
add ( c . contactPointA , c . contactPointA , offsetA ) ;
sub ( c . contactPointA , c . contactPointA , bodyA . position ) ;
sub ( c . contactPointB , worldPoint , offsetB ) ;
add ( c . contactPointB , c . contactPointB , offsetB ) ;
sub ( c . contactPointB , c . contactPointB , bodyB . position ) ;
this . contactEquations . push ( c ) ;
// Todo reduce to 1 friction equation if we have 2 contact points
if ( ! this . enableFrictionReduction ) {
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
}
}
}
}
if ( this . enableFrictionReduction ) {
if ( this . enableFriction && numContacts ) {
this . frictionEquations . push ( this . createFrictionFromAverage ( numContacts ) ) ;
}
}
return numContacts ;
} ;
// .projectConvex is called by other functions, need local tmp vectors
var pcoa _tmp1 = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Project a Convex onto a world - oriented axis
* @ method projectConvexOntoAxis
* @ static
* @ param { Convex } convexShape
* @ param { Array } convexOffset
* @ param { Number } convexAngle
* @ param { Array } worldAxis
* @ param { Array } result
* /
Narrowphase . projectConvexOntoAxis = function ( convexShape , convexOffset , convexAngle , worldAxis , result ) {
var max = null ,
min = null ,
v ,
value ,
localAxis = pcoa _tmp1 ;
// Convert the axis to local coords of the body
vec2 . rotate ( localAxis , worldAxis , - convexAngle ) ;
// Get projected position of all vertices
for ( var i = 0 ; i < convexShape . vertices . length ; i ++ ) {
v = convexShape . vertices [ i ] ;
value = dot ( v , localAxis ) ;
if ( max === null || value > max ) {
max = value ;
}
if ( min === null || value < min ) {
min = value ;
}
}
if ( min > max ) {
var t = min ;
min = max ;
max = t ;
}
// Project the position of the body onto the axis - need to add this to the result
var offset = dot ( convexOffset , worldAxis ) ;
vec2 . set ( result , min + offset , max + offset ) ;
} ;
// .findSeparatingAxis is called by other functions, need local tmp vectors
var fsa _tmp1 = vec2 . fromValues ( 0 , 0 )
, fsa _tmp2 = vec2 . fromValues ( 0 , 0 )
, fsa _tmp3 = vec2 . fromValues ( 0 , 0 )
, fsa _tmp4 = vec2 . fromValues ( 0 , 0 )
, fsa _tmp5 = vec2 . fromValues ( 0 , 0 )
, fsa _tmp6 = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Find a separating axis between the shapes , that maximizes the separating distance between them .
* @ method findSeparatingAxis
* @ static
* @ param { Convex } c1
* @ param { Array } offset1
* @ param { Number } angle1
* @ param { Convex } c2
* @ param { Array } offset2
* @ param { Number } angle2
* @ param { Array } sepAxis The resulting axis
* @ return { Boolean } Whether the axis could be found .
* /
Narrowphase . findSeparatingAxis = function ( c1 , offset1 , angle1 , c2 , offset2 , angle2 , sepAxis ) {
var maxDist = null ,
overlap = false ,
found = false ,
edge = fsa _tmp1 ,
worldPoint0 = fsa _tmp2 ,
worldPoint1 = fsa _tmp3 ,
normal = fsa _tmp4 ,
span1 = fsa _tmp5 ,
span2 = fsa _tmp6 ;
if ( c1 instanceof Box && c2 instanceof Box ) {
for ( var j = 0 ; j !== 2 ; j ++ ) {
var c = c1 ,
angle = angle1 ;
if ( j === 1 ) {
c = c2 ;
angle = angle2 ;
}
for ( var i = 0 ; i !== 2 ; i ++ ) {
// Get the world edge
if ( i === 0 ) {
vec2 . set ( normal , 0 , 1 ) ;
} else if ( i === 1 ) {
vec2 . set ( normal , 1 , 0 ) ;
}
if ( angle !== 0 ) {
vec2 . rotate ( normal , normal , angle ) ;
}
// Project hulls onto that normal
Narrowphase . projectConvexOntoAxis ( c1 , offset1 , angle1 , normal , span1 ) ;
Narrowphase . projectConvexOntoAxis ( c2 , offset2 , angle2 , normal , span2 ) ;
// Order by span position
var a = span1 ,
b = span2 ,
swapped = false ;
if ( span1 [ 0 ] > span2 [ 0 ] ) {
b = span1 ;
a = span2 ;
swapped = true ;
}
// Get separating distance
var dist = b [ 0 ] - a [ 1 ] ;
overlap = ( dist <= 0 ) ;
if ( maxDist === null || dist > maxDist ) {
vec2 . copy ( sepAxis , normal ) ;
maxDist = dist ;
found = overlap ;
}
}
}
} else {
for ( var j = 0 ; j !== 2 ; j ++ ) {
var c = c1 ,
angle = angle1 ;
if ( j === 1 ) {
c = c2 ;
angle = angle2 ;
}
for ( var i = 0 ; i !== c . vertices . length ; i ++ ) {
// Get the world edge
vec2 . rotate ( worldPoint0 , c . vertices [ i ] , angle ) ;
vec2 . rotate ( worldPoint1 , c . vertices [ ( i + 1 ) % c . vertices . length ] , angle ) ;
sub ( edge , worldPoint1 , worldPoint0 ) ;
// Get normal - just rotate 90 degrees since vertices are given in CCW
vec2 . rotate90cw ( normal , edge ) ;
vec2 . normalize ( normal , normal ) ;
// Project hulls onto that normal
Narrowphase . projectConvexOntoAxis ( c1 , offset1 , angle1 , normal , span1 ) ;
Narrowphase . projectConvexOntoAxis ( c2 , offset2 , angle2 , normal , span2 ) ;
// Order by span position
var a = span1 ,
b = span2 ,
swapped = false ;
if ( span1 [ 0 ] > span2 [ 0 ] ) {
b = span1 ;
a = span2 ;
swapped = true ;
}
// Get separating distance
var dist = b [ 0 ] - a [ 1 ] ;
overlap = ( dist <= 0 ) ;
if ( maxDist === null || dist > maxDist ) {
vec2 . copy ( sepAxis , normal ) ;
maxDist = dist ;
found = overlap ;
}
}
}
}
/ *
// Needs to be tested some more
for ( var j = 0 ; j !== 2 ; j ++ ) {
var c = c1 ,
angle = angle1 ;
if ( j === 1 ) {
c = c2 ;
angle = angle2 ;
}
for ( var i = 0 ; i !== c . axes . length ; i ++ ) {
var normal = c . axes [ i ] ;
// Project hulls onto that normal
Narrowphase . projectConvexOntoAxis ( c1 , offset1 , angle1 , normal , span1 ) ;
Narrowphase . projectConvexOntoAxis ( c2 , offset2 , angle2 , normal , span2 ) ;
// Order by span position
var a = span1 ,
b = span2 ,
swapped = false ;
if ( span1 [ 0 ] > span2 [ 0 ] ) {
b = span1 ;
a = span2 ;
swapped = true ;
}
// Get separating distance
var dist = b [ 0 ] - a [ 1 ] ;
overlap = ( dist <= Narrowphase . convexPrecision ) ;
if ( maxDist === null || dist > maxDist ) {
vec2 . copy ( sepAxis , normal ) ;
maxDist = dist ;
found = overlap ;
}
}
}
* /
return found ;
} ;
// .getClosestEdge is called by other functions, need local tmp vectors
var gce _tmp1 = vec2 . fromValues ( 0 , 0 )
, gce _tmp2 = vec2 . fromValues ( 0 , 0 )
, gce _tmp3 = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Get the edge that has a normal closest to an axis .
* @ method getClosestEdge
* @ static
* @ param { Convex } c
* @ param { Number } angle
* @ param { Array } axis
* @ param { Boolean } flip
* @ return { Number } Index of the edge that is closest . This index and the next spans the resulting edge . Returns - 1 if failed .
* /
Narrowphase . getClosestEdge = function ( c , angle , axis , flip ) {
var localAxis = gce _tmp1 ,
edge = gce _tmp2 ,
normal = gce _tmp3 ;
// Convert the axis to local coords of the body
vec2 . rotate ( localAxis , axis , - angle ) ;
if ( flip ) {
vec2 . scale ( localAxis , localAxis , - 1 ) ;
}
var closestEdge = - 1 ,
N = c . vertices . length ,
maxDot = - 1 ;
for ( var i = 0 ; i !== N ; i ++ ) {
// Get the edge
sub ( edge , c . vertices [ ( i + 1 ) % N ] , c . vertices [ i % N ] ) ;
// Get normal - just rotate 90 degrees since vertices are given in CCW
vec2 . rotate90cw ( normal , edge ) ;
vec2 . normalize ( normal , normal ) ;
var d = dot ( normal , localAxis ) ;
if ( closestEdge === - 1 || d > maxDot ) {
closestEdge = i % N ;
maxDot = d ;
}
}
return closestEdge ;
} ;
var circleHeightfield _candidate = vec2 . create ( ) ,
circleHeightfield _dist = vec2 . create ( ) ,
circleHeightfield _v0 = vec2 . create ( ) ,
circleHeightfield _v1 = vec2 . create ( ) ,
circleHeightfield _minCandidate = vec2 . create ( ) ,
circleHeightfield _worldNormal = vec2 . create ( ) ,
circleHeightfield _minCandidateNormal = vec2 . create ( ) ;
/ * *
* @ method circleHeightfield
* @ param { Body } bi
* @ param { Circle } si
* @ param { Array } xi
* @ param { Body } bj
* @ param { Heightfield } sj
* @ param { Array } xj
* @ param { Number } aj
* /
Narrowphase . prototype [ Shape . CIRCLE | Shape . HEIGHTFIELD ] =
Narrowphase . prototype . circleHeightfield = function ( circleBody , circleShape , circlePos , circleAngle ,
hfBody , hfShape , hfPos , hfAngle , justTest , radius ) {
var data = hfShape . heights ,
radius = radius || circleShape . radius ,
w = hfShape . elementWidth ,
dist = circleHeightfield _dist ,
candidate = circleHeightfield _candidate ,
minCandidate = circleHeightfield _minCandidate ,
minCandidateNormal = circleHeightfield _minCandidateNormal ,
worldNormal = circleHeightfield _worldNormal ,
v0 = circleHeightfield _v0 ,
v1 = circleHeightfield _v1 ;
// Get the index of the points to test against
var idxA = Math . floor ( ( circlePos [ 0 ] - radius - hfPos [ 0 ] ) / w ) ,
idxB = Math . ceil ( ( circlePos [ 0 ] + radius - hfPos [ 0 ] ) / w ) ;
/ * i f ( i d x B < 0 | | i d x A > = d a t a . l e n g t h )
return justTest ? false : 0 ; * /
if ( idxA < 0 ) {
idxA = 0 ;
}
if ( idxB >= data . length ) {
idxB = data . length - 1 ;
}
// Get max and min
var max = data [ idxA ] ,
min = data [ idxB ] ;
for ( var i = idxA ; i < idxB ; i ++ ) {
if ( data [ i ] < min ) {
min = data [ i ] ;
}
if ( data [ i ] > max ) {
max = data [ i ] ;
}
}
if ( circlePos [ 1 ] - radius > max ) {
return justTest ? false : 0 ;
}
/ *
if ( circlePos [ 1 ] + radius < min ) {
// Below the minimum point... We can just guess.
// TODO
}
* /
// 1. Check so center of circle is not inside the field. If it is, this wont work...
// 2. For each edge
// 2. 1. Get point on circle that is closest to the edge (scale normal with -radius)
// 2. 2. Check if point is inside.
var found = false ;
// Check all edges first
for ( var i = idxA ; i < idxB ; i ++ ) {
// Get points
vec2 . set ( v0 , i * w , data [ i ] ) ;
vec2 . set ( v1 , ( i + 1 ) * w , data [ i + 1 ] ) ;
vec2 . add ( v0 , v0 , hfPos ) ;
vec2 . add ( v1 , v1 , hfPos ) ;
// Get normal
vec2 . sub ( worldNormal , v1 , v0 ) ;
vec2 . rotate ( worldNormal , worldNormal , Math . PI / 2 ) ;
vec2 . normalize ( worldNormal , worldNormal ) ;
// Get point on circle, closest to the edge
vec2 . scale ( candidate , worldNormal , - radius ) ;
vec2 . add ( candidate , candidate , circlePos ) ;
// Distance from v0 to the candidate point
vec2 . sub ( dist , candidate , v0 ) ;
// Check if it is in the element "stick"
var d = vec2 . dot ( dist , worldNormal ) ;
if ( candidate [ 0 ] >= v0 [ 0 ] && candidate [ 0 ] < v1 [ 0 ] && d <= 0 ) {
if ( justTest ) {
return true ;
}
found = true ;
// Store the candidate point, projected to the edge
vec2 . scale ( dist , worldNormal , - d ) ;
vec2 . add ( minCandidate , candidate , dist ) ;
vec2 . copy ( minCandidateNormal , worldNormal ) ;
var c = this . createContactEquation ( hfBody , circleBody , hfShape , circleShape ) ;
// Normal is out of the heightfield
vec2 . copy ( c . normalA , minCandidateNormal ) ;
// Vector from circle to heightfield
vec2 . scale ( c . contactPointB , c . normalA , - radius ) ;
add ( c . contactPointB , c . contactPointB , circlePos ) ;
sub ( c . contactPointB , c . contactPointB , circleBody . position ) ;
vec2 . copy ( c . contactPointA , minCandidate ) ;
vec2 . sub ( c . contactPointA , c . contactPointA , hfBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
}
}
// Check all vertices
found = false ;
if ( radius > 0 ) {
for ( var i = idxA ; i <= idxB ; i ++ ) {
// Get point
vec2 . set ( v0 , i * w , data [ i ] ) ;
vec2 . add ( v0 , v0 , hfPos ) ;
vec2 . sub ( dist , circlePos , v0 ) ;
if ( vec2 . squaredLength ( dist ) < Math . pow ( radius , 2 ) ) {
if ( justTest ) {
return true ;
}
found = true ;
var c = this . createContactEquation ( hfBody , circleBody , hfShape , circleShape ) ;
// Construct normal - out of heightfield
vec2 . copy ( c . normalA , dist ) ;
vec2 . normalize ( c . normalA , c . normalA ) ;
vec2 . scale ( c . contactPointB , c . normalA , - radius ) ;
add ( c . contactPointB , c . contactPointB , circlePos ) ;
sub ( c . contactPointB , c . contactPointB , circleBody . position ) ;
sub ( c . contactPointA , v0 , hfPos ) ;
add ( c . contactPointA , c . contactPointA , hfPos ) ;
sub ( c . contactPointA , c . contactPointA , hfBody . position ) ;
this . contactEquations . push ( c ) ;
if ( this . enableFriction ) {
this . frictionEquations . push ( this . createFrictionFromContact ( c ) ) ;
}
}
}
}
if ( found ) {
return 1 ;
}
return 0 ;
} ;
var convexHeightfield _v0 = vec2 . create ( ) ,
convexHeightfield _v1 = vec2 . create ( ) ,
convexHeightfield _tilePos = vec2 . create ( ) ,
convexHeightfield _tempConvexShape = new Convex ( { vertices : [ vec2 . create ( ) , vec2 . create ( ) , vec2 . create ( ) , vec2 . create ( ) ] } ) ;
/ * *
* @ method circleHeightfield
* @ param { Body } bi
* @ param { Circle } si
* @ param { Array } xi
* @ param { Body } bj
* @ param { Heightfield } sj
* @ param { Array } xj
* @ param { Number } aj
* /
Narrowphase . prototype [ Shape . BOX | Shape . HEIGHTFIELD ] =
Narrowphase . prototype [ Shape . CONVEX | Shape . HEIGHTFIELD ] =
Narrowphase . prototype . convexHeightfield = function ( convexBody , convexShape , convexPos , convexAngle ,
hfBody , hfShape , hfPos , hfAngle , justTest ) {
var data = hfShape . heights ,
w = hfShape . elementWidth ,
v0 = convexHeightfield _v0 ,
v1 = convexHeightfield _v1 ,
tilePos = convexHeightfield _tilePos ,
tileConvex = convexHeightfield _tempConvexShape ;
// Get the index of the points to test against
var idxA = Math . floor ( ( convexBody . aabb . lowerBound [ 0 ] - hfPos [ 0 ] ) / w ) ,
idxB = Math . ceil ( ( convexBody . aabb . upperBound [ 0 ] - hfPos [ 0 ] ) / w ) ;
if ( idxA < 0 ) {
idxA = 0 ;
}
if ( idxB >= data . length ) {
idxB = data . length - 1 ;
}
// Get max and min
var max = data [ idxA ] ,
min = data [ idxB ] ;
for ( var i = idxA ; i < idxB ; i ++ ) {
if ( data [ i ] < min ) {
min = data [ i ] ;
}
if ( data [ i ] > max ) {
max = data [ i ] ;
}
}
if ( convexBody . aabb . lowerBound [ 1 ] > max ) {
return justTest ? false : 0 ;
}
var found = false ;
var numContacts = 0 ;
// Loop over all edges
// TODO: If possible, construct a convex from several data points (need o check if the points make a convex shape)
for ( var i = idxA ; i < idxB ; i ++ ) {
// Get points
vec2 . set ( v0 , i * w , data [ i ] ) ;
vec2 . set ( v1 , ( i + 1 ) * w , data [ i + 1 ] ) ;
vec2 . add ( v0 , v0 , hfPos ) ;
vec2 . add ( v1 , v1 , hfPos ) ;
// Construct a convex
var tileHeight = 100 ; // todo
vec2 . set ( tilePos , ( v1 [ 0 ] + v0 [ 0 ] ) * 0.5 , ( v1 [ 1 ] + v0 [ 1 ] - tileHeight ) * 0.5 ) ;
vec2 . sub ( tileConvex . vertices [ 0 ] , v1 , tilePos ) ;
vec2 . sub ( tileConvex . vertices [ 1 ] , v0 , tilePos ) ;
vec2 . copy ( tileConvex . vertices [ 2 ] , tileConvex . vertices [ 1 ] ) ;
vec2 . copy ( tileConvex . vertices [ 3 ] , tileConvex . vertices [ 0 ] ) ;
tileConvex . vertices [ 2 ] [ 1 ] -= tileHeight ;
tileConvex . vertices [ 3 ] [ 1 ] -= tileHeight ;
// Do convex collision
numContacts += this . convexConvex ( convexBody , convexShape , convexPos , convexAngle ,
hfBody , tileConvex , tilePos , 0 , justTest ) ;
}
return numContacts ;
} ;
} , { "../equations/ContactEquation" : 21 , "../equations/Equation" : 22 , "../equations/FrictionEquation" : 23 , "../math/vec2" : 30 , "../objects/Body" : 31 , "../shapes/Box" : 37 , "../shapes/Circle" : 39 , "../shapes/Convex" : 40 , "../shapes/Shape" : 45 , "../utils/ContactEquationPool" : 48 , "../utils/FrictionEquationPool" : 49 , "../utils/TupleDictionary" : 56 , "../utils/Utils" : 57 } ] , 11 : [ function ( _dereq _ , module , exports ) {
module . exports = Ray ;
var vec2 = _dereq _ ( '../math/vec2' ) ;
var RaycastResult = _dereq _ ( '../collision/RaycastResult' ) ;
var Shape = _dereq _ ( '../shapes/Shape' ) ;
var AABB = _dereq _ ( '../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 { { / c r o s s L i n k } }
* @ 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 { { / c r o s s L i n k } } , { { # c r o s s L i n k " R a y / A L L : p r o p e r t y " } } R a y . A L L { { / c r o s s L i n k } } o r { { # c r o s s L i n k " R a y / C L O S E S T : p r o p e r t y " } } R a y . C L O S E S T { { / c r o s s L i n k } } .
* @ 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 ) ;
}
} , { "../collision/AABB" : 7 , "../collision/RaycastResult" : 12 , "../math/vec2" : 30 , "../shapes/Shape" : 45 } ] , 12 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' ) ;
var Ray = _dereq _ ( '../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 ;
} ;
} , { "../collision/Ray" : 11 , "../math/vec2" : 30 } ] , 13 : [ function ( _dereq _ , module , exports ) {
var Utils = _dereq _ ( '../utils/Utils' )
, Broadphase = _dereq _ ( '../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 ;
} ;
} , { "../collision/Broadphase" : 8 , "../utils/Utils" : 57 } ] , 14 : [ function ( _dereq _ , module , exports ) {
module . exports = Constraint ;
var Utils = _dereq _ ( '../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 ;
}
} ;
} , { "../utils/Utils" : 57 } ] , 15 : [ function ( _dereq _ , module , exports ) {
var Constraint = _dereq _ ( './Constraint' )
, Equation = _dereq _ ( '../equations/Equation' )
, vec2 = _dereq _ ( '../math/vec2' )
, Utils = _dereq _ ( '../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 ;
} ;
} , { "../equations/Equation" : 22 , "../math/vec2" : 30 , "../utils/Utils" : 57 , "./Constraint" : 14 } ] , 16 : [ function ( _dereq _ , module , exports ) {
var Constraint = _dereq _ ( './Constraint' )
, Equation = _dereq _ ( '../equations/Equation' )
, AngleLockEquation = _dereq _ ( '../equations/AngleLockEquation' )
, vec2 = _dereq _ ( '../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 ;
} ;
} , { "../equations/AngleLockEquation" : 20 , "../equations/Equation" : 22 , "../math/vec2" : 30 , "./Constraint" : 14 } ] , 17 : [ function ( _dereq _ , module , exports ) {
var Constraint = _dereq _ ( './Constraint' )
, vec2 = _dereq _ ( '../math/vec2' )
, Equation = _dereq _ ( '../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 ) ;
} ;
} , { "../equations/Equation" : 22 , "../math/vec2" : 30 , "./Constraint" : 14 } ] , 18 : [ function ( _dereq _ , module , exports ) {
var Constraint = _dereq _ ( './Constraint' )
, ContactEquation = _dereq _ ( '../equations/ContactEquation' )
, Equation = _dereq _ ( '../equations/Equation' )
, vec2 = _dereq _ ( '../math/vec2' )
, RotationalLockEquation = _dereq _ ( '../equations/RotationalLockEquation' ) ;
module . exports = PrismaticConstraint ;
/ * *
* Constraint that only allows bodies to move along a line , relative to each other . See < a href = "http://www.iforce2d.net/b2dtut/joints-prismatic" > this tutorial < / a > . A l s o c a l l e d " s l i d e r c o n s t r a i n t " .
*
* @ class PrismaticConstraint
* @ constructor
* @ extends Constraint
* @ author schteppe
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Object } [ options ]
* @ param { Number } [ options . maxForce ] Max force to be applied by the constraint
* @ param { Array } [ options . localAnchorA ] Body A ' s anchor point , defined in its own local frame .
* @ param { Array } [ options . localAnchorB ] Body B ' s anchor point , defined in its own local frame .
* @ param { Array } [ options . localAxisA ] An axis , defined in body A frame , that body B ' s anchor point may slide along .
* @ param { Boolean } [ options . disableRotationalLock ] If set to true , bodyB will be free to rotate around its anchor point .
* @ param { Number } [ options . upperLimit ]
* @ param { Number } [ options . lowerLimit ]
* @ todo Ability to create using only a point and a worldAxis
* /
function PrismaticConstraint ( bodyA , bodyB , options ) {
options = options || { } ;
Constraint . call ( this , bodyA , bodyB , Constraint . PRISMATIC , options ) ;
// Get anchors
var localAnchorA = vec2 . fromValues ( 0 , 0 ) ,
localAxisA = vec2 . fromValues ( 1 , 0 ) ,
localAnchorB = vec2 . fromValues ( 0 , 0 ) ;
if ( options . localAnchorA ) { vec2 . copy ( localAnchorA , options . localAnchorA ) ; }
if ( options . localAxisA ) { vec2 . copy ( localAxisA , options . localAxisA ) ; }
if ( options . localAnchorB ) { vec2 . copy ( localAnchorB , options . localAnchorB ) ; }
/ * *
* @ property localAnchorA
* @ type { Array }
* /
this . localAnchorA = localAnchorA ;
/ * *
* @ property localAnchorB
* @ type { Array }
* /
this . localAnchorB = localAnchorB ;
/ * *
* @ property localAxisA
* @ type { Array }
* /
this . localAxisA = localAxisA ;
/ *
The constraint violation for the common axis point is
g = ( xj + rj - xi - ri ) * t : = gg * t
where r are body - local anchor points , and t is a tangent to the constraint axis defined in body i frame .
gdot = ( vj + wj x rj - vi - wi x ri ) * t + ( xj + rj - xi - ri ) * ( wi x t )
Note the use of the chain rule . Now we identify the jacobian
G * W = [ - t - ri x t + t x gg t rj x t ] * [ vi wi vj wj ]
The rotational part is just a rotation lock .
* /
var maxForce = this . maxForce = typeof ( options . maxForce ) !== "undefined" ? options . maxForce : Number . MAX _VALUE ;
// Translational part
var trans = new Equation ( bodyA , bodyB , - maxForce , maxForce ) ;
var ri = new vec2 . create ( ) ,
rj = new vec2 . create ( ) ,
gg = new vec2 . create ( ) ,
t = new vec2 . create ( ) ;
trans . computeGq = function ( ) {
// g = ( xj + rj - xi - ri ) * t
return vec2 . dot ( gg , t ) ;
} ;
trans . updateJacobian = function ( ) {
var G = this . G ,
xi = bodyA . position ,
xj = bodyB . position ;
vec2 . rotate ( ri , localAnchorA , bodyA . angle ) ;
vec2 . rotate ( rj , localAnchorB , bodyB . angle ) ;
vec2 . add ( gg , xj , rj ) ;
vec2 . sub ( gg , gg , xi ) ;
vec2 . sub ( gg , gg , ri ) ;
vec2 . rotate ( t , localAxisA , bodyA . angle + Math . PI / 2 ) ;
G [ 0 ] = - t [ 0 ] ;
G [ 1 ] = - t [ 1 ] ;
G [ 2 ] = - vec2 . crossLength ( ri , t ) + vec2 . crossLength ( t , gg ) ;
G [ 3 ] = t [ 0 ] ;
G [ 4 ] = t [ 1 ] ;
G [ 5 ] = vec2 . crossLength ( rj , t ) ;
} ;
this . equations . push ( trans ) ;
// Rotational part
if ( ! options . disableRotationalLock ) {
var rot = new RotationalLockEquation ( bodyA , bodyB , - maxForce , maxForce ) ;
this . equations . push ( rot ) ;
}
/ * *
* The position of anchor A relative to anchor B , along the constraint axis .
* @ property position
* @ type { Number }
* /
this . position = 0 ;
// Is this one used at all?
this . velocity = 0 ;
/ * *
* Set to true to enable lower limit .
* @ property lowerLimitEnabled
* @ type { Boolean }
* /
this . lowerLimitEnabled = typeof ( options . lowerLimit ) !== "undefined" ? true : false ;
/ * *
* Set to true to enable upper limit .
* @ property upperLimitEnabled
* @ type { Boolean }
* /
this . upperLimitEnabled = typeof ( options . upperLimit ) !== "undefined" ? true : false ;
/ * *
* Lower constraint limit . The constraint position is forced to be larger than this value .
* @ property lowerLimit
* @ type { Number }
* /
this . lowerLimit = typeof ( options . lowerLimit ) !== "undefined" ? options . lowerLimit : 0 ;
/ * *
* Upper constraint limit . The constraint position is forced to be smaller than this value .
* @ property upperLimit
* @ type { Number }
* /
this . upperLimit = typeof ( options . upperLimit ) !== "undefined" ? options . upperLimit : 1 ;
// Equations used for limits
this . upperLimitEquation = new ContactEquation ( bodyA , bodyB ) ;
this . lowerLimitEquation = new ContactEquation ( bodyA , bodyB ) ;
// Set max/min forces
this . upperLimitEquation . minForce = this . lowerLimitEquation . minForce = 0 ;
this . upperLimitEquation . maxForce = this . lowerLimitEquation . maxForce = maxForce ;
/ * *
* Equation used for the motor .
* @ property motorEquation
* @ type { Equation }
* /
this . motorEquation = new Equation ( bodyA , bodyB ) ;
/ * *
* The current motor state . Enable or disable the motor using . enableMotor
* @ property motorEnabled
* @ type { Boolean }
* /
this . motorEnabled = false ;
/ * *
* Set the target speed for the motor .
* @ property motorSpeed
* @ type { Number }
* /
this . motorSpeed = 0 ;
var that = this ;
var motorEquation = this . motorEquation ;
var old = motorEquation . computeGW ;
motorEquation . computeGq = function ( ) { return 0 ; } ;
motorEquation . computeGW = function ( ) {
var G = this . G ,
bi = this . bodyA ,
bj = this . bodyB ,
vi = bi . velocity ,
vj = bj . velocity ,
wi = bi . angularVelocity ,
wj = bj . angularVelocity ;
return this . gmult ( G , vi , wi , vj , wj ) + that . motorSpeed ;
} ;
}
PrismaticConstraint . prototype = new Constraint ( ) ;
PrismaticConstraint . prototype . constructor = PrismaticConstraint ;
var worldAxisA = vec2 . create ( ) ,
worldAnchorA = vec2 . create ( ) ,
worldAnchorB = vec2 . create ( ) ,
orientedAnchorA = vec2 . create ( ) ,
orientedAnchorB = vec2 . create ( ) ,
tmp = vec2 . create ( ) ;
/ * *
* Update the constraint equations . Should be done if any of the bodies changed position , before solving .
* @ method update
* /
PrismaticConstraint . prototype . update = function ( ) {
var eqs = this . equations ,
trans = eqs [ 0 ] ,
upperLimit = this . upperLimit ,
lowerLimit = this . lowerLimit ,
upperLimitEquation = this . upperLimitEquation ,
lowerLimitEquation = this . lowerLimitEquation ,
bodyA = this . bodyA ,
bodyB = this . bodyB ,
localAxisA = this . localAxisA ,
localAnchorA = this . localAnchorA ,
localAnchorB = this . localAnchorB ;
trans . updateJacobian ( ) ;
// Transform local things to world
vec2 . rotate ( worldAxisA , localAxisA , bodyA . angle ) ;
vec2 . rotate ( orientedAnchorA , localAnchorA , bodyA . angle ) ;
vec2 . add ( worldAnchorA , orientedAnchorA , bodyA . position ) ;
vec2 . rotate ( orientedAnchorB , localAnchorB , bodyB . angle ) ;
vec2 . add ( worldAnchorB , orientedAnchorB , bodyB . position ) ;
var relPosition = this . position = vec2 . dot ( worldAnchorB , worldAxisA ) - vec2 . dot ( worldAnchorA , worldAxisA ) ;
// Motor
if ( this . motorEnabled ) {
// G = [ a a x ri -a -a x rj ]
var G = this . motorEquation . G ;
G [ 0 ] = worldAxisA [ 0 ] ;
G [ 1 ] = worldAxisA [ 1 ] ;
G [ 2 ] = vec2 . crossLength ( worldAxisA , orientedAnchorB ) ;
G [ 3 ] = - worldAxisA [ 0 ] ;
G [ 4 ] = - worldAxisA [ 1 ] ;
G [ 5 ] = - vec2 . crossLength ( worldAxisA , orientedAnchorA ) ;
}
/ *
Limits strategy :
Add contact equation , with normal along the constraint axis .
min / maxForce is set so the constraint is repulsive in the correct direction .
Some offset is added to either equation . contactPointA or . contactPointB to get the correct upper / lower limit .
^
|
upperLimit x
| -- -- --
anchorB x < -- - | B |
| | |
-- -- -- | -- -- --
| | |
| A | -- > x anchorA
-- -- -- |
x lowerLimit
|
axis
* /
if ( this . upperLimitEnabled && relPosition > upperLimit ) {
// Update contact constraint normal, etc
vec2 . scale ( upperLimitEquation . normalA , worldAxisA , - 1 ) ;
vec2 . sub ( upperLimitEquation . contactPointA , worldAnchorA , bodyA . position ) ;
vec2 . sub ( upperLimitEquation . contactPointB , worldAnchorB , bodyB . position ) ;
vec2 . scale ( tmp , worldAxisA , upperLimit ) ;
vec2 . add ( upperLimitEquation . contactPointA , upperLimitEquation . contactPointA , tmp ) ;
if ( eqs . indexOf ( upperLimitEquation ) === - 1 ) {
eqs . push ( upperLimitEquation ) ;
}
} else {
var idx = eqs . indexOf ( upperLimitEquation ) ;
if ( idx !== - 1 ) {
eqs . splice ( idx , 1 ) ;
}
}
if ( this . lowerLimitEnabled && relPosition < lowerLimit ) {
// Update contact constraint normal, etc
vec2 . scale ( lowerLimitEquation . normalA , worldAxisA , 1 ) ;
vec2 . sub ( lowerLimitEquation . contactPointA , worldAnchorA , bodyA . position ) ;
vec2 . sub ( lowerLimitEquation . contactPointB , worldAnchorB , bodyB . position ) ;
vec2 . scale ( tmp , worldAxisA , lowerLimit ) ;
vec2 . sub ( lowerLimitEquation . contactPointB , lowerLimitEquation . contactPointB , tmp ) ;
if ( eqs . indexOf ( lowerLimitEquation ) === - 1 ) {
eqs . push ( lowerLimitEquation ) ;
}
} else {
var idx = eqs . indexOf ( lowerLimitEquation ) ;
if ( idx !== - 1 ) {
eqs . splice ( idx , 1 ) ;
}
}
} ;
/ * *
* Enable the motor
* @ method enableMotor
* /
PrismaticConstraint . prototype . enableMotor = function ( ) {
if ( this . motorEnabled ) {
return ;
}
this . equations . push ( this . motorEquation ) ;
this . motorEnabled = true ;
} ;
/ * *
* Disable the rotational motor
* @ method disableMotor
* /
PrismaticConstraint . prototype . disableMotor = function ( ) {
if ( ! this . motorEnabled ) {
return ;
}
var i = this . equations . indexOf ( this . motorEquation ) ;
this . equations . splice ( i , 1 ) ;
this . motorEnabled = false ;
} ;
/ * *
* Set the constraint limits .
* @ method setLimits
* @ param { number } lower Lower limit .
* @ param { number } upper Upper limit .
* /
PrismaticConstraint . prototype . setLimits = function ( lower , upper ) {
if ( typeof ( lower ) === 'number' ) {
this . lowerLimit = lower ;
this . lowerLimitEnabled = true ;
} else {
this . lowerLimit = lower ;
this . lowerLimitEnabled = false ;
}
if ( typeof ( upper ) === 'number' ) {
this . upperLimit = upper ;
this . upperLimitEnabled = true ;
} else {
this . upperLimit = upper ;
this . upperLimitEnabled = false ;
}
} ;
} , { "../equations/ContactEquation" : 21 , "../equations/Equation" : 22 , "../equations/RotationalLockEquation" : 24 , "../math/vec2" : 30 , "./Constraint" : 14 } ] , 19 : [ function ( _dereq _ , module , exports ) {
var Constraint = _dereq _ ( './Constraint' )
, Equation = _dereq _ ( '../equations/Equation' )
, RotationalVelocityEquation = _dereq _ ( '../equations/RotationalVelocityEquation' )
, RotationalLockEquation = _dereq _ ( '../equations/RotationalLockEquation' )
, vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = RevoluteConstraint ;
var worldPivotA = vec2 . create ( ) ,
worldPivotB = vec2 . create ( ) ,
xAxis = vec2 . fromValues ( 1 , 0 ) ,
yAxis = vec2 . fromValues ( 0 , 1 ) ,
g = vec2 . create ( ) ;
/ * *
* Connects two bodies at given offset points , letting them rotate relative to each other around this point .
* @ class RevoluteConstraint
* @ constructor
* @ author schteppe
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Object } [ options ]
* @ param { Array } [ options . worldPivot ] A pivot point given in world coordinates . If specified , localPivotA and localPivotB are automatically computed from this value .
* @ param { Array } [ options . localPivotA ] The point relative to the center of mass of bodyA which bodyA is constrained to .
* @ param { Array } [ options . localPivotB ] See localPivotA .
* @ param { Number } [ options . maxForce ] The maximum force that should be applied to constrain the bodies .
* @ extends Constraint
*
* @ example
* // This will create a revolute constraint between two bodies with pivot point in between them.
* var bodyA = new Body ( { mass : 1 , position : [ - 1 , 0 ] } ) ;
* var bodyB = new Body ( { mass : 1 , position : [ 1 , 0 ] } ) ;
* var constraint = new RevoluteConstraint ( bodyA , bodyB , {
* worldPivot : [ 0 , 0 ]
* } ) ;
* world . addConstraint ( constraint ) ;
*
* // Using body-local pivot points, the constraint could have been constructed like this:
* var constraint = new RevoluteConstraint ( bodyA , bodyB , {
* localPivotA : [ 1 , 0 ] ,
* localPivotB : [ - 1 , 0 ]
* } ) ;
* /
function RevoluteConstraint ( bodyA , bodyB , options ) {
options = options || { } ;
Constraint . call ( this , bodyA , bodyB , Constraint . REVOLUTE , options ) ;
var maxForce = this . maxForce = typeof ( options . maxForce ) !== "undefined" ? options . maxForce : Number . MAX _VALUE ;
/ * *
* @ property { Array } pivotA
* /
this . pivotA = vec2 . create ( ) ;
/ * *
* @ property { Array } pivotB
* /
this . pivotB = vec2 . create ( ) ;
if ( options . worldPivot ) {
// Compute pivotA and pivotB
vec2 . sub ( this . pivotA , options . worldPivot , bodyA . position ) ;
vec2 . sub ( this . pivotB , options . worldPivot , bodyB . position ) ;
// Rotate to local coordinate system
vec2 . rotate ( this . pivotA , this . pivotA , - bodyA . angle ) ;
vec2 . rotate ( this . pivotB , this . pivotB , - bodyB . angle ) ;
} else {
// Get pivotA and pivotB
vec2 . copy ( this . pivotA , options . localPivotA ) ;
vec2 . copy ( this . pivotB , options . localPivotB ) ;
}
// Equations to be fed to the solver
var eqs = this . equations = [
new Equation ( bodyA , bodyB , - maxForce , maxForce ) ,
new Equation ( bodyA , bodyB , - maxForce , maxForce ) ,
] ;
var x = eqs [ 0 ] ;
var y = eqs [ 1 ] ;
var that = this ;
x . computeGq = function ( ) {
vec2 . rotate ( worldPivotA , that . pivotA , bodyA . angle ) ;
vec2 . rotate ( worldPivotB , that . pivotB , bodyB . angle ) ;
vec2 . add ( g , bodyB . position , worldPivotB ) ;
vec2 . sub ( g , g , bodyA . position ) ;
vec2 . sub ( g , g , worldPivotA ) ;
return vec2 . dot ( g , xAxis ) ;
} ;
y . computeGq = function ( ) {
vec2 . rotate ( worldPivotA , that . pivotA , bodyA . angle ) ;
vec2 . rotate ( worldPivotB , that . pivotB , bodyB . angle ) ;
vec2 . add ( g , bodyB . position , worldPivotB ) ;
vec2 . sub ( g , g , bodyA . position ) ;
vec2 . sub ( g , g , worldPivotA ) ;
return vec2 . dot ( g , yAxis ) ;
} ;
y . minForce = x . minForce = - maxForce ;
y . maxForce = x . maxForce = maxForce ;
this . motorEquation = new RotationalVelocityEquation ( bodyA , bodyB ) ;
/ * *
* Indicates whether the motor is enabled . Use . enableMotor ( ) to enable the constraint motor .
* @ property { Boolean } motorEnabled
* @ readOnly
* /
this . motorEnabled = false ;
/ * *
* The constraint position .
* @ property angle
* @ type { Number }
* @ readOnly
* /
this . angle = 0 ;
/ * *
* Set to true to enable lower limit
* @ property lowerLimitEnabled
* @ type { Boolean }
* /
this . lowerLimitEnabled = false ;
/ * *
* Set to true to enable upper limit
* @ property upperLimitEnabled
* @ type { Boolean }
* /
this . upperLimitEnabled = false ;
/ * *
* The lower limit on the constraint angle .
* @ property lowerLimit
* @ type { Boolean }
* /
this . lowerLimit = 0 ;
/ * *
* The upper limit on the constraint angle .
* @ property upperLimit
* @ type { Boolean }
* /
this . upperLimit = 0 ;
this . upperLimitEquation = new RotationalLockEquation ( bodyA , bodyB ) ;
this . lowerLimitEquation = new RotationalLockEquation ( bodyA , bodyB ) ;
this . upperLimitEquation . minForce = 0 ;
this . lowerLimitEquation . maxForce = 0 ;
}
RevoluteConstraint . prototype = new Constraint ( ) ;
RevoluteConstraint . prototype . constructor = RevoluteConstraint ;
/ * *
* Set the constraint angle limits .
* @ method setLimits
* @ param { number } lower Lower angle limit .
* @ param { number } upper Upper angle limit .
* /
RevoluteConstraint . prototype . setLimits = function ( lower , upper ) {
if ( typeof ( lower ) === 'number' ) {
this . lowerLimit = lower ;
this . lowerLimitEnabled = true ;
} else {
this . lowerLimit = lower ;
this . lowerLimitEnabled = false ;
}
if ( typeof ( upper ) === 'number' ) {
this . upperLimit = upper ;
this . upperLimitEnabled = true ;
} else {
this . upperLimit = upper ;
this . upperLimitEnabled = false ;
}
} ;
RevoluteConstraint . prototype . update = function ( ) {
var bodyA = this . bodyA ,
bodyB = this . bodyB ,
pivotA = this . pivotA ,
pivotB = this . pivotB ,
eqs = this . equations ,
normal = eqs [ 0 ] ,
tangent = eqs [ 1 ] ,
x = eqs [ 0 ] ,
y = eqs [ 1 ] ,
upperLimit = this . upperLimit ,
lowerLimit = this . lowerLimit ,
upperLimitEquation = this . upperLimitEquation ,
lowerLimitEquation = this . lowerLimitEquation ;
var relAngle = this . angle = bodyB . angle - bodyA . angle ;
if ( this . upperLimitEnabled && relAngle > upperLimit ) {
upperLimitEquation . angle = upperLimit ;
if ( eqs . indexOf ( upperLimitEquation ) === - 1 ) {
eqs . push ( upperLimitEquation ) ;
}
} else {
var idx = eqs . indexOf ( upperLimitEquation ) ;
if ( idx !== - 1 ) {
eqs . splice ( idx , 1 ) ;
}
}
if ( this . lowerLimitEnabled && relAngle < lowerLimit ) {
lowerLimitEquation . angle = lowerLimit ;
if ( eqs . indexOf ( lowerLimitEquation ) === - 1 ) {
eqs . push ( lowerLimitEquation ) ;
}
} else {
var idx = eqs . indexOf ( lowerLimitEquation ) ;
if ( idx !== - 1 ) {
eqs . splice ( idx , 1 ) ;
}
}
/ *
The constraint violation is
g = xj + rj - xi - ri
... where xi and xj are the body positions and ri and rj world - oriented offset vectors . Differentiate :
gdot = vj + wj x rj - vi - wi x ri
We split this into x and y directions . ( let x and y be unit vectors along the respective axes )
gdot * x = ( vj + wj x rj - vi - wi x ri ) * x
= ( vj * x + ( wj x rj ) * x - vi * x - ( wi x ri ) * x
= ( vj * x + ( rj x x ) * wj - vi * x - ( ri x x ) * wi
= [ - x - ( ri x x ) x ( rj x x ) ] * [ vi wi vj wj ]
= G * W
... and similar for y . We have then identified the jacobian entries for x and y directions :
Gx = [ x ( rj x x ) - x - ( ri x x ) ]
Gy = [ y ( rj x y ) - y - ( ri x y ) ]
* /
vec2 . rotate ( worldPivotA , pivotA , bodyA . angle ) ;
vec2 . rotate ( worldPivotB , pivotB , bodyB . angle ) ;
// todo: these are a bit sparse. We could save some computations on making custom eq.computeGW functions, etc
x . G [ 0 ] = - 1 ;
x . G [ 1 ] = 0 ;
x . G [ 2 ] = - vec2 . crossLength ( worldPivotA , xAxis ) ;
x . G [ 3 ] = 1 ;
x . G [ 4 ] = 0 ;
x . G [ 5 ] = vec2 . crossLength ( worldPivotB , xAxis ) ;
y . G [ 0 ] = 0 ;
y . G [ 1 ] = - 1 ;
y . G [ 2 ] = - vec2 . crossLength ( worldPivotA , yAxis ) ;
y . G [ 3 ] = 0 ;
y . G [ 4 ] = 1 ;
y . G [ 5 ] = vec2 . crossLength ( worldPivotB , yAxis ) ;
} ;
/ * *
* Enable the rotational motor
* @ method enableMotor
* /
RevoluteConstraint . prototype . enableMotor = function ( ) {
if ( this . motorEnabled ) {
return ;
}
this . equations . push ( this . motorEquation ) ;
this . motorEnabled = true ;
} ;
/ * *
* Disable the rotational motor
* @ method disableMotor
* /
RevoluteConstraint . prototype . disableMotor = function ( ) {
if ( ! this . motorEnabled ) {
return ;
}
var i = this . equations . indexOf ( this . motorEquation ) ;
this . equations . splice ( i , 1 ) ;
this . motorEnabled = false ;
} ;
/ * *
* Check if the motor is enabled .
* @ method motorIsEnabled
* @ deprecated use property motorEnabled instead .
* @ return { Boolean }
* /
RevoluteConstraint . prototype . motorIsEnabled = function ( ) {
return ! ! this . motorEnabled ;
} ;
/ * *
* Set the speed of the rotational constraint motor
* @ method setMotorSpeed
* @ param { Number } speed
* /
RevoluteConstraint . prototype . setMotorSpeed = function ( speed ) {
if ( ! this . motorEnabled ) {
return ;
}
var i = this . equations . indexOf ( this . motorEquation ) ;
this . equations [ i ] . relativeVelocity = speed ;
} ;
/ * *
* Get the speed of the rotational constraint motor
* @ method getMotorSpeed
* @ return { Number } The current speed , or false if the motor is not enabled .
* /
RevoluteConstraint . prototype . getMotorSpeed = function ( ) {
if ( ! this . motorEnabled ) {
return false ;
}
return this . motorEquation . relativeVelocity ;
} ;
} , { "../equations/Equation" : 22 , "../equations/RotationalLockEquation" : 24 , "../equations/RotationalVelocityEquation" : 25 , "../math/vec2" : 30 , "./Constraint" : 14 } ] , 20 : [ function ( _dereq _ , module , exports ) {
var Equation = _dereq _ ( "./Equation" ) ,
vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = AngleLockEquation ;
/ * *
* Locks the relative angle between two bodies . The constraint tries to keep the dot product between two vectors , local in each body , to zero . The local angle in body i is a parameter .
*
* @ class AngleLockEquation
* @ constructor
* @ extends Equation
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Object } [ options ]
* @ param { Number } [ options . angle ] Angle to add to the local vector in body A .
* @ param { Number } [ options . ratio ] Gear ratio
* /
function AngleLockEquation ( bodyA , bodyB , options ) {
options = options || { } ;
Equation . call ( this , bodyA , bodyB , - Number . MAX _VALUE , Number . MAX _VALUE ) ;
this . angle = options . angle || 0 ;
/ * *
* The gear ratio .
* @ property { Number } ratio
* @ private
* @ see setRatio
* /
this . ratio = typeof ( options . ratio ) === "number" ? options . ratio : 1 ;
this . setRatio ( this . ratio ) ;
}
AngleLockEquation . prototype = new Equation ( ) ;
AngleLockEquation . prototype . constructor = AngleLockEquation ;
AngleLockEquation . prototype . computeGq = function ( ) {
return this . ratio * this . bodyA . angle - this . bodyB . angle + this . angle ;
} ;
/ * *
* Set the gear ratio for this equation
* @ method setRatio
* @ param { Number } ratio
* /
AngleLockEquation . prototype . setRatio = function ( ratio ) {
var G = this . G ;
G [ 2 ] = ratio ;
G [ 5 ] = - 1 ;
this . ratio = ratio ;
} ;
/ * *
* Set the max force for the equation .
* @ method setMaxTorque
* @ param { Number } torque
* /
AngleLockEquation . prototype . setMaxTorque = function ( torque ) {
this . maxForce = torque ;
this . minForce = - torque ;
} ;
} , { "../math/vec2" : 30 , "./Equation" : 22 } ] , 21 : [ function ( _dereq _ , module , exports ) {
var Equation = _dereq _ ( "./Equation" ) ,
vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = ContactEquation ;
/ * *
* Non - penetration constraint equation . Tries to make the contactPointA and contactPointB vectors coincide , while keeping the applied force repulsive .
*
* @ class ContactEquation
* @ constructor
* @ extends Equation
* @ param { Body } bodyA
* @ param { Body } bodyB
* /
function ContactEquation ( bodyA , bodyB ) {
Equation . call ( this , bodyA , bodyB , 0 , Number . MAX _VALUE ) ;
/ * *
* Vector from body i center of mass to the contact point .
* @ property contactPointA
* @ type { Array }
* /
this . contactPointA = vec2 . create ( ) ;
this . penetrationVec = vec2 . create ( ) ;
/ * *
* World - oriented vector from body A center of mass to the contact point .
* @ property contactPointB
* @ type { Array }
* /
this . contactPointB = vec2 . create ( ) ;
/ * *
* The normal vector , pointing out of body i
* @ property normalA
* @ type { Array }
* /
this . normalA = vec2 . create ( ) ;
/ * *
* The restitution to use ( 0 = no bounciness , 1 = max bounciness ) .
* @ property restitution
* @ type { Number }
* /
this . restitution = 0 ;
/ * *
* This property is set to true if this is the first impact between the bodies ( not persistant contact ) .
* @ property firstImpact
* @ type { Boolean }
* @ readOnly
* /
this . firstImpact = false ;
/ * *
* The shape in body i that triggered this contact .
* @ property shapeA
* @ type { Shape }
* /
this . shapeA = null ;
/ * *
* The shape in body j that triggered this contact .
* @ property shapeB
* @ type { Shape }
* /
this . shapeB = null ;
}
ContactEquation . prototype = new Equation ( ) ;
ContactEquation . prototype . constructor = ContactEquation ;
ContactEquation . prototype . computeB = function ( a , b , h ) {
var bi = this . bodyA ,
bj = this . bodyB ,
ri = this . contactPointA ,
rj = this . contactPointB ,
xi = bi . position ,
xj = bj . position ;
var penetrationVec = this . penetrationVec ,
n = this . normalA ,
G = this . G ;
// 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 ;
// Calculate q = xj+rj -(xi+ri) i.e. the penetration vector
vec2 . add ( penetrationVec , xj , rj ) ;
vec2 . sub ( penetrationVec , penetrationVec , xi ) ;
vec2 . sub ( penetrationVec , penetrationVec , ri ) ;
// Compute iteration
var GW , Gq ;
if ( this . firstImpact && this . restitution !== 0 ) {
Gq = 0 ;
GW = ( 1 / b ) * ( 1 + this . restitution ) * this . computeGW ( ) ;
} else {
Gq = vec2 . dot ( n , penetrationVec ) + this . offset ;
GW = this . computeGW ( ) ;
}
var GiMf = this . computeGiMf ( ) ;
var B = - Gq * a - GW * b - h * GiMf ;
return B ;
} ;
2016-09-21 22:22:08 +00:00
var vi = vec2 . create ( ) ;
var vj = vec2 . create ( ) ;
var relVel = vec2 . create ( ) ;
/ * *
* Get the relative velocity along the normal vector .
* @ return { number }
* /
ContactEquation . prototype . getVelocityAlongNormal = function ( ) {
this . bodyA . getVelocityAtPoint ( vi , this . contactPointA ) ;
this . bodyB . getVelocityAtPoint ( vj , this . contactPointB ) ;
vec2 . subtract ( relVel , vi , vj ) ;
return vec2 . dot ( this . normalA , relVel ) ;
} ;
2016-02-12 16:00:36 +00:00
} , { "../math/vec2" : 30 , "./Equation" : 22 } ] , 22 : [ function ( _dereq _ , module , exports ) {
module . exports = Equation ;
var vec2 = _dereq _ ( '../math/vec2' ) ,
Utils = _dereq _ ( '../utils/Utils' ) ,
Body = _dereq _ ( '../objects/Body' ) ;
/ * *
* Base class for constraint equations .
* @ class Equation
* @ constructor
* @ param { Body } bodyA First body participating in the equation
* @ param { Body } bodyB Second body participating in the equation
* @ param { number } minForce Minimum force to apply . Default : - Number . MAX _VALUE
* @ param { number } maxForce Maximum force to apply . Default : Number . MAX _VALUE
* /
function Equation ( bodyA , bodyB , minForce , maxForce ) {
/ * *
* Minimum force to apply when solving .
* @ property minForce
* @ type { Number }
* /
this . minForce = typeof ( minForce ) === "undefined" ? - Number . MAX _VALUE : minForce ;
/ * *
* Max force to apply when solving .
* @ property maxForce
* @ type { Number }
* /
this . maxForce = typeof ( maxForce ) === "undefined" ? Number . MAX _VALUE : maxForce ;
/ * *
* 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 ;
/ * *
* The stiffness of this equation . Typically chosen to a large number ( ~ 1e7 ) , but can be chosen somewhat freely to get a stable simulation .
* @ property stiffness
* @ type { Number }
* /
this . stiffness = Equation . DEFAULT _STIFFNESS ;
/ * *
* The number of time steps needed to stabilize the constraint equation . Typically between 3 and 5 time steps .
* @ property relaxation
* @ type { Number }
* /
this . relaxation = Equation . DEFAULT _RELAXATION ;
/ * *
* The Jacobian entry of this equation . 6 numbers , 3 per body ( x , y , angle ) .
* @ property G
* @ type { Array }
* /
this . G = new Utils . ARRAY _TYPE ( 6 ) ;
for ( var i = 0 ; i < 6 ; i ++ ) {
this . G [ i ] = 0 ;
}
this . offset = 0 ;
this . a = 0 ;
this . b = 0 ;
this . epsilon = 0 ;
this . timeStep = 1 / 60 ;
/ * *
* Indicates if stiffness or relaxation was changed .
* @ property { Boolean } needsUpdate
* /
this . needsUpdate = true ;
/ * *
* The resulting constraint multiplier from the last solve . This is mostly equivalent to the force produced by the constraint .
* @ property multiplier
* @ type { Number }
* /
this . multiplier = 0 ;
/ * *
* Relative velocity .
* @ property { Number } relativeVelocity
* /
this . relativeVelocity = 0 ;
/ * *
* Whether this equation is enabled or not . If true , it will be added to the solver .
* @ property { Boolean } enabled
* /
this . enabled = true ;
}
Equation . prototype . constructor = Equation ;
/ * *
* The default stiffness when creating a new Equation .
* @ static
* @ property { Number } DEFAULT _STIFFNESS
* @ default 1e6
* /
Equation . DEFAULT _STIFFNESS = 1e6 ;
/ * *
* The default relaxation when creating a new Equation .
* @ static
* @ property { Number } DEFAULT _RELAXATION
* @ default 4
* /
Equation . DEFAULT _RELAXATION = 4 ;
/ * *
* Compute SPOOK parameters . a , . b and . epsilon according to the current parameters . See equations 9 , 10 and 11 in the < a href = "http://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf" > SPOOK notes < / a > .
* @ method update
* /
Equation . prototype . update = function ( ) {
var k = this . stiffness ,
d = this . relaxation ,
h = this . timeStep ;
this . a = 4.0 / ( h * ( 1 + 4 * d ) ) ;
this . b = ( 4.0 * d ) / ( 1 + 4 * d ) ;
this . epsilon = 4.0 / ( h * h * k * ( 1 + 4 * d ) ) ;
this . needsUpdate = false ;
} ;
/ * *
* Multiply a jacobian entry with corresponding positions or velocities
* @ method gmult
* @ return { Number }
* /
Equation . prototype . gmult = function ( G , vi , wi , vj , wj ) {
return G [ 0 ] * vi [ 0 ] +
G [ 1 ] * vi [ 1 ] +
G [ 2 ] * wi +
G [ 3 ] * vj [ 0 ] +
G [ 4 ] * vj [ 1 ] +
G [ 5 ] * wj ;
} ;
/ * *
* Computes the RHS of the SPOOK equation
* @ method computeB
* @ return { Number }
* /
Equation . prototype . computeB = function ( a , b , h ) {
var GW = this . computeGW ( ) ;
var Gq = this . computeGq ( ) ;
var GiMf = this . computeGiMf ( ) ;
return - Gq * a - GW * b - GiMf * h ;
} ;
/ * *
* Computes G \ * q , where q are the generalized body coordinates
* @ method computeGq
* @ return { Number }
* /
var qi = vec2 . create ( ) ,
qj = vec2 . create ( ) ;
Equation . prototype . computeGq = function ( ) {
var G = this . G ,
bi = this . bodyA ,
bj = this . bodyB ,
xi = bi . position ,
xj = bj . position ,
ai = bi . angle ,
aj = bj . angle ;
return this . gmult ( G , qi , ai , qj , aj ) + this . offset ;
} ;
/ * *
* Computes G \ * W , where W are the body velocities
* @ method computeGW
* @ return { Number }
* /
Equation . prototype . computeGW = function ( ) {
var G = this . G ,
bi = this . bodyA ,
bj = this . bodyB ,
vi = bi . velocity ,
vj = bj . velocity ,
wi = bi . angularVelocity ,
wj = bj . angularVelocity ;
return this . gmult ( G , vi , wi , vj , wj ) + this . relativeVelocity ;
} ;
/ * *
* Computes G \ * Wlambda , where W are the body velocities
* @ method computeGWlambda
* @ return { Number }
* /
Equation . prototype . computeGWlambda = function ( ) {
var G = this . G ,
bi = this . bodyA ,
bj = this . bodyB ,
vi = bi . vlambda ,
vj = bj . vlambda ,
wi = bi . wlambda ,
wj = bj . wlambda ;
return this . gmult ( G , vi , wi , vj , wj ) ;
} ;
/ * *
* Computes G \ * inv ( M ) \ * f , where M is the mass matrix with diagonal blocks for each body , and f are the forces on the bodies .
* @ method computeGiMf
* @ return { Number }
* /
var iMfi = vec2 . create ( ) ,
iMfj = vec2 . create ( ) ;
Equation . prototype . computeGiMf = function ( ) {
var bi = this . bodyA ,
bj = this . bodyB ,
fi = bi . force ,
ti = bi . angularForce ,
fj = bj . force ,
tj = bj . angularForce ,
invMassi = bi . invMassSolve ,
invMassj = bj . invMassSolve ,
invIi = bi . invInertiaSolve ,
invIj = bj . invInertiaSolve ,
G = this . G ;
vec2 . scale ( iMfi , fi , invMassi ) ;
vec2 . multiply ( iMfi , bi . massMultiplier , iMfi ) ;
vec2 . scale ( iMfj , fj , invMassj ) ;
vec2 . multiply ( iMfj , bj . massMultiplier , iMfj ) ;
return this . gmult ( G , iMfi , ti * invIi , iMfj , tj * invIj ) ;
} ;
/ * *
* Computes G \ * inv ( M ) \ * G '
* @ method computeGiMGt
* @ return { Number }
* /
Equation . prototype . computeGiMGt = function ( ) {
var bi = this . bodyA ,
bj = this . bodyB ,
invMassi = bi . invMassSolve ,
invMassj = bj . invMassSolve ,
invIi = bi . invInertiaSolve ,
invIj = bj . invInertiaSolve ,
G = this . G ;
return G [ 0 ] * G [ 0 ] * invMassi * bi . massMultiplier [ 0 ] +
G [ 1 ] * G [ 1 ] * invMassi * bi . massMultiplier [ 1 ] +
G [ 2 ] * G [ 2 ] * invIi +
G [ 3 ] * G [ 3 ] * invMassj * bj . massMultiplier [ 0 ] +
G [ 4 ] * G [ 4 ] * invMassj * bj . massMultiplier [ 1 ] +
G [ 5 ] * G [ 5 ] * invIj ;
} ;
var addToWlambda _temp = vec2 . create ( ) ,
addToWlambda _Gi = vec2 . create ( ) ,
addToWlambda _Gj = vec2 . create ( ) ,
addToWlambda _ri = vec2 . create ( ) ,
addToWlambda _rj = vec2 . create ( ) ,
addToWlambda _Mdiag = vec2 . create ( ) ;
/ * *
* Add constraint velocity to the bodies .
* @ method addToWlambda
* @ param { Number } deltalambda
* /
Equation . prototype . addToWlambda = function ( deltalambda ) {
var bi = this . bodyA ,
bj = this . bodyB ,
temp = addToWlambda _temp ,
Gi = addToWlambda _Gi ,
Gj = addToWlambda _Gj ,
ri = addToWlambda _ri ,
rj = addToWlambda _rj ,
invMassi = bi . invMassSolve ,
invMassj = bj . invMassSolve ,
invIi = bi . invInertiaSolve ,
invIj = bj . invInertiaSolve ,
Mdiag = addToWlambda _Mdiag ,
G = this . G ;
Gi [ 0 ] = G [ 0 ] ;
Gi [ 1 ] = G [ 1 ] ;
Gj [ 0 ] = G [ 3 ] ;
Gj [ 1 ] = G [ 4 ] ;
// Add to linear velocity
// v_lambda += inv(M) * delta_lamba * G
vec2 . scale ( temp , Gi , invMassi * deltalambda ) ;
vec2 . multiply ( temp , temp , bi . massMultiplier ) ;
vec2 . add ( bi . vlambda , bi . vlambda , temp ) ;
// This impulse is in the offset frame
// Also add contribution to angular
//bi.wlambda -= vec2.crossLength(temp,ri);
bi . wlambda += invIi * G [ 2 ] * deltalambda ;
vec2 . scale ( temp , Gj , invMassj * deltalambda ) ;
vec2 . multiply ( temp , temp , bj . massMultiplier ) ;
vec2 . add ( bj . vlambda , bj . vlambda , temp ) ;
//bj.wlambda -= vec2.crossLength(temp,rj);
bj . wlambda += invIj * G [ 5 ] * deltalambda ;
} ;
/ * *
* Compute the denominator part of the SPOOK equation : C = G \ * inv ( M ) \ * G ' + eps
* @ method computeInvC
* @ param { Number } eps
* @ return { Number }
* /
Equation . prototype . computeInvC = function ( eps ) {
return 1.0 / ( this . computeGiMGt ( ) + eps ) ;
} ;
} , { "../math/vec2" : 30 , "../objects/Body" : 31 , "../utils/Utils" : 57 } ] , 23 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' )
, Equation = _dereq _ ( './Equation' )
, Utils = _dereq _ ( '../utils/Utils' ) ;
module . exports = FrictionEquation ;
/ * *
* Constrains the slipping in a contact along a tangent
*
* @ class FrictionEquation
* @ constructor
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Number } slipForce
* @ extends Equation
* /
function FrictionEquation ( bodyA , bodyB , slipForce ) {
Equation . call ( this , bodyA , bodyB , - slipForce , slipForce ) ;
/ * *
* Relative vector from center of body A to the contact point , world oriented .
* @ property contactPointA
* @ type { Array }
* /
this . contactPointA = vec2 . create ( ) ;
/ * *
* Relative vector from center of body B to the contact point , world oriented .
* @ property contactPointB
* @ type { Array }
* /
this . contactPointB = vec2 . create ( ) ;
/ * *
* Tangent vector that the friction force will act along . World oriented .
* @ property t
* @ type { Array }
* /
this . t = vec2 . create ( ) ;
/ * *
* ContactEquations connected to this friction equation . The contact equations can be used to rescale the max force for the friction . If more than one contact equation is given , then the max force can be set to the average .
* @ property contactEquations
* @ type { ContactEquation }
* /
this . contactEquations = [ ] ;
/ * *
* The shape in body i that triggered this friction .
* @ property shapeA
* @ type { Shape }
* @ todo Needed ? The shape can be looked up via contactEquation . shapeA ...
* /
this . shapeA = null ;
/ * *
* The shape in body j that triggered this friction .
* @ property shapeB
* @ type { Shape }
* @ todo Needed ? The shape can be looked up via contactEquation . shapeB ...
* /
this . shapeB = null ;
/ * *
* The friction coefficient to use .
* @ property frictionCoefficient
* @ type { Number }
* /
this . frictionCoefficient = 0.3 ;
}
FrictionEquation . prototype = new Equation ( ) ;
FrictionEquation . prototype . constructor = FrictionEquation ;
/ * *
* Set the slipping condition for the constraint . The friction force cannot be
* larger than this value .
* @ method setSlipForce
* @ param { Number } slipForce
* /
FrictionEquation . prototype . setSlipForce = function ( slipForce ) {
this . maxForce = slipForce ;
this . minForce = - slipForce ;
} ;
/ * *
* Get the max force for the constraint .
* @ method getSlipForce
* @ return { Number }
* /
FrictionEquation . prototype . getSlipForce = function ( ) {
return this . maxForce ;
} ;
FrictionEquation . prototype . computeB = function ( a , b , h ) {
var bi = this . bodyA ,
bj = this . bodyB ,
ri = this . contactPointA ,
rj = this . contactPointB ,
t = this . t ,
G = this . G ;
// G = [-t -rixt t rjxt]
// And remember, this is a pure velocity constraint, g is always zero!
G [ 0 ] = - t [ 0 ] ;
G [ 1 ] = - t [ 1 ] ;
G [ 2 ] = - vec2 . crossLength ( ri , t ) ;
G [ 3 ] = t [ 0 ] ;
G [ 4 ] = t [ 1 ] ;
G [ 5 ] = vec2 . crossLength ( rj , t ) ;
var GW = this . computeGW ( ) ,
GiMf = this . computeGiMf ( ) ;
var B = /* - g * a */ - GW * b - h * GiMf ;
return B ;
} ;
} , { "../math/vec2" : 30 , "../utils/Utils" : 57 , "./Equation" : 22 } ] , 24 : [ function ( _dereq _ , module , exports ) {
var Equation = _dereq _ ( "./Equation" ) ,
vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = RotationalLockEquation ;
/ * *
* Locks the relative angle between two bodies . The constraint tries to keep the dot product between two vectors , local in each body , to zero . The local angle in body i is a parameter .
*
* @ class RotationalLockEquation
* @ constructor
* @ extends Equation
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Object } [ options ]
* @ param { Number } [ options . angle ] Angle to add to the local vector in bodyA .
* /
function RotationalLockEquation ( bodyA , bodyB , options ) {
options = options || { } ;
Equation . call ( this , bodyA , bodyB , - Number . MAX _VALUE , Number . MAX _VALUE ) ;
/ * *
* @ property { number } angle
* /
this . angle = options . angle || 0 ;
var G = this . G ;
G [ 2 ] = 1 ;
G [ 5 ] = - 1 ;
}
RotationalLockEquation . prototype = new Equation ( ) ;
RotationalLockEquation . prototype . constructor = RotationalLockEquation ;
var worldVectorA = vec2 . create ( ) ,
worldVectorB = vec2 . create ( ) ,
xAxis = vec2 . fromValues ( 1 , 0 ) ,
yAxis = vec2 . fromValues ( 0 , 1 ) ;
RotationalLockEquation . prototype . computeGq = function ( ) {
vec2 . rotate ( worldVectorA , xAxis , this . bodyA . angle + this . angle ) ;
vec2 . rotate ( worldVectorB , yAxis , this . bodyB . angle ) ;
return vec2 . dot ( worldVectorA , worldVectorB ) ;
} ;
} , { "../math/vec2" : 30 , "./Equation" : 22 } ] , 25 : [ function ( _dereq _ , module , exports ) {
var Equation = _dereq _ ( "./Equation" ) ,
vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = RotationalVelocityEquation ;
/ * *
* Syncs rotational velocity of two bodies , or sets a relative velocity ( motor ) .
*
* @ class RotationalVelocityEquation
* @ constructor
* @ extends Equation
* @ param { Body } bodyA
* @ param { Body } bodyB
* /
function RotationalVelocityEquation ( bodyA , bodyB ) {
Equation . call ( this , bodyA , bodyB , - Number . MAX _VALUE , Number . MAX _VALUE ) ;
this . relativeVelocity = 1 ;
this . ratio = 1 ;
}
RotationalVelocityEquation . prototype = new Equation ( ) ;
RotationalVelocityEquation . prototype . constructor = RotationalVelocityEquation ;
RotationalVelocityEquation . prototype . computeB = function ( a , b , h ) {
var G = this . G ;
G [ 2 ] = - 1 ;
G [ 5 ] = this . ratio ;
var GiMf = this . computeGiMf ( ) ;
var GW = this . computeGW ( ) ;
var B = - GW * b - h * GiMf ;
return B ;
} ;
} , { "../math/vec2" : 30 , "./Equation" : 22 } ] , 26 : [ function ( _dereq _ , module , exports ) {
/ * *
* Base class for objects that dispatches events .
* @ class EventEmitter
* @ constructor
* /
var EventEmitter = function ( ) { } ;
module . exports = EventEmitter ;
EventEmitter . prototype = {
constructor : EventEmitter ,
/ * *
* Add an event listener
* @ method on
* @ param { String } type
* @ param { Function } listener
* @ return { EventEmitter } The self object , for chainability .
* /
on : function ( type , listener , context ) {
listener . context = context || this ;
if ( this . _listeners === undefined ) {
this . _listeners = { } ;
}
var listeners = this . _listeners ;
if ( listeners [ type ] === undefined ) {
listeners [ type ] = [ ] ;
}
if ( listeners [ type ] . indexOf ( listener ) === - 1 ) {
listeners [ type ] . push ( listener ) ;
}
return this ;
} ,
/ * *
* Check if an event listener is added
* @ method has
* @ param { String } type
* @ param { Function } listener
* @ return { Boolean }
* /
has : function ( type , listener ) {
if ( this . _listeners === undefined ) {
return false ;
}
var listeners = this . _listeners ;
if ( listener ) {
if ( listeners [ type ] !== undefined && listeners [ type ] . indexOf ( listener ) !== - 1 ) {
return true ;
}
} else {
if ( listeners [ type ] !== undefined ) {
return true ;
}
}
return false ;
} ,
/ * *
* Remove an event listener
* @ method off
* @ param { String } type
* @ param { Function } listener
* @ return { EventEmitter } The self object , for chainability .
* /
off : function ( type , listener ) {
if ( this . _listeners === undefined ) {
return this ;
}
var listeners = this . _listeners ;
var index = listeners [ type ] . indexOf ( listener ) ;
if ( index !== - 1 ) {
listeners [ type ] . splice ( index , 1 ) ;
}
return this ;
} ,
/ * *
* Emit an event .
* @ method emit
* @ param { Object } event
* @ param { String } event . type
* @ return { EventEmitter } The self object , for chainability .
* /
emit : function ( event ) {
if ( this . _listeners === undefined ) {
return this ;
}
var listeners = this . _listeners ;
var listenerArray = listeners [ event . type ] ;
if ( listenerArray !== undefined ) {
event . target = this ;
for ( var i = 0 , l = listenerArray . length ; i < l ; i ++ ) {
var listener = listenerArray [ i ] ;
listener . call ( listener . context , event ) ;
}
}
return this ;
}
} ;
} , { } ] , 27 : [ function ( _dereq _ , module , exports ) {
var Material = _dereq _ ( './Material' ) ;
var Equation = _dereq _ ( '../equations/Equation' ) ;
module . exports = ContactMaterial ;
/ * *
* Defines what happens when two materials meet , such as what friction coefficient to use . You can also set other things such as restitution , surface velocity and constraint parameters .
* @ class ContactMaterial
* @ constructor
* @ param { Material } materialA
* @ param { Material } materialB
* @ param { Object } [ options ]
* @ param { Number } [ options . friction = 0.3 ] Friction coefficient .
* @ param { Number } [ options . restitution = 0 ] Restitution coefficient aka "bounciness" .
* @ param { Number } [ options . stiffness ] ContactEquation stiffness .
* @ param { Number } [ options . relaxation ] ContactEquation relaxation .
* @ param { Number } [ options . frictionStiffness ] FrictionEquation stiffness .
* @ param { Number } [ options . frictionRelaxation ] FrictionEquation relaxation .
* @ param { Number } [ options . surfaceVelocity = 0 ] Surface velocity .
* @ author schteppe
* /
function ContactMaterial ( materialA , materialB , options ) {
options = options || { } ;
if ( ! ( materialA instanceof Material ) || ! ( materialB instanceof Material ) ) {
throw new Error ( "First two arguments must be Material instances." ) ;
}
/ * *
* The contact material identifier
* @ property id
* @ type { Number }
* /
this . id = ContactMaterial . idCounter ++ ;
/ * *
* First material participating in the contact material
* @ property materialA
* @ type { Material }
* /
this . materialA = materialA ;
/ * *
* Second material participating in the contact material
* @ property materialB
* @ type { Material }
* /
this . materialB = materialB ;
/ * *
2016-09-21 22:22:08 +00:00
* Friction coefficient to use in the contact of these two materials . Friction = 0 will make the involved objects super slippery , and friction = 1 will make it much less slippery . A friction coefficient larger than 1 will allow for very large friction forces , which can be convenient for preventing car tires not slip on the ground .
2016-02-12 16:00:36 +00:00
* @ property friction
* @ type { Number }
2016-09-21 22:22:08 +00:00
* @ default 0.3
2016-02-12 16:00:36 +00:00
* /
2016-09-21 22:22:08 +00:00
this . friction = typeof ( options . friction ) !== "undefined" ? Number ( options . friction ) : 0.3 ;
2016-02-12 16:00:36 +00:00
/ * *
2016-09-21 22:22:08 +00:00
* Restitution , or "bounciness" to use in the contact of these two materials . A restitution of 0 will make no bounce , while restitution = 1 will approximately bounce back with the same velocity the object came with .
2016-02-12 16:00:36 +00:00
* @ property restitution
* @ type { Number }
2016-09-21 22:22:08 +00:00
* @ default 0
2016-02-12 16:00:36 +00:00
* /
2016-09-21 22:22:08 +00:00
this . restitution = typeof ( options . restitution ) !== "undefined" ? Number ( options . restitution ) : 0 ;
2016-02-12 16:00:36 +00:00
/ * *
2016-09-21 22:22:08 +00:00
* Hardness of the contact . Less stiffness will make the objects penetrate more , and will make the contact act more like a spring than a contact force . Default value is { { # crossLink "Equation/DEFAULT_STIFFNESS:property" } } Equation . DEFAULT _STIFFNESS { { / c r o s s L i n k } } .
2016-02-12 16:00:36 +00:00
* @ property stiffness
* @ type { Number }
* /
2016-09-21 22:22:08 +00:00
this . stiffness = typeof ( options . stiffness ) !== "undefined" ? Number ( options . stiffness ) : Equation . DEFAULT _STIFFNESS ;
2016-02-12 16:00:36 +00:00
/ * *
2016-09-21 22:22:08 +00:00
* Relaxation of the resulting ContactEquation that this ContactMaterial generate . Default value is { { # crossLink "Equation/DEFAULT_RELAXATION:property" } } Equation . DEFAULT _RELAXATION { { / c r o s s L i n k } } .
2016-02-12 16:00:36 +00:00
* @ property relaxation
* @ type { Number }
* /
2016-09-21 22:22:08 +00:00
this . relaxation = typeof ( options . relaxation ) !== "undefined" ? Number ( options . relaxation ) : Equation . DEFAULT _RELAXATION ;
2016-02-12 16:00:36 +00:00
/ * *
2016-09-21 22:22:08 +00:00
* Stiffness of the resulting friction force . For most cases , the value of this property should be a large number . I cannot think of any case where you would want less frictionStiffness . Default value is { { # crossLink "Equation/DEFAULT_STIFFNESS:property" } } Equation . DEFAULT _STIFFNESS { { / c r o s s L i n k } } .
2016-02-12 16:00:36 +00:00
* @ property frictionStiffness
* @ type { Number }
* /
2016-09-21 22:22:08 +00:00
this . frictionStiffness = typeof ( options . frictionStiffness ) !== "undefined" ? Number ( options . frictionStiffness ) : Equation . DEFAULT _STIFFNESS ;
2016-02-12 16:00:36 +00:00
/ * *
2016-09-21 22:22:08 +00:00
* Relaxation of the resulting friction force . The default value should be good for most simulations . Default value is { { # crossLink "Equation/DEFAULT_RELAXATION:property" } } Equation . DEFAULT _RELAXATION { { / c r o s s L i n k } } .
2016-02-12 16:00:36 +00:00
* @ property frictionRelaxation
* @ type { Number }
* /
2016-09-21 22:22:08 +00:00
this . frictionRelaxation = typeof ( options . frictionRelaxation ) !== "undefined" ? Number ( options . frictionRelaxation ) : Equation . DEFAULT _RELAXATION ;
2016-02-12 16:00:36 +00:00
/ * *
* Will add surface velocity to this material . If bodyA rests on top if bodyB , and the surface velocity is positive , bodyA will slide to the right .
* @ property { Number } surfaceVelocity
2016-09-21 22:22:08 +00:00
* @ default 0
2016-02-12 16:00:36 +00:00
* /
2016-09-21 22:22:08 +00:00
this . surfaceVelocity = typeof ( options . surfaceVelocity ) !== "undefined" ? Number ( options . surfaceVelocity ) : 0 ;
2016-02-12 16:00:36 +00:00
/ * *
* Offset to be set on ContactEquations . A positive value will make the bodies penetrate more into each other . Can be useful in scenes where contacts need to be more persistent , for example when stacking . Aka "cure for nervous contacts" .
* @ property contactSkinSize
* @ type { Number }
* /
this . contactSkinSize = 0.005 ;
}
ContactMaterial . idCounter = 0 ;
} , { "../equations/Equation" : 22 , "./Material" : 28 } ] , 28 : [ function ( _dereq _ , module , exports ) {
module . exports = Material ;
/ * *
* Defines a physics material .
* @ class Material
* @ constructor
* @ param { number } id Material identifier
* @ author schteppe
* /
function Material ( id ) {
/ * *
* The material identifier
* @ property id
* @ type { Number }
* /
this . id = id || Material . idCounter ++ ;
}
Material . idCounter = 0 ;
} , { } ] , 29 : [ function ( _dereq _ , module , exports ) {
/ *
PolyK library
url : http : //polyk.ivank.net
Released under MIT licence .
Copyright ( c ) 2012 Ivan Kuckir
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 PolyK = { } ;
/ *
Is Polygon self - intersecting ?
O ( n ^ 2 )
* /
/ *
PolyK . IsSimple = function ( p )
{
var n = p . length >> 1 ;
if ( n < 4 ) return true ;
var a1 = new PolyK . _P ( ) , a2 = new PolyK . _P ( ) ;
var b1 = new PolyK . _P ( ) , b2 = new PolyK . _P ( ) ;
var c = new PolyK . _P ( ) ;
for ( var i = 0 ; i < n ; i ++ )
{
a1 . x = p [ 2 * i ] ;
a1 . y = p [ 2 * i + 1 ] ;
if ( i == n - 1 ) { a2 . x = p [ 0 ] ; a2 . y = p [ 1 ] ; }
else { a2 . x = p [ 2 * i + 2 ] ; a2 . y = p [ 2 * i + 3 ] ; }
for ( var j = 0 ; j < n ; j ++ )
{
if ( Math . abs ( i - j ) < 2 ) continue ;
if ( j == n - 1 && i == 0 ) continue ;
if ( i == n - 1 && j == 0 ) continue ;
b1 . x = p [ 2 * j ] ;
b1 . y = p [ 2 * j + 1 ] ;
if ( j == n - 1 ) { b2 . x = p [ 0 ] ; b2 . y = p [ 1 ] ; }
else { b2 . x = p [ 2 * j + 2 ] ; b2 . y = p [ 2 * j + 3 ] ; }
if ( PolyK . _GetLineIntersection ( a1 , a2 , b1 , b2 , c ) != null ) return false ;
}
}
return true ;
}
PolyK . IsConvex = function ( p )
{
if ( p . length < 6 ) return true ;
var l = p . length - 4 ;
for ( var i = 0 ; i < l ; i += 2 )
if ( ! PolyK . _convex ( p [ i ] , p [ i + 1 ] , p [ i + 2 ] , p [ i + 3 ] , p [ i + 4 ] , p [ i + 5 ] ) ) return false ;
if ( ! PolyK . _convex ( p [ l ] , p [ l + 1 ] , p [ l + 2 ] , p [ l + 3 ] , p [ 0 ] , p [ 1 ] ) ) return false ;
if ( ! PolyK . _convex ( p [ l + 2 ] , p [ l + 3 ] , p [ 0 ] , p [ 1 ] , p [ 2 ] , p [ 3 ] ) ) return false ;
return true ;
}
* /
PolyK . GetArea = function ( p )
{
if ( p . length < 6 ) return 0 ;
var l = p . length - 2 ;
var sum = 0 ;
for ( var i = 0 ; i < l ; i += 2 )
sum += ( p [ i + 2 ] - p [ i ] ) * ( p [ i + 1 ] + p [ i + 3 ] ) ;
sum += ( p [ 0 ] - p [ l ] ) * ( p [ l + 1 ] + p [ 1 ] ) ;
return - sum * 0.5 ;
}
/ *
PolyK . GetAABB = function ( p )
{
var minx = Infinity ;
var miny = Infinity ;
var maxx = - minx ;
var maxy = - miny ;
for ( var i = 0 ; i < p . length ; i += 2 )
{
minx = Math . min ( minx , p [ i ] ) ;
maxx = Math . max ( maxx , p [ i ] ) ;
miny = Math . min ( miny , p [ i + 1 ] ) ;
maxy = Math . max ( maxy , p [ i + 1 ] ) ;
}
return { x : minx , y : miny , width : maxx - minx , height : maxy - miny } ;
}
* /
PolyK . Triangulate = function ( p )
{
var n = p . length >> 1 ;
if ( n < 3 ) return [ ] ;
var tgs = [ ] ;
var avl = [ ] ;
for ( var i = 0 ; i < n ; i ++ ) avl . push ( i ) ;
var i = 0 ;
var al = n ;
while ( al > 3 )
{
var i0 = avl [ ( i + 0 ) % al ] ;
var i1 = avl [ ( i + 1 ) % al ] ;
var i2 = avl [ ( i + 2 ) % al ] ;
var ax = p [ 2 * i0 ] , ay = p [ 2 * i0 + 1 ] ;
var bx = p [ 2 * i1 ] , by = p [ 2 * i1 + 1 ] ;
var cx = p [ 2 * i2 ] , cy = p [ 2 * i2 + 1 ] ;
var earFound = false ;
if ( PolyK . _convex ( ax , ay , bx , by , cx , cy ) )
{
earFound = true ;
for ( var j = 0 ; j < al ; j ++ )
{
var vi = avl [ j ] ;
if ( vi == i0 || vi == i1 || vi == i2 ) continue ;
if ( PolyK . _PointInTriangle ( p [ 2 * vi ] , p [ 2 * vi + 1 ] , ax , ay , bx , by , cx , cy ) ) { earFound = false ; break ; }
}
}
if ( earFound )
{
tgs . push ( i0 , i1 , i2 ) ;
avl . splice ( ( i + 1 ) % al , 1 ) ;
al -- ;
i = 0 ;
}
else if ( i ++ > 3 * al ) break ; // no convex angles :(
}
tgs . push ( avl [ 0 ] , avl [ 1 ] , avl [ 2 ] ) ;
return tgs ;
}
/ *
PolyK . ContainsPoint = function ( p , px , py )
{
var n = p . length >> 1 ;
var ax , ay , bx = p [ 2 * n - 2 ] - px , by = p [ 2 * n - 1 ] - py ;
var depth = 0 ;
for ( var i = 0 ; i < n ; i ++ )
{
ax = bx ; ay = by ;
bx = p [ 2 * i ] - px ;
by = p [ 2 * i + 1 ] - py ;
if ( ay < 0 && by < 0 ) continue ; // both "up" or both "donw"
if ( ay >= 0 && by >= 0 ) continue ; // both "up" or both "donw"
if ( ax < 0 && bx < 0 ) continue ;
var lx = ax + ( bx - ax ) * ( - ay ) / ( by - ay ) ;
if ( lx > 0 ) depth ++ ;
}
return ( depth & 1 ) == 1 ;
}
PolyK . Slice = function ( p , ax , ay , bx , by )
{
if ( PolyK . ContainsPoint ( p , ax , ay ) || PolyK . ContainsPoint ( p , bx , by ) ) return [ p . slice ( 0 ) ] ;
var a = new PolyK . _P ( ax , ay ) ;
var b = new PolyK . _P ( bx , by ) ;
var iscs = [ ] ; // intersections
var ps = [ ] ; // points
for ( var i = 0 ; i < p . length ; i += 2 ) ps . push ( new PolyK . _P ( p [ i ] , p [ i + 1 ] ) ) ;
for ( var i = 0 ; i < ps . length ; i ++ )
{
var isc = new PolyK . _P ( 0 , 0 ) ;
isc = PolyK . _GetLineIntersection ( a , b , ps [ i ] , ps [ ( i + 1 ) % ps . length ] , isc ) ;
if ( isc )
{
isc . flag = true ;
iscs . push ( isc ) ;
ps . splice ( i + 1 , 0 , isc ) ;
i ++ ;
}
}
if ( iscs . length == 0 ) return [ p . slice ( 0 ) ] ;
var comp = function ( u , v ) { return PolyK . _P . dist ( a , u ) - PolyK . _P . dist ( a , v ) ; }
iscs . sort ( comp ) ;
var pgs = [ ] ;
var dir = 0 ;
while ( iscs . length > 0 )
{
var n = ps . length ;
var i0 = iscs [ 0 ] ;
var i1 = iscs [ 1 ] ;
var ind0 = ps . indexOf ( i0 ) ;
var ind1 = ps . indexOf ( i1 ) ;
var solved = false ;
if ( PolyK . _firstWithFlag ( ps , ind0 ) == ind1 ) solved = true ;
else
{
i0 = iscs [ 1 ] ;
i1 = iscs [ 0 ] ;
ind0 = ps . indexOf ( i0 ) ;
ind1 = ps . indexOf ( i1 ) ;
if ( PolyK . _firstWithFlag ( ps , ind0 ) == ind1 ) solved = true ;
}
if ( solved )
{
dir -- ;
var pgn = PolyK . _getPoints ( ps , ind0 , ind1 ) ;
pgs . push ( pgn ) ;
ps = PolyK . _getPoints ( ps , ind1 , ind0 ) ;
i0 . flag = i1 . flag = false ;
iscs . splice ( 0 , 2 ) ;
if ( iscs . length == 0 ) pgs . push ( ps ) ;
}
else { dir ++ ; iscs . reverse ( ) ; }
if ( dir > 1 ) break ;
}
var result = [ ] ;
for ( var i = 0 ; i < pgs . length ; i ++ )
{
var pg = pgs [ i ] ;
var npg = [ ] ;
for ( var j = 0 ; j < pg . length ; j ++ ) npg . push ( pg [ j ] . x , pg [ j ] . y ) ;
result . push ( npg ) ;
}
return result ;
}
PolyK . Raycast = function ( p , x , y , dx , dy , isc )
{
var l = p . length - 2 ;
var tp = PolyK . _tp ;
var a1 = tp [ 0 ] , a2 = tp [ 1 ] ,
b1 = tp [ 2 ] , b2 = tp [ 3 ] , c = tp [ 4 ] ;
a1 . x = x ; a1 . y = y ;
a2 . x = x + dx ; a2 . y = y + dy ;
if ( isc == null ) isc = { dist : 0 , edge : 0 , norm : { x : 0 , y : 0 } , refl : { x : 0 , y : 0 } } ;
isc . dist = Infinity ;
for ( var i = 0 ; i < l ; i += 2 )
{
b1 . x = p [ i ] ; b1 . y = p [ i + 1 ] ;
b2 . x = p [ i + 2 ] ; b2 . y = p [ i + 3 ] ;
var nisc = PolyK . _RayLineIntersection ( a1 , a2 , b1 , b2 , c ) ;
if ( nisc ) PolyK . _updateISC ( dx , dy , a1 , b1 , b2 , c , i / 2 , isc ) ;
}
b1 . x = b2 . x ; b1 . y = b2 . y ;
b2 . x = p [ 0 ] ; b2 . y = p [ 1 ] ;
var nisc = PolyK . _RayLineIntersection ( a1 , a2 , b1 , b2 , c ) ;
if ( nisc ) PolyK . _updateISC ( dx , dy , a1 , b1 , b2 , c , p . length / 2 , isc ) ;
return ( isc . dist != Infinity ) ? isc : null ;
}
PolyK . ClosestEdge = function ( p , x , y , isc )
{
var l = p . length - 2 ;
var tp = PolyK . _tp ;
var a1 = tp [ 0 ] ,
b1 = tp [ 2 ] , b2 = tp [ 3 ] , c = tp [ 4 ] ;
a1 . x = x ; a1 . y = y ;
if ( isc == null ) isc = { dist : 0 , edge : 0 , point : { x : 0 , y : 0 } , norm : { x : 0 , y : 0 } } ;
isc . dist = Infinity ;
for ( var i = 0 ; i < l ; i += 2 )
{
b1 . x = p [ i ] ; b1 . y = p [ i + 1 ] ;
b2 . x = p [ i + 2 ] ; b2 . y = p [ i + 3 ] ;
PolyK . _pointLineDist ( a1 , b1 , b2 , i >> 1 , isc ) ;
}
b1 . x = b2 . x ; b1 . y = b2 . y ;
b2 . x = p [ 0 ] ; b2 . y = p [ 1 ] ;
PolyK . _pointLineDist ( a1 , b1 , b2 , l >> 1 , isc ) ;
var idst = 1 / isc . dist ;
isc . norm . x = ( x - isc . point . x ) * idst ;
isc . norm . y = ( y - isc . point . y ) * idst ;
return isc ;
}
PolyK . _pointLineDist = function ( p , a , b , edge , isc )
{
var x = p . x , y = p . y , x1 = a . x , y1 = a . y , x2 = b . x , y2 = b . y ;
var A = x - x1 ;
var B = y - y1 ;
var C = x2 - x1 ;
var D = y2 - y1 ;
var dot = A * C + B * D ;
var len _sq = C * C + D * D ;
var param = dot / len _sq ;
var xx , yy ;
if ( param < 0 || ( x1 == x2 && y1 == y2 ) ) {
xx = x1 ;
yy = y1 ;
}
else if ( param > 1 ) {
xx = x2 ;
yy = y2 ;
}
else {
xx = x1 + param * C ;
yy = y1 + param * D ;
}
var dx = x - xx ;
var dy = y - yy ;
var dst = Math . sqrt ( dx * dx + dy * dy ) ;
if ( dst < isc . dist )
{
isc . dist = dst ;
isc . edge = edge ;
isc . point . x = xx ;
isc . point . y = yy ;
}
}
PolyK . _updateISC = function ( dx , dy , a1 , b1 , b2 , c , edge , isc )
{
var nrl = PolyK . _P . dist ( a1 , c ) ;
if ( nrl < isc . dist )
{
var ibl = 1 / PolyK . _P . dist ( b1 , b2 ) ;
var nx = - ( b2 . y - b1 . y ) * ibl ;
var ny = ( b2 . x - b1 . x ) * ibl ;
var ddot = 2 * ( dx * nx + dy * ny ) ;
isc . dist = nrl ;
isc . norm . x = nx ;
isc . norm . y = ny ;
isc . refl . x = - ddot * nx + dx ;
isc . refl . y = - ddot * ny + dy ;
isc . edge = edge ;
}
}
PolyK . _getPoints = function ( ps , ind0 , ind1 )
{
var n = ps . length ;
var nps = [ ] ;
if ( ind1 < ind0 ) ind1 += n ;
for ( var i = ind0 ; i <= ind1 ; i ++ ) nps . push ( ps [ i % n ] ) ;
return nps ;
}
PolyK . _firstWithFlag = function ( ps , ind )
{
var n = ps . length ;
while ( true )
{
ind = ( ind + 1 ) % n ;
if ( ps [ ind ] . flag ) return ind ;
}
}
* /
PolyK . _PointInTriangle = function ( px , py , ax , ay , bx , by , cx , cy )
{
var v0x = cx - ax ;
var v0y = cy - ay ;
var v1x = bx - ax ;
var v1y = by - ay ;
var v2x = px - ax ;
var v2y = py - ay ;
var dot00 = v0x * v0x + v0y * v0y ;
var dot01 = v0x * v1x + v0y * v1y ;
var dot02 = v0x * v2x + v0y * v2y ;
var dot11 = v1x * v1x + v1y * v1y ;
var dot12 = v1x * v2x + v1y * v2y ;
var invDenom = 1 / ( dot00 * dot11 - dot01 * dot01 ) ;
var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom ;
var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom ;
// Check if point is in triangle
return ( u >= 0 ) && ( v >= 0 ) && ( u + v < 1 ) ;
}
/ *
PolyK . _RayLineIntersection = function ( a1 , a2 , b1 , b2 , c )
{
var dax = ( a1 . x - a2 . x ) , dbx = ( b1 . x - b2 . x ) ;
var day = ( a1 . y - a2 . y ) , dby = ( b1 . y - b2 . y ) ;
var Den = dax * dby - day * dbx ;
if ( Den == 0 ) return null ; // parallel
var A = ( a1 . x * a2 . y - a1 . y * a2 . x ) ;
var B = ( b1 . x * b2 . y - b1 . y * b2 . x ) ;
var I = c ;
var iDen = 1 / Den ;
I . x = ( A * dbx - dax * B ) * iDen ;
I . y = ( A * dby - day * B ) * iDen ;
if ( ! PolyK . _InRect ( I , b1 , b2 ) ) return null ;
if ( ( day > 0 && I . y > a1 . y ) || ( day < 0 && I . y < a1 . y ) ) return null ;
if ( ( dax > 0 && I . x > a1 . x ) || ( dax < 0 && I . x < a1 . x ) ) return null ;
return I ;
}
PolyK . _GetLineIntersection = function ( a1 , a2 , b1 , b2 , c )
{
var dax = ( a1 . x - a2 . x ) , dbx = ( b1 . x - b2 . x ) ;
var day = ( a1 . y - a2 . y ) , dby = ( b1 . y - b2 . y ) ;
var Den = dax * dby - day * dbx ;
if ( Den == 0 ) return null ; // parallel
var A = ( a1 . x * a2 . y - a1 . y * a2 . x ) ;
var B = ( b1 . x * b2 . y - b1 . y * b2 . x ) ;
var I = c ;
I . x = ( A * dbx - dax * B ) / Den ;
I . y = ( A * dby - day * B ) / Den ;
if ( PolyK . _InRect ( I , a1 , a2 ) && PolyK . _InRect ( I , b1 , b2 ) ) return I ;
return null ;
}
PolyK . _InRect = function ( a , b , c )
{
if ( b . x == c . x ) return ( a . y >= Math . min ( b . y , c . y ) && a . y <= Math . max ( b . y , c . y ) ) ;
if ( b . y == c . y ) return ( a . x >= Math . min ( b . x , c . x ) && a . x <= Math . max ( b . x , c . x ) ) ;
if ( a . x >= Math . min ( b . x , c . x ) && a . x <= Math . max ( b . x , c . x )
&& a . y >= Math . min ( b . y , c . y ) && a . y <= Math . max ( b . y , c . y ) )
return true ;
return false ;
}
* /
PolyK . _convex = function ( ax , ay , bx , by , cx , cy )
{
return ( ay - by ) * ( cx - bx ) + ( bx - ax ) * ( cy - by ) >= 0 ;
}
/ *
PolyK . _P = function ( x , y )
{
this . x = x ;
this . y = y ;
this . flag = false ;
}
PolyK . _P . prototype . toString = function ( )
{
return "Point [" + this . x + ", " + this . y + "]" ;
}
PolyK . _P . dist = function ( a , b )
{
var dx = b . x - a . x ;
var dy = b . y - a . y ;
return Math . sqrt ( dx * dx + dy * dy ) ;
}
PolyK . _tp = [ ] ;
for ( var i = 0 ; i < 10 ; i ++ ) PolyK . _tp . push ( new PolyK . _P ( 0 , 0 ) ) ;
* /
module . exports = PolyK ;
} , { } ] , 30 : [ function ( _dereq _ , module , exports ) {
/ * C o p y r i g h t ( c ) 2 0 1 3 , B r a n d o n J o n e s , C o l i n M a c K e n z i e I V . A l l r i g h t s r e s e r v e d .
Redistribution and use in source and binary forms , with or without modification ,
are permitted provided that the following conditions are met :
* Redistributions of source code must retain the above copyright notice , this
list of conditions and the following disclaimer .
* Redistributions in binary form must reproduce the above copyright notice ,
this list of conditions and the following disclaimer in the documentation
and / or other materials provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE . * /
/ * *
* The vec2 object from glMatrix , with some extensions and some removed methods . See http : //glmatrix.net.
* @ class vec2
* /
var vec2 = module . exports = { } ;
var Utils = _dereq _ ( '../utils/Utils' ) ;
/ * *
* Make a cross product and only return the z component
* @ method crossLength
* @ static
* @ param { Array } a
* @ param { Array } b
* @ return { Number }
* /
vec2 . crossLength = function ( a , b ) {
return a [ 0 ] * b [ 1 ] - a [ 1 ] * b [ 0 ] ;
} ;
/ * *
* Cross product between a vector and the Z component of a vector
* @ method crossVZ
* @ static
* @ param { Array } out
* @ param { Array } vec
* @ param { Number } zcomp
* @ return { Number }
* /
vec2 . crossVZ = function ( out , vec , zcomp ) {
vec2 . rotate ( out , vec , - Math . PI / 2 ) ; // Rotate according to the right hand rule
vec2 . scale ( out , out , zcomp ) ; // Scale with z
return out ;
} ;
/ * *
* Cross product between a vector and the Z component of a vector
* @ method crossZV
* @ static
* @ param { Array } out
* @ param { Number } zcomp
* @ param { Array } vec
* @ return { Number }
* /
vec2 . crossZV = function ( out , zcomp , vec ) {
vec2 . rotate ( out , vec , Math . PI / 2 ) ; // Rotate according to the right hand rule
vec2 . scale ( out , out , zcomp ) ; // Scale with z
return out ;
} ;
/ * *
* Rotate a vector by an angle
* @ method rotate
* @ static
* @ param { Array } out
* @ param { Array } a
* @ param { Number } angle
* /
vec2 . rotate = function ( out , a , angle ) {
if ( angle !== 0 ) {
var c = Math . cos ( angle ) ,
s = Math . sin ( angle ) ,
x = a [ 0 ] ,
y = a [ 1 ] ;
out [ 0 ] = c * x - s * y ;
out [ 1 ] = s * x + c * y ;
} else {
out [ 0 ] = a [ 0 ] ;
out [ 1 ] = a [ 1 ] ;
}
} ;
/ * *
* Rotate a vector 90 degrees clockwise
* @ method rotate90cw
* @ static
* @ param { Array } out
* @ param { Array } a
* @ param { Number } angle
* /
vec2 . rotate90cw = function ( out , a ) {
var x = a [ 0 ] ;
var y = a [ 1 ] ;
out [ 0 ] = y ;
out [ 1 ] = - x ;
} ;
/ * *
* Transform a point position to local frame .
* @ method toLocalFrame
* @ param { Array } out
* @ param { Array } worldPoint
* @ param { Array } framePosition
* @ param { Number } frameAngle
* /
vec2 . toLocalFrame = function ( out , worldPoint , framePosition , frameAngle ) {
vec2 . copy ( out , worldPoint ) ;
vec2 . sub ( out , out , framePosition ) ;
vec2 . rotate ( out , out , - frameAngle ) ;
} ;
/ * *
* Transform a point position to global frame .
* @ method toGlobalFrame
* @ param { Array } out
* @ param { Array } localPoint
* @ param { Array } framePosition
* @ param { Number } frameAngle
* /
vec2 . toGlobalFrame = function ( out , localPoint , framePosition , frameAngle ) {
vec2 . copy ( out , localPoint ) ;
vec2 . rotate ( out , out , frameAngle ) ;
vec2 . add ( out , out , framePosition ) ;
} ;
/ * *
* Transform a vector to local frame .
* @ method vectorToLocalFrame
* @ param { Array } out
* @ param { Array } worldVector
* @ param { Number } frameAngle
* /
vec2 . vectorToLocalFrame = function ( out , worldVector , frameAngle ) {
vec2 . rotate ( out , worldVector , - frameAngle ) ;
} ;
/ * *
* Transform a point position to global frame .
* @ method toGlobalFrame
* @ param { Array } out
* @ param { Array } localVector
* @ param { Number } frameAngle
* /
vec2 . vectorToGlobalFrame = function ( out , localVector , frameAngle ) {
vec2 . rotate ( out , localVector , frameAngle ) ;
} ;
/ * *
* Compute centroid of a triangle spanned by vectors a , b , c . See http : //easycalculation.com/analytical/learn-centroid.php
* @ method centroid
* @ static
* @ param { Array } out
* @ param { Array } a
* @ param { Array } b
* @ param { Array } c
* @ return { Array } The out object
* /
vec2 . centroid = function ( out , a , b , c ) {
vec2 . add ( out , a , b ) ;
vec2 . add ( out , out , c ) ;
vec2 . scale ( out , out , 1 / 3 ) ;
return out ;
} ;
/ * *
* Creates a new , empty vec2
* @ static
* @ method create
* @ return { Array } a new 2 D vector
* /
vec2 . create = function ( ) {
var out = new Utils . ARRAY _TYPE ( 2 ) ;
out [ 0 ] = 0 ;
out [ 1 ] = 0 ;
return out ;
} ;
/ * *
* Creates a new vec2 initialized with values from an existing vector
* @ static
* @ method clone
* @ param { Array } a vector to clone
* @ return { Array } a new 2 D vector
* /
vec2 . clone = function ( a ) {
var out = new Utils . ARRAY _TYPE ( 2 ) ;
out [ 0 ] = a [ 0 ] ;
out [ 1 ] = a [ 1 ] ;
return out ;
} ;
/ * *
* Creates a new vec2 initialized with the given values
* @ static
* @ method fromValues
* @ param { Number } x X component
* @ param { Number } y Y component
* @ return { Array } a new 2 D vector
* /
vec2 . fromValues = function ( x , y ) {
var out = new Utils . ARRAY _TYPE ( 2 ) ;
out [ 0 ] = x ;
out [ 1 ] = y ;
return out ;
} ;
/ * *
* Copy the values from one vec2 to another
* @ static
* @ method copy
* @ param { Array } out the receiving vector
* @ param { Array } a the source vector
* @ return { Array } out
* /
vec2 . copy = function ( out , a ) {
out [ 0 ] = a [ 0 ] ;
out [ 1 ] = a [ 1 ] ;
return out ;
} ;
/ * *
* Set the components of a vec2 to the given values
* @ static
* @ method set
* @ param { Array } out the receiving vector
* @ param { Number } x X component
* @ param { Number } y Y component
* @ return { Array } out
* /
vec2 . set = function ( out , x , y ) {
out [ 0 ] = x ;
out [ 1 ] = y ;
return out ;
} ;
/ * *
* Adds two vec2 ' s
* @ static
* @ method add
* @ param { Array } out the receiving vector
* @ param { Array } a the first operand
* @ param { Array } b the second operand
* @ return { Array } out
* /
vec2 . add = function ( out , a , b ) {
out [ 0 ] = a [ 0 ] + b [ 0 ] ;
out [ 1 ] = a [ 1 ] + b [ 1 ] ;
return out ;
} ;
/ * *
* Subtracts two vec2 ' s
* @ static
* @ method subtract
* @ param { Array } out the receiving vector
* @ param { Array } a the first operand
* @ param { Array } b the second operand
* @ return { Array } out
* /
vec2 . subtract = function ( out , a , b ) {
out [ 0 ] = a [ 0 ] - b [ 0 ] ;
out [ 1 ] = a [ 1 ] - b [ 1 ] ;
return out ;
} ;
/ * *
* Alias for vec2 . subtract
* @ static
* @ method sub
* /
vec2 . sub = vec2 . subtract ;
/ * *
* Multiplies two vec2 ' s
* @ static
* @ method multiply
* @ param { Array } out the receiving vector
* @ param { Array } a the first operand
* @ param { Array } b the second operand
* @ return { Array } out
* /
vec2 . multiply = function ( out , a , b ) {
out [ 0 ] = a [ 0 ] * b [ 0 ] ;
out [ 1 ] = a [ 1 ] * b [ 1 ] ;
return out ;
} ;
/ * *
* Alias for vec2 . multiply
* @ static
* @ method mul
* /
vec2 . mul = vec2 . multiply ;
/ * *
* Divides two vec2 ' s
* @ static
* @ method divide
* @ param { Array } out the receiving vector
* @ param { Array } a the first operand
* @ param { Array } b the second operand
* @ return { Array } out
* /
vec2 . divide = function ( out , a , b ) {
out [ 0 ] = a [ 0 ] / b [ 0 ] ;
out [ 1 ] = a [ 1 ] / b [ 1 ] ;
return out ;
} ;
/ * *
* Alias for vec2 . divide
* @ static
* @ method div
* /
vec2 . div = vec2 . divide ;
/ * *
* Scales a vec2 by a scalar number
* @ static
* @ method scale
* @ param { Array } out the receiving vector
* @ param { Array } a the vector to scale
* @ param { Number } b amount to scale the vector by
* @ return { Array } out
* /
vec2 . scale = function ( out , a , b ) {
out [ 0 ] = a [ 0 ] * b ;
out [ 1 ] = a [ 1 ] * b ;
return out ;
} ;
/ * *
* Calculates the euclidian distance between two vec2 ' s
* @ static
* @ method distance
* @ param { Array } a the first operand
* @ param { Array } b the second operand
* @ return { Number } distance between a and b
* /
vec2 . distance = function ( a , b ) {
var x = b [ 0 ] - a [ 0 ] ,
y = b [ 1 ] - a [ 1 ] ;
return Math . sqrt ( x * x + y * y ) ;
} ;
/ * *
* Alias for vec2 . distance
* @ static
* @ method dist
* /
vec2 . dist = vec2 . distance ;
/ * *
* Calculates the squared euclidian distance between two vec2 ' s
* @ static
* @ method squaredDistance
* @ param { Array } a the first operand
* @ param { Array } b the second operand
* @ return { Number } squared distance between a and b
* /
vec2 . squaredDistance = function ( a , b ) {
var x = b [ 0 ] - a [ 0 ] ,
y = b [ 1 ] - a [ 1 ] ;
return x * x + y * y ;
} ;
/ * *
* Alias for vec2 . squaredDistance
* @ static
* @ method sqrDist
* /
vec2 . sqrDist = vec2 . squaredDistance ;
/ * *
* Calculates the length of a vec2
* @ static
* @ method length
* @ param { Array } a vector to calculate length of
* @ return { Number } length of a
* /
vec2 . length = function ( a ) {
var x = a [ 0 ] ,
y = a [ 1 ] ;
return Math . sqrt ( x * x + y * y ) ;
} ;
/ * *
* Alias for vec2 . length
* @ method len
* @ static
* /
vec2 . len = vec2 . length ;
/ * *
* Calculates the squared length of a vec2
* @ static
* @ method squaredLength
* @ param { Array } a vector to calculate squared length of
* @ return { Number } squared length of a
* /
vec2 . squaredLength = function ( a ) {
var x = a [ 0 ] ,
y = a [ 1 ] ;
return x * x + y * y ;
} ;
/ * *
* Alias for vec2 . squaredLength
* @ static
* @ method sqrLen
* /
vec2 . sqrLen = vec2 . squaredLength ;
/ * *
* Negates the components of a vec2
* @ static
* @ method negate
* @ param { Array } out the receiving vector
* @ param { Array } a vector to negate
* @ return { Array } out
* /
vec2 . negate = function ( out , a ) {
out [ 0 ] = - a [ 0 ] ;
out [ 1 ] = - a [ 1 ] ;
return out ;
} ;
/ * *
* Normalize a vec2
* @ static
* @ method normalize
* @ param { Array } out the receiving vector
* @ param { Array } a vector to normalize
* @ return { Array } out
* /
vec2 . normalize = function ( out , a ) {
var x = a [ 0 ] ,
y = a [ 1 ] ;
var len = x * x + y * y ;
if ( len > 0 ) {
//TODO: evaluate use of glm_invsqrt here?
len = 1 / Math . sqrt ( len ) ;
out [ 0 ] = a [ 0 ] * len ;
out [ 1 ] = a [ 1 ] * len ;
}
return out ;
} ;
/ * *
* Calculates the dot product of two vec2 ' s
* @ static
* @ method dot
* @ param { Array } a the first operand
* @ param { Array } b the second operand
* @ return { Number } dot product of a and b
* /
vec2 . dot = function ( a , b ) {
return a [ 0 ] * b [ 0 ] + a [ 1 ] * b [ 1 ] ;
} ;
/ * *
* Returns a string representation of a vector
* @ static
* @ method str
* @ param { Array } vec vector to represent as a string
* @ return { String } string representation of the vector
* /
vec2 . str = function ( a ) {
return 'vec2(' + a [ 0 ] + ', ' + a [ 1 ] + ')' ;
} ;
/ * *
* Linearly interpolate / mix two vectors .
* @ static
* @ method lerp
* @ param { Array } out
* @ param { Array } a First vector
* @ param { Array } b Second vector
* @ param { number } t Lerp factor
* /
vec2 . lerp = function ( out , a , b , t ) {
var ax = a [ 0 ] ,
ay = a [ 1 ] ;
out [ 0 ] = ax + t * ( b [ 0 ] - ax ) ;
out [ 1 ] = ay + t * ( b [ 1 ] - ay ) ;
return out ;
} ;
/ * *
* Reflect a vector along a normal .
* @ static
* @ method reflect
* @ param { Array } out
* @ param { Array } vector
* @ param { Array } normal
* /
vec2 . reflect = function ( out , vector , normal ) {
var dot = vector [ 0 ] * normal [ 0 ] + vector [ 1 ] * normal [ 1 ] ;
out [ 0 ] = vector [ 0 ] - 2 * normal [ 0 ] * dot ;
out [ 1 ] = vector [ 1 ] - 2 * normal [ 1 ] * dot ;
} ;
/ * *
* Get the intersection point between two line segments .
* @ static
* @ method getLineSegmentsIntersection
* @ param { Array } out
* @ param { Array } p0
* @ param { Array } p1
* @ param { Array } p2
* @ param { Array } p3
* @ return { boolean } True if there was an intersection , otherwise false .
* /
vec2 . getLineSegmentsIntersection = function ( out , p0 , p1 , p2 , p3 ) {
var t = vec2 . getLineSegmentsIntersectionFraction ( p0 , p1 , p2 , p3 ) ;
if ( t < 0 ) {
return false ;
} else {
out [ 0 ] = p0 [ 0 ] + ( t * ( p1 [ 0 ] - p0 [ 0 ] ) ) ;
out [ 1 ] = p0 [ 1 ] + ( t * ( p1 [ 1 ] - p0 [ 1 ] ) ) ;
return true ;
}
} ;
/ * *
* Get the intersection fraction between two line segments . If successful , the intersection is at p0 + t * ( p1 - p0 )
* @ static
* @ method getLineSegmentsIntersectionFraction
* @ param { Array } p0
* @ param { Array } p1
* @ param { Array } p2
* @ param { Array } p3
* @ return { number } A number between 0 and 1 if there was an intersection , otherwise - 1.
* /
vec2 . getLineSegmentsIntersectionFraction = function ( p0 , p1 , p2 , p3 ) {
var s1 _x = p1 [ 0 ] - p0 [ 0 ] ;
var s1 _y = p1 [ 1 ] - p0 [ 1 ] ;
var s2 _x = p3 [ 0 ] - p2 [ 0 ] ;
var s2 _y = p3 [ 1 ] - p2 [ 1 ] ;
var s , t ;
s = ( - s1 _y * ( p0 [ 0 ] - p2 [ 0 ] ) + s1 _x * ( p0 [ 1 ] - p2 [ 1 ] ) ) / ( - s2 _x * s1 _y + s1 _x * s2 _y ) ;
t = ( s2 _x * ( p0 [ 1 ] - p2 [ 1 ] ) - s2 _y * ( p0 [ 0 ] - p2 [ 0 ] ) ) / ( - s2 _x * s1 _y + s1 _x * s2 _y ) ;
if ( s >= 0 && s <= 1 && t >= 0 && t <= 1 ) { // Collision detected
return t ;
}
return - 1 ; // No collision
} ;
} , { "../utils/Utils" : 57 } ] , 31 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' )
, decomp = _dereq _ ( 'poly-decomp' )
, Convex = _dereq _ ( '../shapes/Convex' )
, RaycastResult = _dereq _ ( '../collision/RaycastResult' )
, Ray = _dereq _ ( '../collision/Ray' )
, AABB = _dereq _ ( '../collision/AABB' )
, EventEmitter = _dereq _ ( '../events/EventEmitter' ) ;
module . exports = Body ;
/ * *
* A rigid body . Has got a center of mass , position , velocity and a number of
* shapes that are used for collisions .
*
* @ class Body
* @ constructor
* @ extends EventEmitter
* @ param { Array } [ options . force ]
* @ param { Array } [ options . position ]
* @ param { Array } [ options . velocity ]
* @ param { Boolean } [ options . allowSleep ]
* @ param { Boolean } [ options . collisionResponse ]
* @ param { Number } [ options . angle = 0 ]
* @ param { Number } [ options . angularForce = 0 ]
* @ param { Number } [ options . angularVelocity = 0 ]
* @ param { Number } [ options . ccdIterations = 10 ]
* @ param { Number } [ options . ccdSpeedThreshold = - 1 ]
* @ param { Number } [ options . fixedRotation = false ]
* @ param { Number } [ options . gravityScale ]
* @ param { Number } [ options . id ]
* @ param { Number } [ options . mass = 0 ] A number >= 0. If zero , the . type will be set to Body . STATIC .
* @ param { Number } [ options . sleepSpeedLimit ]
* @ param { Number } [ options . sleepTimeLimit ]
* @ param { Object } [ options ]
*
* @ example
*
* // Create a typical dynamic body
* var body = new Body ( {
* mass : 1 ,
* position : [ 0 , 0 ] ,
* angle : 0 ,
* velocity : [ 0 , 0 ] ,
* angularVelocity : 0
* } ) ;
*
* // Add a circular shape to the body
* body . addShape ( new Circle ( { radius : 1 } ) ) ;
*
* // Add the body to the world
* world . addBody ( body ) ;
* /
function Body ( options ) {
options = options || { } ;
EventEmitter . call ( this ) ;
/ * *
* The body identifyer
* @ property id
* @ type { Number }
* /
this . id = options . id || ++ Body . _idCounter ;
/ * *
* The world that this body is added to . This property is set to NULL if the body is not added to any world .
* @ property world
* @ type { World }
* /
this . world = null ;
/ * *
* The shapes of the body .
*
* @ property shapes
* @ type { Array }
* /
this . shapes = [ ] ;
/ * *
* The mass of the body .
* @ property mass
* @ type { number }
* /
this . mass = options . mass || 0 ;
/ * *
* The inverse mass of the body .
* @ property invMass
* @ type { number }
* /
this . invMass = 0 ;
/ * *
* The inertia of the body around the Z axis .
* @ property inertia
* @ type { number }
* /
this . inertia = 0 ;
/ * *
* The inverse inertia of the body .
* @ property invInertia
* @ type { number }
* /
this . invInertia = 0 ;
this . invMassSolve = 0 ;
this . invInertiaSolve = 0 ;
/ * *
* Set to true if you want to fix the rotation of the body .
* @ property fixedRotation
* @ type { Boolean }
* /
this . fixedRotation = ! ! options . fixedRotation ;
/ * *
* Set to true if you want to fix the body movement along the X axis . The body will still be able to move along Y .
* @ property { Boolean } fixedX
* /
this . fixedX = ! ! options . fixedX ;
/ * *
* Set to true if you want to fix the body movement along the Y axis . The body will still be able to move along X .
* @ property { Boolean } fixedY
* /
this . fixedY = ! ! options . fixedY ;
/ * *
* @ private
* @ property { array } massMultiplier
* /
this . massMultiplier = vec2 . create ( ) ;
/ * *
* The position of the body
* @ property position
* @ type { Array }
* /
this . position = vec2 . fromValues ( 0 , 0 ) ;
if ( options . position ) {
vec2 . copy ( this . position , options . position ) ;
}
/ * *
* The interpolated position of the body . Use this for rendering .
* @ property interpolatedPosition
* @ type { Array }
* /
this . interpolatedPosition = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* The interpolated angle of the body . Use this for rendering .
* @ property interpolatedAngle
* @ type { Number }
* /
this . interpolatedAngle = 0 ;
/ * *
* The previous position of the body .
* @ property previousPosition
* @ type { Array }
* /
this . previousPosition = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* The previous angle of the body .
* @ property previousAngle
* @ type { Number }
* /
this . previousAngle = 0 ;
/ * *
* The current velocity of the body .
* @ property velocity
* @ type { Array }
* /
this . velocity = vec2 . fromValues ( 0 , 0 ) ;
if ( options . velocity ) {
vec2 . copy ( this . velocity , options . velocity ) ;
}
/ * *
* Constraint velocity that was added to the body during the last step .
* @ property vlambda
* @ type { Array }
* /
this . vlambda = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Angular constraint velocity that was added to the body during last step .
* @ property wlambda
* @ type { Array }
* /
this . wlambda = 0 ;
/ * *
* The angle of the body , in radians .
* @ property angle
* @ type { number }
* @ example
* // The angle property is not normalized to the interval 0 to 2*pi, it can be any value.
* // If you need a value between 0 and 2*pi, use the following function to normalize it.
* function normalizeAngle ( angle ) {
* angle = angle % ( 2 * Math . PI ) ;
* if ( angle < 0 ) {
* angle += ( 2 * Math . PI ) ;
* }
* return angle ;
* }
* /
this . angle = options . angle || 0 ;
/ * *
* The angular velocity of the body , in radians per second .
* @ property angularVelocity
* @ type { number }
* /
this . angularVelocity = options . angularVelocity || 0 ;
/ * *
* The force acting on the body . Since the body force ( and { { # crossLink "Body/angularForce:property" } } { { / c r o s s L i n k } } ) w i l l b e z e r o e d a f t e r e a c h s t e p , s o y o u n e e d t o s e t t h e f o r c e b e f o r e e a c h s t e p .
* @ property force
* @ type { Array }
*
* @ example
* // This produces a forcefield of 1 Newton in the positive x direction.
* for ( var i = 0 ; i < numSteps ; i ++ ) {
* body . force [ 0 ] = 1 ;
* world . step ( 1 / 60 ) ;
* }
*
* @ example
* // This will apply a rotational force on the body
* for ( var i = 0 ; i < numSteps ; i ++ ) {
* body . angularForce = - 3 ;
* world . step ( 1 / 60 ) ;
* }
* /
this . force = vec2 . create ( ) ;
if ( options . force ) {
vec2 . copy ( this . force , options . force ) ;
}
/ * *
* The angular force acting on the body . See { { # crossLink "Body/force:property" } } { { / c r o s s L i n k } } .
* @ property angularForce
* @ type { number }
* /
this . angularForce = options . angularForce || 0 ;
/ * *
* The linear damping acting on the body in the velocity direction . Should be a value between 0 and 1.
* @ property damping
* @ type { Number }
* @ default 0.1
* /
this . damping = typeof ( options . damping ) === "number" ? options . damping : 0.1 ;
/ * *
* The angular force acting on the body . Should be a value between 0 and 1.
* @ property angularDamping
* @ type { Number }
* @ default 0.1
* /
this . angularDamping = typeof ( options . angularDamping ) === "number" ? options . angularDamping : 0.1 ;
/ * *
* The type of motion this body has . Should be one of : { { # crossLink "Body/STATIC:property" } } Body . STATIC { { / c r o s s L i n k } } , { { # c r o s s L i n k " B o d y / D Y N A M I C : p r o p e r t y " } } B o d y . D Y N A M I C { { / c r o s s L i n k } } a n d { { # c r o s s L i n k " B o d y / K I N E M A T I C : p r o p e r t y " } } B o d y . K I N E M A T I C { { / c r o s s L i n k } } .
*
* * Static bodies do not move , and they do not respond to forces or collision .
* * Dynamic bodies body can move and respond to collisions and forces .
* * Kinematic bodies only moves according to its . velocity , and does not respond to collisions or force .
*
* @ property type
* @ type { number }
*
* @ example
* // Bodies are static by default. Static bodies will never move.
* var body = new Body ( ) ;
* console . log ( body . type == Body . STATIC ) ; // true
*
* @ example
* // By setting the mass of a body to a nonzero number, the body
* // will become dynamic and will move and interact with other bodies.
* var dynamicBody = new Body ( {
* mass : 1
* } ) ;
* console . log ( dynamicBody . type == Body . DYNAMIC ) ; // true
*
* @ example
* // Kinematic bodies will only move if you change their velocity.
* var kinematicBody = new Body ( {
* type : Body . KINEMATIC // Type can be set via the options object.
* } ) ;
* /
this . type = Body . STATIC ;
if ( typeof ( options . type ) !== 'undefined' ) {
this . type = options . type ;
} else if ( ! options . mass ) {
this . type = Body . STATIC ;
} else {
this . type = Body . DYNAMIC ;
}
/ * *
* Bounding circle radius .
* @ property boundingRadius
* @ type { Number }
* /
this . boundingRadius = 0 ;
/ * *
* Bounding box of this body .
* @ property aabb
* @ type { AABB }
* /
this . aabb = new AABB ( ) ;
/ * *
* Indicates if the AABB needs update . Update it with { { # crossLink "Body/updateAABB:method" } } . updateAABB ( ) { { / c r o s s L i n k } } .
* @ property aabbNeedsUpdate
* @ type { Boolean }
* @ see updateAABB
*
* @ example
* // Force update the AABB
* body . aabbNeedsUpdate = true ;
* body . updateAABB ( ) ;
* console . log ( body . aabbNeedsUpdate ) ; // false
* /
this . aabbNeedsUpdate = true ;
/ * *
* If true , the body will automatically fall to sleep . Note that you need to enable sleeping in the { { # crossLink "World" } } { { / c r o s s L i n k } } b e f o r e a n y t h i n g w i l l h a p p e n .
* @ property allowSleep
* @ type { Boolean }
* @ default true
* /
this . allowSleep = options . allowSleep !== undefined ? options . allowSleep : true ;
this . wantsToSleep = false ;
/ * *
* One of { { # crossLink "Body/AWAKE:property" } } Body . AWAKE { { / c r o s s L i n k } } , { { # c r o s s L i n k " B o d y / S L E E P Y : p r o p e r t y " } } B o d y . S L E E P Y { { / c r o s s L i n k } } a n d { { # c r o s s L i n k " B o d y / S L E E P I N G : p r o p e r t y " } } B o d y . S L E E P I N G { { / c r o s s L i n k } } .
*
* The body is initially Body . AWAKE . If its velocity norm is below . sleepSpeedLimit , the sleepState will become Body . SLEEPY . If the body continues to be Body . SLEEPY for . sleepTimeLimit seconds , it will fall asleep ( Body . SLEEPY ) .
*
* @ property sleepState
* @ type { Number }
* @ default Body . AWAKE
* /
this . sleepState = Body . AWAKE ;
/ * *
* If the speed ( the norm of the velocity ) is smaller than this value , the body is considered sleepy .
* @ property sleepSpeedLimit
* @ type { Number }
* @ default 0.2
* /
this . sleepSpeedLimit = options . sleepSpeedLimit !== undefined ? options . sleepSpeedLimit : 0.2 ;
/ * *
* If the body has been sleepy for this sleepTimeLimit seconds , it is considered sleeping .
* @ property sleepTimeLimit
* @ type { Number }
* @ default 1
* /
this . sleepTimeLimit = options . sleepTimeLimit !== undefined ? options . sleepTimeLimit : 1 ;
/ * *
* Gravity scaling factor . If you want the body to ignore gravity , set this to zero . If you want to reverse gravity , set it to - 1.
* @ property { Number } gravityScale
* @ default 1
* /
this . gravityScale = options . gravityScale !== undefined ? options . gravityScale : 1 ;
/ * *
* Whether to produce contact forces when in contact with other bodies . Note that contacts will be generated , but they will be disabled . That means that this body will move through other bodies , but it will still trigger contact events , etc .
* @ property { Boolean } collisionResponse
* /
this . collisionResponse = options . collisionResponse !== undefined ? options . collisionResponse : true ;
/ * *
* How long the body has been sleeping .
* @ property { Number } idleTime
* /
this . idleTime = 0 ;
/ * *
* The last time when the body went to SLEEPY state .
* @ property { Number } timeLastSleepy
* @ private
* /
this . timeLastSleepy = 0 ;
/ * *
* If the body speed exceeds this threshold , CCD ( continuous collision detection ) will be enabled . Set it to a negative number to disable CCD completely for this body .
* @ property { number } ccdSpeedThreshold
* @ default - 1
* /
this . ccdSpeedThreshold = options . ccdSpeedThreshold !== undefined ? options . ccdSpeedThreshold : - 1 ;
/ * *
* The number of iterations that should be used when searching for the time of impact during CCD . A larger number will assure that there ' s a small penetration on CCD collision , but a small number will give more performance .
* @ property { number } ccdIterations
* @ default 10
* /
this . ccdIterations = options . ccdIterations !== undefined ? options . ccdIterations : 10 ;
this . concavePath = null ;
this . _wakeUpAfterNarrowphase = false ;
this . updateMassProperties ( ) ;
}
Body . prototype = new EventEmitter ( ) ;
Body . prototype . constructor = Body ;
Body . _idCounter = 0 ;
/ * *
* @ private
* @ method updateSolveMassProperties
* /
Body . prototype . updateSolveMassProperties = function ( ) {
if ( this . sleepState === Body . SLEEPING || this . type === Body . KINEMATIC ) {
this . invMassSolve = 0 ;
this . invInertiaSolve = 0 ;
} else {
this . invMassSolve = this . invMass ;
this . invInertiaSolve = this . invInertia ;
}
} ;
/ * *
* Set the total density of the body
* @ method setDensity
* @ param { number } density
* /
Body . prototype . setDensity = function ( density ) {
var totalArea = this . getArea ( ) ;
this . mass = totalArea * density ;
this . updateMassProperties ( ) ;
} ;
/ * *
* Get the total area of all shapes in the body
* @ method getArea
* @ return { Number }
* /
Body . prototype . getArea = function ( ) {
var totalArea = 0 ;
for ( var i = 0 ; i < this . shapes . length ; i ++ ) {
totalArea += this . shapes [ i ] . area ;
}
return totalArea ;
} ;
/ * *
* Get the AABB from the body . The AABB is updated if necessary .
* @ method getAABB
* @ return { AABB } The AABB instance ( this . aabb )
* /
Body . prototype . getAABB = function ( ) {
if ( this . aabbNeedsUpdate ) {
this . updateAABB ( ) ;
}
return this . aabb ;
} ;
var shapeAABB = new AABB ( ) ,
tmp = vec2 . create ( ) ;
/ * *
* Updates the AABB of the Body , and set . aabbNeedsUpdate = false .
* @ method updateAABB
* /
Body . prototype . updateAABB = function ( ) {
var shapes = this . shapes ,
N = shapes . length ,
offset = tmp ,
bodyAngle = this . angle ;
for ( var i = 0 ; i !== N ; i ++ ) {
var shape = shapes [ i ] ,
angle = shape . angle + bodyAngle ;
// Get shape world offset
vec2 . rotate ( offset , shape . position , bodyAngle ) ;
vec2 . add ( offset , offset , this . position ) ;
// Get shape AABB
shape . computeAABB ( shapeAABB , offset , angle ) ;
if ( i === 0 ) {
this . aabb . copy ( shapeAABB ) ;
} else {
this . aabb . extend ( shapeAABB ) ;
}
}
this . aabbNeedsUpdate = false ;
} ;
/ * *
* Update the bounding radius of the body ( this . boundingRadius ) . Should be done if any of the shape dimensions or positions are changed .
* @ method updateBoundingRadius
* /
Body . prototype . updateBoundingRadius = function ( ) {
var shapes = this . shapes ,
N = shapes . length ,
radius = 0 ;
for ( var i = 0 ; i !== N ; i ++ ) {
var shape = shapes [ i ] ,
offset = vec2 . length ( shape . position ) ,
r = shape . boundingRadius ;
if ( offset + r > radius ) {
radius = offset + r ;
}
}
this . boundingRadius = radius ;
} ;
/ * *
* Add a shape to the body . You can pass a local transform when adding a shape ,
* so that the shape gets an offset and angle relative to the body center of mass .
* Will automatically update the mass properties and bounding radius .
*
* @ method addShape
* @ param { Shape } shape
* @ param { Array } [ offset ] Local body offset of the shape .
* @ param { Number } [ angle ] Local body angle .
*
* @ example
* var body = new Body ( ) ,
* shape = new Circle ( { radius : 1 } ) ;
*
* // Add the shape to the body, positioned in the center
* body . addShape ( shape ) ;
*
* // Add another shape to the body, positioned 1 unit length from the body center of mass along the local x-axis.
* body . addShape ( shape , [ 1 , 0 ] ) ;
*
* // Add another shape to the body, positioned 1 unit length from the body center of mass along the local y-axis, and rotated 90 degrees CCW.
* body . addShape ( shape , [ 0 , 1 ] , Math . PI / 2 ) ;
* /
Body . prototype . addShape = function ( shape , offset , angle ) {
if ( shape . body ) {
throw new Error ( 'A shape can only be added to one body.' ) ;
}
shape . body = this ;
// Copy the offset vector
if ( offset ) {
vec2 . copy ( shape . position , offset ) ;
} else {
vec2 . set ( shape . position , 0 , 0 ) ;
}
shape . angle = angle || 0 ;
this . shapes . push ( shape ) ;
this . updateMassProperties ( ) ;
this . updateBoundingRadius ( ) ;
this . aabbNeedsUpdate = true ;
} ;
/ * *
* Remove a shape
* @ method removeShape
* @ param { Shape } shape
* @ return { Boolean } True if the shape was found and removed , else false .
* /
Body . prototype . removeShape = function ( shape ) {
var idx = this . shapes . indexOf ( shape ) ;
if ( idx !== - 1 ) {
this . shapes . splice ( idx , 1 ) ;
this . aabbNeedsUpdate = true ;
shape . body = null ;
return true ;
} else {
return false ;
}
} ;
/ * *
* Updates . inertia , . invMass , . invInertia for this Body . Should be called when
* changing the structure or mass of the Body .
*
* @ method updateMassProperties
*
* @ example
* body . mass += 1 ;
* body . updateMassProperties ( ) ;
* /
Body . prototype . updateMassProperties = function ( ) {
if ( this . type === Body . STATIC || this . type === Body . KINEMATIC ) {
this . mass = Number . MAX _VALUE ;
this . invMass = 0 ;
this . inertia = Number . MAX _VALUE ;
this . invInertia = 0 ;
} else {
var shapes = this . shapes ,
N = shapes . length ,
m = this . mass / N ,
I = 0 ;
if ( ! this . fixedRotation ) {
for ( var i = 0 ; i < N ; i ++ ) {
var shape = shapes [ i ] ,
r2 = vec2 . squaredLength ( shape . position ) ,
Icm = shape . computeMomentOfInertia ( m ) ;
I += Icm + m * r2 ;
}
this . inertia = I ;
this . invInertia = I > 0 ? 1 / I : 0 ;
} else {
this . inertia = Number . MAX _VALUE ;
this . invInertia = 0 ;
}
// Inverse mass properties are easy
this . invMass = 1 / this . mass ;
vec2 . set (
this . massMultiplier ,
this . fixedX ? 0 : 1 ,
this . fixedY ? 0 : 1
) ;
}
} ;
var Body _applyForce _r = vec2 . create ( ) ;
/ * *
* Apply force to a point relative to the center of mass of the body . This could for example be a point on the RigidBody surface . Applying force this way will add to Body . force and Body . angularForce . If relativePoint is zero , the force will be applied directly on the center of mass , and the torque produced will be zero .
* @ method applyForce
* @ param { Array } force The force to add .
* @ param { Array } [ relativePoint ] A world point to apply the force on .
* /
Body . prototype . applyForce = function ( force , relativePoint ) {
// Add linear force
vec2 . add ( this . force , this . force , force ) ;
if ( relativePoint ) {
// Compute produced rotational force
var rotForce = vec2 . crossLength ( relativePoint , force ) ;
// Add rotational force
this . angularForce += rotForce ;
}
} ;
/ * *
* Apply force to a body - local point .
* @ method applyForceLocal
* @ param { Array } localForce The force vector to add , oriented in local body space .
2016-09-21 22:22:08 +00:00
* @ param { Array } [ localPoint ] A point relative to the body in world space . If not given , it is set to zero and all of the impulse will be excerted on the center of mass .
2016-02-12 16:00:36 +00:00
* /
var Body _applyForce _forceWorld = vec2 . create ( ) ;
var Body _applyForce _pointWorld = vec2 . create ( ) ;
var Body _applyForce _pointLocal = vec2 . create ( ) ;
Body . prototype . applyForceLocal = function ( localForce , localPoint ) {
localPoint = localPoint || Body _applyForce _pointLocal ;
var worldForce = Body _applyForce _forceWorld ;
var worldPoint = Body _applyForce _pointWorld ;
this . vectorToWorldFrame ( worldForce , localForce ) ;
this . vectorToWorldFrame ( worldPoint , localPoint ) ;
this . applyForce ( worldForce , worldPoint ) ;
} ;
/ * *
* Apply impulse to a point relative to the body . This could for example be a point on the Body surface . An impulse is a force added to a body during a short period of time ( impulse = force * time ) . Impulses will be added to Body . velocity and Body . angularVelocity .
* @ method applyImpulse
* @ param { Array } impulse The impulse vector to add , oriented in world space .
* @ param { Array } [ relativePoint ] A point relative to the body in world space . If not given , it is set to zero and all of the impulse will be excerted on the center of mass .
* /
var Body _applyImpulse _velo = vec2 . create ( ) ;
Body . prototype . applyImpulse = function ( impulseVector , relativePoint ) {
if ( this . type !== Body . DYNAMIC ) {
return ;
}
// Compute produced central impulse velocity
var velo = Body _applyImpulse _velo ;
vec2 . scale ( velo , impulseVector , this . invMass ) ;
vec2 . multiply ( velo , this . massMultiplier , velo ) ;
// Add linear impulse
vec2 . add ( this . velocity , velo , this . velocity ) ;
if ( relativePoint ) {
// Compute produced rotational impulse velocity
var rotVelo = vec2 . crossLength ( relativePoint , impulseVector ) ;
rotVelo *= this . invInertia ;
// Add rotational Impulse
this . angularVelocity += rotVelo ;
}
} ;
/ * *
* Apply impulse to a point relative to the body . This could for example be a point on the Body surface . An impulse is a force added to a body during a short period of time ( impulse = force * time ) . Impulses will be added to Body . velocity and Body . angularVelocity .
* @ method applyImpulseLocal
* @ param { Array } impulse The impulse vector to add , oriented in world space .
* @ param { Array } [ relativePoint ] A point relative to the body in world space . If not given , it is set to zero and all of the impulse will be excerted on the center of mass .
* /
var Body _applyImpulse _impulseWorld = vec2 . create ( ) ;
var Body _applyImpulse _pointWorld = vec2 . create ( ) ;
var Body _applyImpulse _pointLocal = vec2 . create ( ) ;
Body . prototype . applyImpulseLocal = function ( localImpulse , localPoint ) {
localPoint = localPoint || Body _applyImpulse _pointLocal ;
var worldImpulse = Body _applyImpulse _impulseWorld ;
var worldPoint = Body _applyImpulse _pointWorld ;
this . vectorToWorldFrame ( worldImpulse , localImpulse ) ;
this . vectorToWorldFrame ( worldPoint , localPoint ) ;
this . applyImpulse ( worldImpulse , worldPoint ) ;
} ;
/ * *
* Transform a world point to local body frame .
* @ method toLocalFrame
* @ param { Array } out The vector to store the result in
* @ param { Array } worldPoint The input world point
* /
Body . prototype . toLocalFrame = function ( out , worldPoint ) {
vec2 . toLocalFrame ( out , worldPoint , this . position , this . angle ) ;
} ;
/ * *
* Transform a local point to world frame .
* @ method toWorldFrame
* @ param { Array } out The vector to store the result in
* @ param { Array } localPoint The input local point
* /
Body . prototype . toWorldFrame = function ( out , localPoint ) {
vec2 . toGlobalFrame ( out , localPoint , this . position , this . angle ) ;
} ;
/ * *
* Transform a world point to local body frame .
* @ method vectorToLocalFrame
* @ param { Array } out The vector to store the result in
* @ param { Array } worldVector The input world vector
* /
Body . prototype . vectorToLocalFrame = function ( out , worldVector ) {
vec2 . vectorToLocalFrame ( out , worldVector , this . angle ) ;
} ;
/ * *
* Transform a local point to world frame .
* @ method vectorToWorldFrame
* @ param { Array } out The vector to store the result in
* @ param { Array } localVector The input local vector
* /
Body . prototype . vectorToWorldFrame = function ( out , localVector ) {
vec2 . vectorToGlobalFrame ( out , localVector , this . angle ) ;
} ;
/ * *
* Reads a polygon shape path , and assembles convex shapes from that and puts them at proper offset points .
* @ method fromPolygon
* @ param { Array } path An array of 2 d vectors , e . g . [ [ 0 , 0 ] , [ 0 , 1 ] , ... ] that resembles a concave or convex polygon . The shape must be simple and without holes .
* @ param { Object } [ options ]
* @ param { Boolean } [ options . optimalDecomp = false ] Set to true if you need optimal decomposition . Warning : very slow for polygons with more than 10 vertices .
* @ param { Boolean } [ options . skipSimpleCheck = false ] Set to true if you already know that the path is not intersecting itself .
* @ param { Boolean | Number } [ options . removeCollinearPoints = false ] Set to a number ( angle threshold value ) to remove collinear points , or false to keep all points .
* @ return { Boolean } True on success , else false .
* /
Body . prototype . fromPolygon = function ( path , options ) {
options = options || { } ;
// Remove all shapes
for ( var i = this . shapes . length ; i >= 0 ; -- i ) {
this . removeShape ( this . shapes [ i ] ) ;
}
var p = new decomp . Polygon ( ) ;
p . vertices = path ;
// Make it counter-clockwise
p . makeCCW ( ) ;
if ( typeof ( options . removeCollinearPoints ) === "number" ) {
p . removeCollinearPoints ( options . removeCollinearPoints ) ;
}
// Check if any line segment intersects the path itself
if ( typeof ( options . skipSimpleCheck ) === "undefined" ) {
if ( ! p . isSimple ( ) ) {
return false ;
}
}
// Save this path for later
this . concavePath = p . vertices . slice ( 0 ) ;
for ( var i = 0 ; i < this . concavePath . length ; i ++ ) {
var v = [ 0 , 0 ] ;
vec2 . copy ( v , this . concavePath [ i ] ) ;
this . concavePath [ i ] = v ;
}
// Slow or fast decomp?
var convexes ;
if ( options . optimalDecomp ) {
convexes = p . decomp ( ) ;
} else {
convexes = p . quickDecomp ( ) ;
}
var cm = vec2 . create ( ) ;
// Add convexes
for ( var i = 0 ; i !== convexes . length ; i ++ ) {
// Create convex
var c = new Convex ( { vertices : convexes [ i ] . vertices } ) ;
// Move all vertices so its center of mass is in the local center of the convex
for ( var j = 0 ; j !== c . vertices . length ; j ++ ) {
var v = c . vertices [ j ] ;
vec2 . sub ( v , v , c . centerOfMass ) ;
}
vec2 . scale ( cm , c . centerOfMass , 1 ) ;
c . updateTriangles ( ) ;
c . updateCenterOfMass ( ) ;
c . updateBoundingRadius ( ) ;
// Add the shape
this . addShape ( c , cm ) ;
}
this . adjustCenterOfMass ( ) ;
this . aabbNeedsUpdate = true ;
return true ;
} ;
var adjustCenterOfMass _tmp1 = vec2 . fromValues ( 0 , 0 ) ,
adjustCenterOfMass _tmp2 = vec2 . fromValues ( 0 , 0 ) ,
adjustCenterOfMass _tmp3 = vec2 . fromValues ( 0 , 0 ) ,
adjustCenterOfMass _tmp4 = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Moves the shape offsets so their center of mass becomes the body center of mass .
* @ method adjustCenterOfMass
* /
Body . prototype . adjustCenterOfMass = function ( ) {
var offset _times _area = adjustCenterOfMass _tmp2 ,
sum = adjustCenterOfMass _tmp3 ,
cm = adjustCenterOfMass _tmp4 ,
totalArea = 0 ;
vec2 . set ( sum , 0 , 0 ) ;
for ( var i = 0 ; i !== this . shapes . length ; i ++ ) {
var s = this . shapes [ i ] ;
vec2 . scale ( offset _times _area , s . position , s . area ) ;
vec2 . add ( sum , sum , offset _times _area ) ;
totalArea += s . area ;
}
vec2 . scale ( cm , sum , 1 / totalArea ) ;
// Now move all shapes
for ( var i = 0 ; i !== this . shapes . length ; i ++ ) {
var s = this . shapes [ i ] ;
vec2 . sub ( s . position , s . position , cm ) ;
}
// Move the body position too
vec2 . add ( this . position , this . position , cm ) ;
// And concave path
for ( var i = 0 ; this . concavePath && i < this . concavePath . length ; i ++ ) {
vec2 . sub ( this . concavePath [ i ] , this . concavePath [ i ] , cm ) ;
}
this . updateMassProperties ( ) ;
this . updateBoundingRadius ( ) ;
} ;
/ * *
* Sets the force on the body to zero .
* @ method setZeroForce
* /
Body . prototype . setZeroForce = function ( ) {
vec2 . set ( this . force , 0.0 , 0.0 ) ;
this . angularForce = 0.0 ;
} ;
Body . prototype . resetConstraintVelocity = function ( ) {
var b = this ,
vlambda = b . vlambda ;
vec2 . set ( vlambda , 0 , 0 ) ;
b . wlambda = 0 ;
} ;
Body . prototype . addConstraintVelocity = function ( ) {
var b = this ,
v = b . velocity ;
vec2 . add ( v , v , b . vlambda ) ;
b . angularVelocity += b . wlambda ;
} ;
/ * *
* Apply damping , see < a href = "http://code.google.com/p/bullet/issues/detail?id=74" > this < / a > f o r d e t a i l s .
* @ method applyDamping
* @ param { number } dt Current time step
* /
Body . prototype . applyDamping = function ( dt ) {
if ( this . type === Body . DYNAMIC ) { // Only for dynamic bodies
var v = this . velocity ;
vec2 . scale ( v , v , Math . pow ( 1.0 - this . damping , dt ) ) ;
this . angularVelocity *= Math . pow ( 1.0 - this . angularDamping , dt ) ;
}
} ;
/ * *
* Wake the body up . Normally you should not need this , as the body is automatically awoken at events such as collisions .
* Sets the sleepState to { { # crossLink "Body/AWAKE:property" } } Body . AWAKE { { / c r o s s L i n k } } a n d e m i t s t h e w a k e U p e v e n t i f t h e b o d y w a s n ' t a w a k e b e f o r e .
* @ method wakeUp
* /
Body . prototype . wakeUp = function ( ) {
var s = this . sleepState ;
this . sleepState = Body . AWAKE ;
this . idleTime = 0 ;
if ( s !== Body . AWAKE ) {
this . emit ( Body . wakeUpEvent ) ;
}
} ;
/ * *
* Force body sleep
* @ method sleep
* /
Body . prototype . sleep = function ( ) {
this . sleepState = Body . SLEEPING ;
this . angularVelocity = 0 ;
this . angularForce = 0 ;
vec2 . set ( this . velocity , 0 , 0 ) ;
vec2 . set ( this . force , 0 , 0 ) ;
this . emit ( Body . sleepEvent ) ;
} ;
/ * *
* Called every timestep to update internal sleep timer and change sleep state if needed .
* @ method sleepTick
* @ param { number } time The world time in seconds
* @ param { boolean } dontSleep
* @ param { number } dt
* /
Body . prototype . sleepTick = function ( time , dontSleep , dt ) {
if ( ! this . allowSleep || this . type === Body . SLEEPING ) {
return ;
}
this . wantsToSleep = false ;
var sleepState = this . sleepState ,
speedSquared = vec2 . squaredLength ( this . velocity ) + Math . pow ( this . angularVelocity , 2 ) ,
speedLimitSquared = Math . pow ( this . sleepSpeedLimit , 2 ) ;
// Add to idle time
if ( speedSquared >= speedLimitSquared ) {
this . idleTime = 0 ;
this . sleepState = Body . AWAKE ;
} else {
this . idleTime += dt ;
this . sleepState = Body . SLEEPY ;
}
if ( this . idleTime > this . sleepTimeLimit ) {
if ( ! dontSleep ) {
this . sleep ( ) ;
} else {
this . wantsToSleep = true ;
}
}
} ;
/ * *
* Check if the body is overlapping another body . Note that this method only works if the body was added to a World and if at least one step was taken .
* @ method overlaps
* @ param { Body } body
* @ return { boolean }
* /
Body . prototype . overlaps = function ( body ) {
return this . world . overlapKeeper . bodiesAreOverlapping ( this , body ) ;
} ;
var integrate _fhMinv = vec2 . create ( ) ;
var integrate _velodt = vec2 . create ( ) ;
/ * *
* Move the body forward in time given its current velocity .
* @ method integrate
* @ param { Number } dt
* /
Body . prototype . integrate = function ( dt ) {
var minv = this . invMass ,
f = this . force ,
pos = this . position ,
velo = this . velocity ;
// Save old position
vec2 . copy ( this . previousPosition , this . position ) ;
this . previousAngle = this . angle ;
// Velocity update
if ( ! this . fixedRotation ) {
this . angularVelocity += this . angularForce * this . invInertia * dt ;
}
vec2 . scale ( integrate _fhMinv , f , dt * minv ) ;
vec2 . multiply ( integrate _fhMinv , this . massMultiplier , integrate _fhMinv ) ;
vec2 . add ( velo , integrate _fhMinv , velo ) ;
// CCD
if ( ! this . integrateToTimeOfImpact ( dt ) ) {
// Regular position update
vec2 . scale ( integrate _velodt , velo , dt ) ;
vec2 . add ( pos , pos , integrate _velodt ) ;
if ( ! this . fixedRotation ) {
this . angle += this . angularVelocity * dt ;
}
}
this . aabbNeedsUpdate = true ;
} ;
var result = new RaycastResult ( ) ;
var ray = new Ray ( {
mode : Ray . ALL
} ) ;
var direction = vec2 . create ( ) ;
var end = vec2 . create ( ) ;
var startToEnd = vec2 . create ( ) ;
var rememberPosition = vec2 . create ( ) ;
Body . prototype . integrateToTimeOfImpact = function ( dt ) {
if ( this . ccdSpeedThreshold < 0 || vec2 . squaredLength ( this . velocity ) < Math . pow ( this . ccdSpeedThreshold , 2 ) ) {
return false ;
}
vec2 . normalize ( direction , this . velocity ) ;
vec2 . scale ( end , this . velocity , dt ) ;
vec2 . add ( end , end , this . position ) ;
vec2 . sub ( startToEnd , end , this . position ) ;
var startToEndAngle = this . angularVelocity * dt ;
var len = vec2 . length ( startToEnd ) ;
var timeOfImpact = 1 ;
var hit ;
var that = this ;
result . reset ( ) ;
ray . callback = function ( result ) {
if ( result . body === that ) {
return ;
}
hit = result . body ;
result . getHitPoint ( end , ray ) ;
vec2 . sub ( startToEnd , end , that . position ) ;
timeOfImpact = vec2 . length ( startToEnd ) / len ;
result . stop ( ) ;
} ;
vec2 . copy ( ray . from , this . position ) ;
vec2 . copy ( ray . to , end ) ;
ray . update ( ) ;
this . world . raycast ( result , ray ) ;
if ( ! hit ) {
return false ;
}
var rememberAngle = this . angle ;
vec2 . copy ( rememberPosition , this . position ) ;
// Got a start and end point. Approximate time of impact using binary search
var iter = 0 ;
var tmin = 0 ;
var tmid = 0 ;
var tmax = timeOfImpact ;
while ( tmax >= tmin && iter < this . ccdIterations ) {
iter ++ ;
// calculate the midpoint
tmid = ( tmax - tmin ) / 2 ;
// Move the body to that point
vec2 . scale ( integrate _velodt , startToEnd , timeOfImpact ) ;
vec2 . add ( this . position , rememberPosition , integrate _velodt ) ;
this . angle = rememberAngle + startToEndAngle * timeOfImpact ;
this . updateAABB ( ) ;
// check overlap
var overlaps = this . aabb . overlaps ( hit . aabb ) && this . world . narrowphase . bodiesOverlap ( this , hit ) ;
if ( overlaps ) {
// change min to search upper interval
tmin = tmid ;
} else {
// change max to search lower interval
tmax = tmid ;
}
}
timeOfImpact = tmid ;
vec2 . copy ( this . position , rememberPosition ) ;
this . angle = rememberAngle ;
// move to TOI
vec2 . scale ( integrate _velodt , startToEnd , timeOfImpact ) ;
vec2 . add ( this . position , this . position , integrate _velodt ) ;
if ( ! this . fixedRotation ) {
this . angle += startToEndAngle * timeOfImpact ;
}
return true ;
} ;
/ * *
* Get velocity of a point in the body .
* @ method getVelocityAtPoint
* @ param { Array } result A vector to store the result in
* @ param { Array } relativePoint A world oriented vector , indicating the position of the point to get the velocity from
* @ return { Array } The result vector
* /
Body . prototype . getVelocityAtPoint = function ( result , relativePoint ) {
vec2 . crossVZ ( result , relativePoint , this . angularVelocity ) ;
vec2 . subtract ( result , this . velocity , result ) ;
return result ;
} ;
/ * *
* @ event sleepy
* /
Body . sleepyEvent = {
type : "sleepy"
} ;
/ * *
* @ event sleep
* /
Body . sleepEvent = {
type : "sleep"
} ;
/ * *
* @ event wakeup
* /
Body . wakeUpEvent = {
type : "wakeup"
} ;
/ * *
* Dynamic body .
* @ property DYNAMIC
* @ type { Number }
* @ static
* /
Body . DYNAMIC = 1 ;
/ * *
* Static body .
* @ property STATIC
* @ type { Number }
* @ static
* /
Body . STATIC = 2 ;
/ * *
* Kinematic body .
* @ property KINEMATIC
* @ type { Number }
* @ static
* /
Body . KINEMATIC = 4 ;
/ * *
* @ property AWAKE
* @ type { Number }
* @ static
* /
Body . AWAKE = 0 ;
/ * *
* @ property SLEEPY
* @ type { Number }
* @ static
* /
Body . SLEEPY = 1 ;
/ * *
* @ property SLEEPING
* @ type { Number }
* @ static
* /
Body . SLEEPING = 2 ;
} , { "../collision/AABB" : 7 , "../collision/Ray" : 11 , "../collision/RaycastResult" : 12 , "../events/EventEmitter" : 26 , "../math/vec2" : 30 , "../shapes/Convex" : 40 , "poly-decomp" : 5 } ] , 32 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' ) ;
var Spring = _dereq _ ( './Spring' ) ;
var Utils = _dereq _ ( '../utils/Utils' ) ;
module . exports = LinearSpring ;
/ * *
* A spring , connecting two bodies .
*
* The Spring explicitly adds force and angularForce to the bodies .
*
* @ class LinearSpring
* @ extends Spring
* @ constructor
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Object } [ options ]
* @ param { number } [ options . restLength ] A number > 0. Default is the current distance between the world anchor points .
* @ param { number } [ options . stiffness = 100 ] Spring constant ( see Hookes Law ) . A number >= 0.
* @ param { number } [ options . damping = 1 ] A number >= 0. Default : 1
* @ param { Array } [ options . worldAnchorA ] Where to hook the spring to body A , in world coordinates . Overrides the option "localAnchorA" if given .
* @ param { Array } [ options . worldAnchorB ]
* @ param { Array } [ options . localAnchorA ] Where to hook the spring to body A , in local body coordinates . Defaults to the body center .
* @ param { Array } [ options . localAnchorB ]
* /
function LinearSpring ( bodyA , bodyB , options ) {
options = options || { } ;
Spring . call ( this , bodyA , bodyB , options ) ;
/ * *
* Anchor for bodyA in local bodyA coordinates .
* @ property localAnchorA
* @ type { Array }
* /
this . localAnchorA = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Anchor for bodyB in local bodyB coordinates .
* @ property localAnchorB
* @ type { Array }
* /
this . localAnchorB = vec2 . fromValues ( 0 , 0 ) ;
if ( options . localAnchorA ) { vec2 . copy ( this . localAnchorA , options . localAnchorA ) ; }
if ( options . localAnchorB ) { vec2 . copy ( this . localAnchorB , options . localAnchorB ) ; }
if ( options . worldAnchorA ) { this . setWorldAnchorA ( options . worldAnchorA ) ; }
if ( options . worldAnchorB ) { this . setWorldAnchorB ( options . worldAnchorB ) ; }
var worldAnchorA = vec2 . create ( ) ;
var worldAnchorB = vec2 . create ( ) ;
this . getWorldAnchorA ( worldAnchorA ) ;
this . getWorldAnchorB ( worldAnchorB ) ;
var worldDistance = vec2 . distance ( worldAnchorA , worldAnchorB ) ;
/ * *
* Rest length of the spring .
* @ property restLength
* @ type { number }
* /
this . restLength = typeof ( options . restLength ) === "number" ? options . restLength : worldDistance ;
}
LinearSpring . prototype = new Spring ( ) ;
LinearSpring . prototype . constructor = LinearSpring ;
/ * *
* Set the anchor point on body A , using world coordinates .
* @ method setWorldAnchorA
* @ param { Array } worldAnchorA
* /
LinearSpring . prototype . setWorldAnchorA = function ( worldAnchorA ) {
this . bodyA . toLocalFrame ( this . localAnchorA , worldAnchorA ) ;
} ;
/ * *
* Set the anchor point on body B , using world coordinates .
* @ method setWorldAnchorB
* @ param { Array } worldAnchorB
* /
LinearSpring . prototype . setWorldAnchorB = function ( worldAnchorB ) {
this . bodyB . toLocalFrame ( this . localAnchorB , worldAnchorB ) ;
} ;
/ * *
* Get the anchor point on body A , in world coordinates .
* @ method getWorldAnchorA
* @ param { Array } result The vector to store the result in .
* /
LinearSpring . prototype . getWorldAnchorA = function ( result ) {
this . bodyA . toWorldFrame ( result , this . localAnchorA ) ;
} ;
/ * *
* Get the anchor point on body B , in world coordinates .
* @ method getWorldAnchorB
* @ param { Array } result The vector to store the result in .
* /
LinearSpring . prototype . getWorldAnchorB = function ( result ) {
this . bodyB . toWorldFrame ( result , this . localAnchorB ) ;
} ;
var applyForce _r = vec2 . create ( ) ,
applyForce _r _unit = vec2 . create ( ) ,
applyForce _u = vec2 . create ( ) ,
applyForce _f = vec2 . create ( ) ,
applyForce _worldAnchorA = vec2 . create ( ) ,
applyForce _worldAnchorB = vec2 . create ( ) ,
applyForce _ri = vec2 . create ( ) ,
applyForce _rj = vec2 . create ( ) ,
applyForce _tmp = vec2 . create ( ) ;
/ * *
* Apply the spring force to the connected bodies .
* @ method applyForce
* /
LinearSpring . prototype . applyForce = function ( ) {
var k = this . stiffness ,
d = this . damping ,
l = this . restLength ,
bodyA = this . bodyA ,
bodyB = this . bodyB ,
r = applyForce _r ,
r _unit = applyForce _r _unit ,
u = applyForce _u ,
f = applyForce _f ,
tmp = applyForce _tmp ;
var worldAnchorA = applyForce _worldAnchorA ,
worldAnchorB = applyForce _worldAnchorB ,
ri = applyForce _ri ,
rj = applyForce _rj ;
// Get world anchors
this . getWorldAnchorA ( worldAnchorA ) ;
this . getWorldAnchorB ( worldAnchorB ) ;
// Get offset points
vec2 . sub ( ri , worldAnchorA , bodyA . position ) ;
vec2 . sub ( rj , worldAnchorB , bodyB . position ) ;
// Compute distance vector between world anchor points
vec2 . sub ( r , worldAnchorB , worldAnchorA ) ;
var rlen = vec2 . len ( r ) ;
vec2 . normalize ( r _unit , r ) ;
//console.log(rlen)
//console.log("A",vec2.str(worldAnchorA),"B",vec2.str(worldAnchorB))
// Compute relative velocity of the anchor points, u
vec2 . sub ( u , bodyB . velocity , bodyA . velocity ) ;
vec2 . crossZV ( tmp , bodyB . angularVelocity , rj ) ;
vec2 . add ( u , u , tmp ) ;
vec2 . crossZV ( tmp , bodyA . angularVelocity , ri ) ;
vec2 . sub ( u , u , tmp ) ;
// F = - k * ( x - L ) - D * ( u )
vec2 . scale ( f , r _unit , - k * ( rlen - l ) - d * vec2 . dot ( u , r _unit ) ) ;
// Add forces to bodies
vec2 . sub ( bodyA . force , bodyA . force , f ) ;
vec2 . add ( bodyB . force , bodyB . force , f ) ;
// Angular force
var ri _x _f = vec2 . crossLength ( ri , f ) ;
var rj _x _f = vec2 . crossLength ( rj , f ) ;
bodyA . angularForce -= ri _x _f ;
bodyB . angularForce += rj _x _f ;
} ;
} , { "../math/vec2" : 30 , "../utils/Utils" : 57 , "./Spring" : 34 } ] , 33 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' ) ;
var Spring = _dereq _ ( './Spring' ) ;
module . exports = RotationalSpring ;
/ * *
* A rotational spring , connecting two bodies rotation . This spring explicitly adds angularForce ( torque ) to the bodies .
*
* The spring can be combined with a { { # crossLink "RevoluteConstraint" } } { { / c r o s s L i n k } } t o m a k e , f o r e x a m p l e , a m o u s e t r a p .
*
* @ class RotationalSpring
* @ extends Spring
* @ constructor
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Object } [ options ]
* @ param { number } [ options . restAngle ] The relative angle of bodies at which the spring is at rest . If not given , it ' s set to the current relative angle between the bodies .
* @ param { number } [ options . stiffness = 100 ] Spring constant ( see Hookes Law ) . A number >= 0.
* @ param { number } [ options . damping = 1 ] A number >= 0.
* /
function RotationalSpring ( bodyA , bodyB , options ) {
options = options || { } ;
Spring . call ( this , bodyA , bodyB , options ) ;
/ * *
* Rest angle of the spring .
* @ property restAngle
* @ type { number }
* /
this . restAngle = typeof ( options . restAngle ) === "number" ? options . restAngle : bodyB . angle - bodyA . angle ;
}
RotationalSpring . prototype = new Spring ( ) ;
RotationalSpring . prototype . constructor = RotationalSpring ;
/ * *
* Apply the spring force to the connected bodies .
* @ method applyForce
* /
RotationalSpring . prototype . applyForce = function ( ) {
var k = this . stiffness ,
d = this . damping ,
l = this . restAngle ,
bodyA = this . bodyA ,
bodyB = this . bodyB ,
x = bodyB . angle - bodyA . angle ,
u = bodyB . angularVelocity - bodyA . angularVelocity ;
var torque = - k * ( x - l ) - d * u * 0 ;
bodyA . angularForce -= torque ;
bodyB . angularForce += torque ;
} ;
} , { "../math/vec2" : 30 , "./Spring" : 34 } ] , 34 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' ) ;
var Utils = _dereq _ ( '../utils/Utils' ) ;
module . exports = Spring ;
/ * *
* A spring , connecting two bodies . The Spring explicitly adds force and angularForce to the bodies and does therefore not put load on the constraint solver .
*
* @ class Spring
* @ constructor
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Object } [ options ]
* @ param { number } [ options . stiffness = 100 ] Spring constant ( see Hookes Law ) . A number >= 0.
* @ param { number } [ options . damping = 1 ] A number >= 0. Default : 1
* @ param { Array } [ options . localAnchorA ] Where to hook the spring to body A , in local body coordinates . Defaults to the body center .
* @ param { Array } [ options . localAnchorB ]
* @ param { Array } [ options . worldAnchorA ] Where to hook the spring to body A , in world coordinates . Overrides the option "localAnchorA" if given .
* @ param { Array } [ options . worldAnchorB ]
* /
function Spring ( bodyA , bodyB , options ) {
options = Utils . defaults ( options , {
stiffness : 100 ,
damping : 1 ,
} ) ;
/ * *
* Stiffness of the spring .
* @ property stiffness
* @ type { number }
* /
this . stiffness = options . stiffness ;
/ * *
* Damping of the spring .
* @ property damping
* @ type { number }
* /
this . damping = options . damping ;
/ * *
* First connected body .
* @ property bodyA
* @ type { Body }
* /
this . bodyA = bodyA ;
/ * *
* Second connected body .
* @ property bodyB
* @ type { Body }
* /
this . bodyB = bodyB ;
}
/ * *
* Apply the spring force to the connected bodies .
* @ method applyForce
* /
Spring . prototype . applyForce = function ( ) {
// To be implemented by subclasses
} ;
} , { "../math/vec2" : 30 , "../utils/Utils" : 57 } ] , 35 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' ) ;
var Utils = _dereq _ ( '../utils/Utils' ) ;
var Constraint = _dereq _ ( '../constraints/Constraint' ) ;
var FrictionEquation = _dereq _ ( '../equations/FrictionEquation' ) ;
var Body = _dereq _ ( '../objects/Body' ) ;
module . exports = TopDownVehicle ;
/ * *
* @ class TopDownVehicle
* @ constructor
* @ param { Body } chassisBody A dynamic body , already added to the world .
* @ param { Object } [ options ]
*
* @ example
*
* // Create a dynamic body for the chassis
* var chassisBody = new Body ( {
* mass : 1
* } ) ;
* var boxShape = new Box ( { width : 0.5 , height : 1 } ) ;
* chassisBody . addShape ( boxShape ) ;
* world . addBody ( chassisBody ) ;
*
* // Create the vehicle
* var vehicle = new TopDownVehicle ( chassisBody ) ;
*
* // Add one front wheel and one back wheel - we don't actually need four :)
* var frontWheel = vehicle . addWheel ( {
* localPosition : [ 0 , 0.5 ] // front
* } ) ;
* frontWheel . setSideFriction ( 4 ) ;
*
* // Back wheel
* var backWheel = vehicle . addWheel ( {
* localPosition : [ 0 , - 0.5 ] // back
* } ) ;
* backWheel . setSideFriction ( 3 ) ; // Less side friction on back wheel makes it easier to drift
* vehicle . addToWorld ( world ) ;
*
* // Steer value zero means straight forward. Positive is left and negative right.
* frontWheel . steerValue = Math . PI / 16 ;
*
* // Engine force forward
* backWheel . engineForce = 10 ;
* backWheel . setBrakeForce ( 0 ) ;
* /
function TopDownVehicle ( chassisBody , options ) {
options = options || { } ;
/ * *
* @ property { Body } chassisBody
* /
this . chassisBody = chassisBody ;
/ * *
* @ property { Array } wheels
* /
this . wheels = [ ] ;
// A dummy body to constrain the chassis to
this . groundBody = new Body ( { mass : 0 } ) ;
this . world = null ;
var that = this ;
this . preStepCallback = function ( ) {
that . update ( ) ;
} ;
}
/ * *
* @ method addToWorld
* @ param { World } world
* /
TopDownVehicle . prototype . addToWorld = function ( world ) {
this . world = world ;
world . addBody ( this . groundBody ) ;
world . on ( 'preStep' , this . preStepCallback ) ;
for ( var i = 0 ; i < this . wheels . length ; i ++ ) {
var wheel = this . wheels [ i ] ;
world . addConstraint ( wheel ) ;
}
} ;
/ * *
* @ method removeFromWorld
* @ param { World } world
* /
TopDownVehicle . prototype . removeFromWorld = function ( ) {
var world = this . world ;
world . removeBody ( this . groundBody ) ;
world . off ( 'preStep' , this . preStepCallback ) ;
for ( var i = 0 ; i < this . wheels . length ; i ++ ) {
var wheel = this . wheels [ i ] ;
world . removeConstraint ( wheel ) ;
}
this . world = null ;
} ;
/ * *
* @ method addWheel
* @ param { object } [ wheelOptions ]
* @ return { WheelConstraint }
* /
TopDownVehicle . prototype . addWheel = function ( wheelOptions ) {
var wheel = new WheelConstraint ( this , wheelOptions ) ;
this . wheels . push ( wheel ) ;
return wheel ;
} ;
/ * *
* @ method update
* /
TopDownVehicle . prototype . update = function ( ) {
for ( var i = 0 ; i < this . wheels . length ; i ++ ) {
this . wheels [ i ] . update ( ) ;
}
} ;
/ * *
* @ class WheelConstraint
* @ constructor
* @ extends { Constraint }
* @ param { Vehicle } vehicle
* @ param { object } [ options ]
* @ param { Array } [ options . localForwardVector ] The local wheel forward vector in local body space . Default is zero .
* @ param { Array } [ options . localPosition ] The local position of the wheen in the chassis body . Default is zero - the center of the body .
* @ param { Array } [ options . sideFriction = 5 ] The max friction force in the sideways direction .
* /
function WheelConstraint ( vehicle , options ) {
options = options || { } ;
this . vehicle = vehicle ;
this . forwardEquation = new FrictionEquation ( vehicle . chassisBody , vehicle . groundBody ) ;
this . sideEquation = new FrictionEquation ( vehicle . chassisBody , vehicle . groundBody ) ;
/ * *
* @ property { number } steerValue
* /
this . steerValue = 0 ;
/ * *
* @ property { number } engineForce
* /
this . engineForce = 0 ;
this . setSideFriction ( options . sideFriction !== undefined ? options . sideFriction : 5 ) ;
/ * *
* @ property { Array } localForwardVector
* /
this . localForwardVector = vec2 . fromValues ( 0 , 1 ) ;
if ( options . localForwardVector ) {
vec2 . copy ( this . localForwardVector , options . localForwardVector ) ;
}
/ * *
* @ property { Array } localPosition
* /
this . localPosition = vec2 . fromValues ( 0 , 0 ) ;
if ( options . localPosition ) {
vec2 . copy ( this . localPosition , options . localPosition ) ;
}
Constraint . apply ( this , vehicle . chassisBody , vehicle . groundBody ) ;
this . equations . push (
this . forwardEquation ,
this . sideEquation
) ;
this . setBrakeForce ( 0 ) ;
}
WheelConstraint . prototype = new Constraint ( ) ;
/ * *
* @ method setForwardFriction
* /
WheelConstraint . prototype . setBrakeForce = function ( force ) {
this . forwardEquation . setSlipForce ( force ) ;
} ;
/ * *
* @ method setSideFriction
* /
WheelConstraint . prototype . setSideFriction = function ( force ) {
this . sideEquation . setSlipForce ( force ) ;
} ;
var worldVelocity = vec2 . create ( ) ;
var relativePoint = vec2 . create ( ) ;
/ * *
* @ method getSpeed
* /
WheelConstraint . prototype . getSpeed = function ( ) {
this . vehicle . chassisBody . vectorToWorldFrame ( relativePoint , this . localForwardVector ) ;
this . vehicle . chassisBody . getVelocityAtPoint ( worldVelocity , relativePoint ) ;
return vec2 . dot ( worldVelocity , relativePoint ) ;
} ;
var tmpVec = vec2 . create ( ) ;
/ * *
* @ method update
* /
WheelConstraint . prototype . update = function ( ) {
// Directional
this . vehicle . chassisBody . vectorToWorldFrame ( this . forwardEquation . t , this . localForwardVector ) ;
vec2 . rotate ( this . sideEquation . t , this . localForwardVector , Math . PI / 2 ) ;
this . vehicle . chassisBody . vectorToWorldFrame ( this . sideEquation . t , this . sideEquation . t ) ;
vec2 . rotate ( this . forwardEquation . t , this . forwardEquation . t , this . steerValue ) ;
vec2 . rotate ( this . sideEquation . t , this . sideEquation . t , this . steerValue ) ;
// Attachment point
this . vehicle . chassisBody . toWorldFrame ( this . forwardEquation . contactPointB , this . localPosition ) ;
vec2 . copy ( this . sideEquation . contactPointB , this . forwardEquation . contactPointB ) ;
this . vehicle . chassisBody . vectorToWorldFrame ( this . forwardEquation . contactPointA , this . localPosition ) ;
vec2 . copy ( this . sideEquation . contactPointA , this . forwardEquation . contactPointA ) ;
// Add engine force
vec2 . normalize ( tmpVec , this . forwardEquation . t ) ;
vec2 . scale ( tmpVec , tmpVec , this . engineForce ) ;
this . vehicle . chassisBody . applyForce ( tmpVec , this . forwardEquation . contactPointA ) ;
} ;
} , { "../constraints/Constraint" : 14 , "../equations/FrictionEquation" : 23 , "../math/vec2" : 30 , "../objects/Body" : 31 , "../utils/Utils" : 57 } ] , 36 : [ function ( _dereq _ , module , exports ) {
// Export p2 classes
var p2 = module . exports = {
AABB : _dereq _ ( './collision/AABB' ) ,
AngleLockEquation : _dereq _ ( './equations/AngleLockEquation' ) ,
Body : _dereq _ ( './objects/Body' ) ,
Broadphase : _dereq _ ( './collision/Broadphase' ) ,
Capsule : _dereq _ ( './shapes/Capsule' ) ,
Circle : _dereq _ ( './shapes/Circle' ) ,
Constraint : _dereq _ ( './constraints/Constraint' ) ,
ContactEquation : _dereq _ ( './equations/ContactEquation' ) ,
ContactEquationPool : _dereq _ ( './utils/ContactEquationPool' ) ,
ContactMaterial : _dereq _ ( './material/ContactMaterial' ) ,
Convex : _dereq _ ( './shapes/Convex' ) ,
DistanceConstraint : _dereq _ ( './constraints/DistanceConstraint' ) ,
Equation : _dereq _ ( './equations/Equation' ) ,
EventEmitter : _dereq _ ( './events/EventEmitter' ) ,
FrictionEquation : _dereq _ ( './equations/FrictionEquation' ) ,
FrictionEquationPool : _dereq _ ( './utils/FrictionEquationPool' ) ,
GearConstraint : _dereq _ ( './constraints/GearConstraint' ) ,
GSSolver : _dereq _ ( './solver/GSSolver' ) ,
Heightfield : _dereq _ ( './shapes/Heightfield' ) ,
Line : _dereq _ ( './shapes/Line' ) ,
LockConstraint : _dereq _ ( './constraints/LockConstraint' ) ,
Material : _dereq _ ( './material/Material' ) ,
Narrowphase : _dereq _ ( './collision/Narrowphase' ) ,
NaiveBroadphase : _dereq _ ( './collision/NaiveBroadphase' ) ,
Particle : _dereq _ ( './shapes/Particle' ) ,
Plane : _dereq _ ( './shapes/Plane' ) ,
Pool : _dereq _ ( './utils/Pool' ) ,
RevoluteConstraint : _dereq _ ( './constraints/RevoluteConstraint' ) ,
PrismaticConstraint : _dereq _ ( './constraints/PrismaticConstraint' ) ,
Ray : _dereq _ ( './collision/Ray' ) ,
RaycastResult : _dereq _ ( './collision/RaycastResult' ) ,
Box : _dereq _ ( './shapes/Box' ) ,
RotationalVelocityEquation : _dereq _ ( './equations/RotationalVelocityEquation' ) ,
SAPBroadphase : _dereq _ ( './collision/SAPBroadphase' ) ,
Shape : _dereq _ ( './shapes/Shape' ) ,
Solver : _dereq _ ( './solver/Solver' ) ,
Spring : _dereq _ ( './objects/Spring' ) ,
TopDownVehicle : _dereq _ ( './objects/TopDownVehicle' ) ,
LinearSpring : _dereq _ ( './objects/LinearSpring' ) ,
RotationalSpring : _dereq _ ( './objects/RotationalSpring' ) ,
Utils : _dereq _ ( './utils/Utils' ) ,
World : _dereq _ ( './world/World' ) ,
vec2 : _dereq _ ( './math/vec2' ) ,
version : _dereq _ ( '../package.json' ) . version ,
} ;
Object . defineProperty ( p2 , 'Rectangle' , {
get : function ( ) {
console . warn ( 'The Rectangle class has been renamed to Box.' ) ;
return this . Box ;
}
} ) ;
} , { "../package.json" : 6 , "./collision/AABB" : 7 , "./collision/Broadphase" : 8 , "./collision/NaiveBroadphase" : 9 , "./collision/Narrowphase" : 10 , "./collision/Ray" : 11 , "./collision/RaycastResult" : 12 , "./collision/SAPBroadphase" : 13 , "./constraints/Constraint" : 14 , "./constraints/DistanceConstraint" : 15 , "./constraints/GearConstraint" : 16 , "./constraints/LockConstraint" : 17 , "./constraints/PrismaticConstraint" : 18 , "./constraints/RevoluteConstraint" : 19 , "./equations/AngleLockEquation" : 20 , "./equations/ContactEquation" : 21 , "./equations/Equation" : 22 , "./equations/FrictionEquation" : 23 , "./equations/RotationalVelocityEquation" : 25 , "./events/EventEmitter" : 26 , "./material/ContactMaterial" : 27 , "./material/Material" : 28 , "./math/vec2" : 30 , "./objects/Body" : 31 , "./objects/LinearSpring" : 32 , "./objects/RotationalSpring" : 33 , "./objects/Spring" : 34 , "./objects/TopDownVehicle" : 35 , "./shapes/Box" : 37 , "./shapes/Capsule" : 38 , "./shapes/Circle" : 39 , "./shapes/Convex" : 40 , "./shapes/Heightfield" : 41 , "./shapes/Line" : 42 , "./shapes/Particle" : 43 , "./shapes/Plane" : 44 , "./shapes/Shape" : 45 , "./solver/GSSolver" : 46 , "./solver/Solver" : 47 , "./utils/ContactEquationPool" : 48 , "./utils/FrictionEquationPool" : 49 , "./utils/Pool" : 55 , "./utils/Utils" : 57 , "./world/World" : 61 } ] , 37 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' )
, Shape = _dereq _ ( './Shape' )
, Convex = _dereq _ ( './Convex' ) ;
module . exports = Box ;
/ * *
* Box shape class .
* @ class Box
* @ constructor
* @ param { object } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* @ param { Number } [ options . width = 1 ] Total width of the box
* @ param { Number } [ options . height = 1 ] Total height of the box
* @ extends Convex
* /
function Box ( options ) {
if ( typeof ( arguments [ 0 ] ) === 'number' && typeof ( arguments [ 1 ] ) === 'number' ) {
options = {
width : arguments [ 0 ] ,
height : arguments [ 1 ]
} ;
console . warn ( 'The Rectangle has been renamed to Box and its constructor signature has changed. Please use the following format: new Box({ width: 1, height: 1, ... })' ) ;
}
options = options || { } ;
/ * *
* Total width of the box
* @ property width
* @ type { Number }
* /
var width = this . width = options . width || 1 ;
/ * *
* Total height of the box
* @ property height
* @ type { Number }
* /
var height = this . height = options . height || 1 ;
var verts = [
vec2 . fromValues ( - width / 2 , - height / 2 ) ,
vec2 . fromValues ( width / 2 , - height / 2 ) ,
vec2 . fromValues ( width / 2 , height / 2 ) ,
vec2 . fromValues ( - width / 2 , height / 2 )
] ;
var axes = [
vec2 . fromValues ( 1 , 0 ) ,
vec2 . fromValues ( 0 , 1 )
] ;
options . vertices = verts ;
options . axes = axes ;
options . type = Shape . BOX ;
Convex . call ( this , options ) ;
}
Box . prototype = new Convex ( ) ;
Box . prototype . constructor = Box ;
/ * *
* Compute moment of inertia
* @ method computeMomentOfInertia
* @ param { Number } mass
* @ return { Number }
* /
Box . prototype . computeMomentOfInertia = function ( mass ) {
var w = this . width ,
h = this . height ;
return mass * ( h * h + w * w ) / 12 ;
} ;
/ * *
* Update the bounding radius
* @ method updateBoundingRadius
* /
Box . prototype . updateBoundingRadius = function ( ) {
var w = this . width ,
h = this . height ;
this . boundingRadius = Math . sqrt ( w * w + h * h ) / 2 ;
} ;
var corner1 = vec2 . create ( ) ,
corner2 = vec2 . create ( ) ,
corner3 = vec2 . create ( ) ,
corner4 = vec2 . create ( ) ;
/ * *
* @ method computeAABB
* @ param { AABB } out The resulting AABB .
* @ param { Array } position
* @ param { Number } angle
* /
Box . prototype . computeAABB = function ( out , position , angle ) {
out . setFromPoints ( this . vertices , position , angle , 0 ) ;
} ;
Box . prototype . updateArea = function ( ) {
this . area = this . width * this . height ;
} ;
} , { "../math/vec2" : 30 , "./Convex" : 40 , "./Shape" : 45 } ] , 38 : [ function ( _dereq _ , module , exports ) {
var Shape = _dereq _ ( './Shape' )
, vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = Capsule ;
/ * *
* Capsule shape class .
* @ class Capsule
* @ constructor
* @ extends Shape
* @ param { object } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* @ param { Number } [ options . length = 1 ] The distance between the end points
* @ param { Number } [ options . radius = 1 ] Radius of the capsule
* @ example
* var capsuleShape = new Capsule ( {
* length : 1 ,
* radius : 2
* } ) ;
* body . addShape ( capsuleShape ) ;
* /
function Capsule ( options ) {
if ( typeof ( arguments [ 0 ] ) === 'number' && typeof ( arguments [ 1 ] ) === 'number' ) {
options = {
length : arguments [ 0 ] ,
radius : arguments [ 1 ]
} ;
console . warn ( 'The Capsule constructor signature has changed. Please use the following format: new Capsule({ radius: 1, length: 1 })' ) ;
}
options = options || { } ;
/ * *
* The distance between the end points .
* @ property { Number } length
* /
this . length = options . length || 1 ;
/ * *
* The radius of the capsule .
* @ property { Number } radius
* /
this . radius = options . radius || 1 ;
options . type = Shape . CAPSULE ;
Shape . call ( this , options ) ;
}
Capsule . prototype = new Shape ( ) ;
Capsule . prototype . constructor = Capsule ;
/ * *
* Compute the mass moment of inertia of the Capsule .
* @ method conputeMomentOfInertia
* @ param { Number } mass
* @ return { Number }
* @ todo
* /
Capsule . prototype . computeMomentOfInertia = function ( mass ) {
// Approximate with rectangle
var r = this . radius ,
w = this . length + r , // 2*r is too much, 0 is too little
h = r * 2 ;
return mass * ( h * h + w * w ) / 12 ;
} ;
/ * *
* @ method updateBoundingRadius
* /
Capsule . prototype . updateBoundingRadius = function ( ) {
this . boundingRadius = this . radius + this . length / 2 ;
} ;
/ * *
* @ method updateArea
* /
Capsule . prototype . updateArea = function ( ) {
this . area = Math . PI * this . radius * this . radius + this . radius * 2 * this . length ;
} ;
var r = vec2 . create ( ) ;
/ * *
* @ method computeAABB
* @ param { AABB } out The resulting AABB .
* @ param { Array } position
* @ param { Number } angle
* /
Capsule . prototype . computeAABB = function ( out , position , angle ) {
var radius = this . radius ;
// Compute center position of one of the the circles, world oriented, but with local offset
vec2 . set ( r , this . length / 2 , 0 ) ;
if ( angle !== 0 ) {
vec2 . rotate ( r , r , angle ) ;
}
// Get bounds
vec2 . set ( out . upperBound , Math . max ( r [ 0 ] + radius , - r [ 0 ] + radius ) ,
Math . max ( r [ 1 ] + radius , - r [ 1 ] + radius ) ) ;
vec2 . set ( out . lowerBound , Math . min ( r [ 0 ] - radius , - r [ 0 ] - radius ) ,
Math . min ( r [ 1 ] - radius , - r [ 1 ] - radius ) ) ;
// Add offset
vec2 . add ( out . lowerBound , out . lowerBound , position ) ;
vec2 . add ( out . upperBound , out . upperBound , position ) ;
} ;
var intersectCapsule _hitPointWorld = vec2 . create ( ) ;
var intersectCapsule _normal = vec2 . create ( ) ;
var intersectCapsule _l0 = vec2 . create ( ) ;
var intersectCapsule _l1 = vec2 . create ( ) ;
var intersectCapsule _unit _y = vec2 . fromValues ( 0 , 1 ) ;
/ * *
* @ method raycast
* @ param { RaycastResult } result
* @ param { Ray } ray
* @ param { array } position
* @ param { number } angle
* /
Capsule . prototype . raycast = function ( result , ray , position , angle ) {
var from = ray . from ;
var to = ray . to ;
var direction = ray . direction ;
var hitPointWorld = intersectCapsule _hitPointWorld ;
var normal = intersectCapsule _normal ;
var l0 = intersectCapsule _l0 ;
var l1 = intersectCapsule _l1 ;
// The sides
var halfLen = this . length / 2 ;
for ( var i = 0 ; i < 2 ; i ++ ) {
// get start and end of the line
var y = this . radius * ( i * 2 - 1 ) ;
vec2 . set ( l0 , - halfLen , y ) ;
vec2 . set ( l1 , halfLen , y ) ;
vec2 . toGlobalFrame ( l0 , l0 , position , angle ) ;
vec2 . toGlobalFrame ( l1 , l1 , position , angle ) ;
var delta = vec2 . getLineSegmentsIntersectionFraction ( from , to , l0 , l1 ) ;
if ( delta >= 0 ) {
vec2 . rotate ( normal , intersectCapsule _unit _y , angle ) ;
vec2 . scale ( normal , normal , ( i * 2 - 1 ) ) ;
ray . reportIntersection ( result , delta , normal , - 1 ) ;
if ( result . shouldStop ( ray ) ) {
return ;
}
}
}
// Circles
var diagonalLengthSquared = Math . pow ( this . radius , 2 ) + Math . pow ( halfLen , 2 ) ;
for ( var i = 0 ; i < 2 ; i ++ ) {
vec2 . set ( l0 , halfLen * ( i * 2 - 1 ) , 0 ) ;
vec2 . toGlobalFrame ( l0 , l0 , position , angle ) ;
var a = Math . pow ( to [ 0 ] - from [ 0 ] , 2 ) + Math . pow ( to [ 1 ] - from [ 1 ] , 2 ) ;
var b = 2 * ( ( to [ 0 ] - from [ 0 ] ) * ( from [ 0 ] - l0 [ 0 ] ) + ( to [ 1 ] - from [ 1 ] ) * ( from [ 1 ] - l0 [ 1 ] ) ) ;
var c = Math . pow ( from [ 0 ] - l0 [ 0 ] , 2 ) + Math . pow ( from [ 1 ] - l0 [ 1 ] , 2 ) - Math . pow ( this . radius , 2 ) ;
var delta = Math . pow ( b , 2 ) - 4 * a * c ;
if ( delta < 0 ) {
// No intersection
continue ;
} else if ( delta === 0 ) {
// single intersection point
vec2 . lerp ( hitPointWorld , from , to , delta ) ;
if ( vec2 . squaredDistance ( hitPointWorld , position ) > diagonalLengthSquared ) {
vec2 . sub ( normal , hitPointWorld , l0 ) ;
vec2 . normalize ( normal , normal ) ;
ray . reportIntersection ( result , delta , normal , - 1 ) ;
if ( result . shouldStop ( ray ) ) {
return ;
}
}
} else {
var sqrtDelta = Math . sqrt ( delta ) ;
var inv2a = 1 / ( 2 * a ) ;
var d1 = ( - b - sqrtDelta ) * inv2a ;
var d2 = ( - b + sqrtDelta ) * inv2a ;
if ( d1 >= 0 && d1 <= 1 ) {
vec2 . lerp ( hitPointWorld , from , to , d1 ) ;
if ( vec2 . squaredDistance ( hitPointWorld , position ) > diagonalLengthSquared ) {
vec2 . sub ( normal , hitPointWorld , l0 ) ;
vec2 . normalize ( normal , normal ) ;
ray . reportIntersection ( result , d1 , normal , - 1 ) ;
if ( result . shouldStop ( ray ) ) {
return ;
}
}
}
if ( d2 >= 0 && d2 <= 1 ) {
vec2 . lerp ( hitPointWorld , from , to , d2 ) ;
if ( vec2 . squaredDistance ( hitPointWorld , position ) > diagonalLengthSquared ) {
vec2 . sub ( normal , hitPointWorld , l0 ) ;
vec2 . normalize ( normal , normal ) ;
ray . reportIntersection ( result , d2 , normal , - 1 ) ;
if ( result . shouldStop ( ray ) ) {
return ;
}
}
}
}
}
} ;
} , { "../math/vec2" : 30 , "./Shape" : 45 } ] , 39 : [ function ( _dereq _ , module , exports ) {
var Shape = _dereq _ ( './Shape' )
, vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = Circle ;
/ * *
* Circle shape class .
* @ class Circle
* @ extends Shape
* @ constructor
* @ param { options } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* @ param { number } [ options . radius = 1 ] The radius of this circle
*
* @ example
* var circleShape = new Circle ( { radius : 1 } ) ;
* body . addShape ( circleShape ) ;
* /
function Circle ( options ) {
if ( typeof ( arguments [ 0 ] ) === 'number' ) {
options = {
radius : arguments [ 0 ]
} ;
console . warn ( 'The Circle constructor signature has changed. Please use the following format: new Circle({ radius: 1 })' ) ;
}
options = options || { } ;
/ * *
* The radius of the circle .
* @ property radius
* @ type { number }
* /
this . radius = options . radius || 1 ;
options . type = Shape . CIRCLE ;
Shape . call ( this , options ) ;
}
Circle . prototype = new Shape ( ) ;
Circle . prototype . constructor = Circle ;
/ * *
* @ method computeMomentOfInertia
* @ param { Number } mass
* @ return { Number }
* /
Circle . prototype . computeMomentOfInertia = function ( mass ) {
var r = this . radius ;
return mass * r * r / 2 ;
} ;
/ * *
* @ method updateBoundingRadius
* @ return { Number }
* /
Circle . prototype . updateBoundingRadius = function ( ) {
this . boundingRadius = this . radius ;
} ;
/ * *
* @ method updateArea
* @ return { Number }
* /
Circle . prototype . updateArea = function ( ) {
this . area = Math . PI * this . radius * this . radius ;
} ;
/ * *
* @ method computeAABB
* @ param { AABB } out The resulting AABB .
* @ param { Array } position
* @ param { Number } angle
* /
Circle . prototype . computeAABB = function ( out , position , angle ) {
var r = this . radius ;
vec2 . set ( out . upperBound , r , r ) ;
vec2 . set ( out . lowerBound , - r , - r ) ;
if ( position ) {
vec2 . add ( out . lowerBound , out . lowerBound , position ) ;
vec2 . add ( out . upperBound , out . upperBound , position ) ;
}
} ;
var Ray _intersectSphere _intersectionPoint = vec2 . create ( ) ;
var Ray _intersectSphere _normal = vec2 . create ( ) ;
/ * *
* @ method raycast
* @ param { RaycastResult } result
* @ param { Ray } ray
* @ param { array } position
* @ param { number } angle
* /
Circle . prototype . raycast = function ( result , ray , position , angle ) {
var from = ray . from ,
to = ray . to ,
r = this . radius ;
var a = Math . pow ( to [ 0 ] - from [ 0 ] , 2 ) + Math . pow ( to [ 1 ] - from [ 1 ] , 2 ) ;
var b = 2 * ( ( to [ 0 ] - from [ 0 ] ) * ( from [ 0 ] - position [ 0 ] ) + ( to [ 1 ] - from [ 1 ] ) * ( from [ 1 ] - position [ 1 ] ) ) ;
var c = Math . pow ( from [ 0 ] - position [ 0 ] , 2 ) + Math . pow ( from [ 1 ] - position [ 1 ] , 2 ) - Math . pow ( r , 2 ) ;
var delta = Math . pow ( b , 2 ) - 4 * a * c ;
var intersectionPoint = Ray _intersectSphere _intersectionPoint ;
var normal = Ray _intersectSphere _normal ;
if ( delta < 0 ) {
// No intersection
return ;
} else if ( delta === 0 ) {
// single intersection point
vec2 . lerp ( intersectionPoint , from , to , delta ) ;
vec2 . sub ( normal , intersectionPoint , position ) ;
vec2 . normalize ( normal , normal ) ;
ray . reportIntersection ( result , delta , normal , - 1 ) ;
} else {
var sqrtDelta = Math . sqrt ( delta ) ;
var inv2a = 1 / ( 2 * a ) ;
var d1 = ( - b - sqrtDelta ) * inv2a ;
var d2 = ( - b + sqrtDelta ) * inv2a ;
if ( d1 >= 0 && d1 <= 1 ) {
vec2 . lerp ( intersectionPoint , from , to , d1 ) ;
vec2 . sub ( normal , intersectionPoint , position ) ;
vec2 . normalize ( normal , normal ) ;
ray . reportIntersection ( result , d1 , normal , - 1 ) ;
if ( result . shouldStop ( ray ) ) {
return ;
}
}
if ( d2 >= 0 && d2 <= 1 ) {
vec2 . lerp ( intersectionPoint , from , to , d2 ) ;
vec2 . sub ( normal , intersectionPoint , position ) ;
vec2 . normalize ( normal , normal ) ;
ray . reportIntersection ( result , d2 , normal , - 1 ) ;
}
}
} ;
} , { "../math/vec2" : 30 , "./Shape" : 45 } ] , 40 : [ function ( _dereq _ , module , exports ) {
var Shape = _dereq _ ( './Shape' )
, vec2 = _dereq _ ( '../math/vec2' )
, polyk = _dereq _ ( '../math/polyk' )
, decomp = _dereq _ ( 'poly-decomp' ) ;
module . exports = Convex ;
/ * *
* Convex shape class .
* @ class Convex
* @ constructor
* @ extends Shape
* @ param { object } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* @ param { Array } [ options . vertices ] An array of vertices that span this shape . Vertices are given in counter - clockwise ( CCW ) direction .
* @ param { Array } [ options . axes ] An array of unit length vectors , representing the symmetry axes in the convex .
* @ example
* // Create a box
* var vertices = [ [ - 1 , - 1 ] , [ 1 , - 1 ] , [ 1 , 1 ] , [ - 1 , 1 ] ] ;
* var convexShape = new Convex ( { vertices : vertices } ) ;
* body . addShape ( convexShape ) ;
* /
function Convex ( options ) {
if ( Array . isArray ( arguments [ 0 ] ) ) {
options = {
vertices : arguments [ 0 ] ,
axes : arguments [ 1 ]
} ;
console . warn ( 'The Convex constructor signature has changed. Please use the following format: new Convex({ vertices: [...], ... })' ) ;
}
options = options || { } ;
/ * *
* Vertices defined in the local frame .
* @ property vertices
* @ type { Array }
* /
this . vertices = [ ] ;
// Copy the verts
var vertices = options . vertices !== undefined ? options . vertices : [ ] ;
for ( var i = 0 ; i < vertices . length ; i ++ ) {
var v = vec2 . create ( ) ;
vec2 . copy ( v , vertices [ i ] ) ;
this . vertices . push ( v ) ;
}
/ * *
* Axes defined in the local frame .
* @ property axes
* @ type { Array }
* /
this . axes = [ ] ;
if ( options . axes ) {
// Copy the axes
for ( var i = 0 ; i < options . axes . length ; i ++ ) {
var axis = vec2 . create ( ) ;
vec2 . copy ( axis , options . axes [ i ] ) ;
this . axes . push ( axis ) ;
}
} else {
// Construct axes from the vertex data
for ( var i = 0 ; i < this . vertices . length ; i ++ ) {
// Get the world edge
var worldPoint0 = this . vertices [ i ] ;
var worldPoint1 = this . vertices [ ( i + 1 ) % this . vertices . length ] ;
var normal = vec2 . create ( ) ;
vec2 . sub ( normal , worldPoint1 , worldPoint0 ) ;
// Get normal - just rotate 90 degrees since vertices are given in CCW
vec2 . rotate90cw ( normal , normal ) ;
vec2 . normalize ( normal , normal ) ;
this . axes . push ( normal ) ;
}
}
/ * *
* The center of mass of the Convex
* @ property centerOfMass
* @ type { Array }
* /
this . centerOfMass = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Triangulated version of this convex . The structure is Array of 3 - Arrays , and each subarray contains 3 integers , referencing the vertices .
* @ property triangles
* @ type { Array }
* /
this . triangles = [ ] ;
if ( this . vertices . length ) {
this . updateTriangles ( ) ;
this . updateCenterOfMass ( ) ;
}
/ * *
* The bounding radius of the convex
* @ property boundingRadius
* @ type { Number }
* /
this . boundingRadius = 0 ;
options . type = Shape . CONVEX ;
Shape . call ( this , options ) ;
this . updateBoundingRadius ( ) ;
this . updateArea ( ) ;
if ( this . area < 0 ) {
throw new Error ( "Convex vertices must be given in conter-clockwise winding." ) ;
}
}
Convex . prototype = new Shape ( ) ;
Convex . prototype . constructor = Convex ;
var tmpVec1 = vec2 . create ( ) ;
var tmpVec2 = vec2 . create ( ) ;
/ * *
* Project a Convex onto a world - oriented axis
* @ method projectOntoAxis
* @ static
* @ param { Array } offset
* @ param { Array } localAxis
* @ param { Array } result
* /
Convex . prototype . projectOntoLocalAxis = function ( localAxis , result ) {
var max = null ,
min = null ,
v ,
value ,
localAxis = tmpVec1 ;
// Get projected position of all vertices
for ( var i = 0 ; i < this . vertices . length ; i ++ ) {
v = this . vertices [ i ] ;
value = vec2 . dot ( v , localAxis ) ;
if ( max === null || value > max ) {
max = value ;
}
if ( min === null || value < min ) {
min = value ;
}
}
if ( min > max ) {
var t = min ;
min = max ;
max = t ;
}
vec2 . set ( result , min , max ) ;
} ;
Convex . prototype . projectOntoWorldAxis = function ( localAxis , shapeOffset , shapeAngle , result ) {
var worldAxis = tmpVec2 ;
this . projectOntoLocalAxis ( localAxis , result ) ;
// Project the position of the body onto the axis - need to add this to the result
if ( shapeAngle !== 0 ) {
vec2 . rotate ( worldAxis , localAxis , shapeAngle ) ;
} else {
worldAxis = localAxis ;
}
var offset = vec2 . dot ( shapeOffset , worldAxis ) ;
vec2 . set ( result , result [ 0 ] + offset , result [ 1 ] + offset ) ;
} ;
/ * *
* Update the . triangles property
* @ method updateTriangles
* /
Convex . prototype . updateTriangles = function ( ) {
this . triangles . length = 0 ;
// Rewrite on polyk notation, array of numbers
var polykVerts = [ ] ;
for ( var i = 0 ; i < this . vertices . length ; i ++ ) {
var v = this . vertices [ i ] ;
polykVerts . push ( v [ 0 ] , v [ 1 ] ) ;
}
// Triangulate
var triangles = polyk . Triangulate ( polykVerts ) ;
// Loop over all triangles, add their inertia contributions to I
for ( var i = 0 ; i < triangles . length ; i += 3 ) {
var id1 = triangles [ i ] ,
id2 = triangles [ i + 1 ] ,
id3 = triangles [ i + 2 ] ;
// Add to triangles
this . triangles . push ( [ id1 , id2 , id3 ] ) ;
}
} ;
var updateCenterOfMass _centroid = vec2 . create ( ) ,
updateCenterOfMass _centroid _times _mass = vec2 . create ( ) ,
updateCenterOfMass _a = vec2 . create ( ) ,
updateCenterOfMass _b = vec2 . create ( ) ,
updateCenterOfMass _c = vec2 . create ( ) ,
updateCenterOfMass _ac = vec2 . create ( ) ,
updateCenterOfMass _ca = vec2 . create ( ) ,
updateCenterOfMass _cb = vec2 . create ( ) ,
updateCenterOfMass _n = vec2 . create ( ) ;
/ * *
* Update the . centerOfMass property .
* @ method updateCenterOfMass
* /
Convex . prototype . updateCenterOfMass = function ( ) {
var triangles = this . triangles ,
verts = this . vertices ,
cm = this . centerOfMass ,
centroid = updateCenterOfMass _centroid ,
n = updateCenterOfMass _n ,
a = updateCenterOfMass _a ,
b = updateCenterOfMass _b ,
c = updateCenterOfMass _c ,
ac = updateCenterOfMass _ac ,
ca = updateCenterOfMass _ca ,
cb = updateCenterOfMass _cb ,
centroid _times _mass = updateCenterOfMass _centroid _times _mass ;
vec2 . set ( cm , 0 , 0 ) ;
var totalArea = 0 ;
for ( var i = 0 ; i !== triangles . length ; i ++ ) {
var t = triangles [ i ] ,
a = verts [ t [ 0 ] ] ,
b = verts [ t [ 1 ] ] ,
c = verts [ t [ 2 ] ] ;
vec2 . centroid ( centroid , a , b , c ) ;
// Get mass for the triangle (density=1 in this case)
// http://math.stackexchange.com/questions/80198/area-of-triangle-via-vectors
var m = Convex . triangleArea ( a , b , c ) ;
totalArea += m ;
// Add to center of mass
vec2 . scale ( centroid _times _mass , centroid , m ) ;
vec2 . add ( cm , cm , centroid _times _mass ) ;
}
vec2 . scale ( cm , cm , 1 / totalArea ) ;
} ;
/ * *
* Compute the mass moment of inertia of the Convex .
* @ method computeMomentOfInertia
* @ param { Number } mass
* @ return { Number }
* @ see http : //www.gamedev.net/topic/342822-moment-of-inertia-of-a-polygon-2d/
* /
Convex . prototype . computeMomentOfInertia = function ( mass ) {
var denom = 0.0 ,
numer = 0.0 ,
N = this . vertices . length ;
for ( var j = N - 1 , i = 0 ; i < N ; j = i , i ++ ) {
var p0 = this . vertices [ j ] ;
var p1 = this . vertices [ i ] ;
var a = Math . abs ( vec2 . crossLength ( p0 , p1 ) ) ;
var b = vec2 . dot ( p1 , p1 ) + vec2 . dot ( p1 , p0 ) + vec2 . dot ( p0 , p0 ) ;
denom += a * b ;
numer += a ;
}
return ( mass / 6.0 ) * ( denom / numer ) ;
} ;
/ * *
* Updates the . boundingRadius property
* @ method updateBoundingRadius
* /
Convex . prototype . updateBoundingRadius = function ( ) {
var verts = this . vertices ,
r2 = 0 ;
for ( var i = 0 ; i !== verts . length ; i ++ ) {
var l2 = vec2 . squaredLength ( verts [ i ] ) ;
if ( l2 > r2 ) {
r2 = l2 ;
}
}
this . boundingRadius = Math . sqrt ( r2 ) ;
} ;
/ * *
* Get the area of the triangle spanned by the three points a , b , c . The area is positive if the points are given in counter - clockwise order , otherwise negative .
* @ static
* @ method triangleArea
* @ param { Array } a
* @ param { Array } b
* @ param { Array } c
* @ return { Number }
* /
Convex . triangleArea = function ( a , b , c ) {
return ( ( ( b [ 0 ] - a [ 0 ] ) * ( c [ 1 ] - a [ 1 ] ) ) - ( ( c [ 0 ] - a [ 0 ] ) * ( b [ 1 ] - a [ 1 ] ) ) ) * 0.5 ;
} ;
/ * *
* Update the . area
* @ method updateArea
* /
Convex . prototype . updateArea = function ( ) {
this . updateTriangles ( ) ;
this . area = 0 ;
var triangles = this . triangles ,
verts = this . vertices ;
for ( var i = 0 ; i !== triangles . length ; i ++ ) {
var t = triangles [ i ] ,
a = verts [ t [ 0 ] ] ,
b = verts [ t [ 1 ] ] ,
c = verts [ t [ 2 ] ] ;
// Get mass for the triangle (density=1 in this case)
var m = Convex . triangleArea ( a , b , c ) ;
this . area += m ;
}
} ;
/ * *
* @ method computeAABB
* @ param { AABB } out
* @ param { Array } position
* @ param { Number } angle
* /
Convex . prototype . computeAABB = function ( out , position , angle ) {
out . setFromPoints ( this . vertices , position , angle , 0 ) ;
} ;
var intersectConvex _rayStart = vec2 . create ( ) ;
var intersectConvex _rayEnd = vec2 . create ( ) ;
var intersectConvex _normal = vec2 . create ( ) ;
/ * *
* @ method raycast
* @ param { RaycastResult } result
* @ param { Ray } ray
* @ param { array } position
* @ param { number } angle
* /
Convex . prototype . raycast = function ( result , ray , position , angle ) {
var rayStart = intersectConvex _rayStart ;
var rayEnd = intersectConvex _rayEnd ;
var normal = intersectConvex _normal ;
var vertices = this . vertices ;
// Transform to local shape space
vec2 . toLocalFrame ( rayStart , ray . from , position , angle ) ;
vec2 . toLocalFrame ( rayEnd , ray . to , position , angle ) ;
var n = vertices . length ;
for ( var i = 0 ; i < n && ! result . shouldStop ( ray ) ; i ++ ) {
var q1 = vertices [ i ] ;
var q2 = vertices [ ( i + 1 ) % n ] ;
var delta = vec2 . getLineSegmentsIntersectionFraction ( rayStart , rayEnd , q1 , q2 ) ;
if ( delta >= 0 ) {
vec2 . sub ( normal , q2 , q1 ) ;
vec2 . rotate ( normal , normal , - Math . PI / 2 + angle ) ;
vec2 . normalize ( normal , normal ) ;
ray . reportIntersection ( result , delta , normal , i ) ;
}
}
} ;
} , { "../math/polyk" : 29 , "../math/vec2" : 30 , "./Shape" : 45 , "poly-decomp" : 5 } ] , 41 : [ function ( _dereq _ , module , exports ) {
var Shape = _dereq _ ( './Shape' )
, vec2 = _dereq _ ( '../math/vec2' )
, Utils = _dereq _ ( '../utils/Utils' ) ;
module . exports = Heightfield ;
/ * *
* Heightfield shape class . Height data is given as an array . These data points are spread out evenly with a distance "elementWidth" .
* @ class Heightfield
* @ extends Shape
* @ constructor
* @ param { object } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* @ param { array } [ options . heights ] An array of Y values that will be used to construct the terrain .
* @ param { Number } [ options . minValue ] Minimum value of the data points in the data array . Will be computed automatically if not given .
* @ param { Number } [ options . maxValue ] Maximum value .
* @ param { Number } [ options . elementWidth = 0.1 ] World spacing between the data points in X direction .
*
* @ example
* // Generate some height data (y-values).
* var heights = [ ] ;
* for ( var i = 0 ; i < 1000 ; i ++ ) {
* var y = 0.5 * Math . cos ( 0.2 * i ) ;
* heights . push ( y ) ;
* }
*
* // Create the heightfield shape
* var heightfieldShape = new Heightfield ( {
* heights : heights ,
* elementWidth : 1 // Distance between the data points in X direction
* } ) ;
* var heightfieldBody = new Body ( ) ;
* heightfieldBody . addShape ( heightfieldShape ) ;
* world . addBody ( heightfieldBody ) ;
*
* @ todo Should use a scale property with X and Y direction instead of just elementWidth
* /
function Heightfield ( options ) {
if ( Array . isArray ( arguments [ 0 ] ) ) {
options = {
heights : arguments [ 0 ]
} ;
if ( typeof ( arguments [ 1 ] ) === 'object' ) {
for ( var key in arguments [ 1 ] ) {
options [ key ] = arguments [ 1 ] [ key ] ;
}
}
console . warn ( 'The Heightfield constructor signature has changed. Please use the following format: new Heightfield({ heights: [...], ... })' ) ;
}
options = options || { } ;
/ * *
* An array of numbers , or height values , that are spread out along the x axis .
* @ property { array } heights
* /
this . heights = options . heights ? options . heights . slice ( 0 ) : [ ] ;
/ * *
* Max value of the heights
* @ property { number } maxValue
* /
this . maxValue = options . maxValue || null ;
/ * *
* Max value of the heights
* @ property { number } minValue
* /
this . minValue = options . minValue || null ;
/ * *
* The width of each element
* @ property { number } elementWidth
* /
this . elementWidth = options . elementWidth || 0.1 ;
if ( options . maxValue === undefined || options . minValue === undefined ) {
this . updateMaxMinValues ( ) ;
}
options . type = Shape . HEIGHTFIELD ;
Shape . call ( this , options ) ;
}
Heightfield . prototype = new Shape ( ) ;
Heightfield . prototype . constructor = Heightfield ;
/ * *
* Update the . minValue and the . maxValue
* @ method updateMaxMinValues
* /
Heightfield . prototype . updateMaxMinValues = function ( ) {
var data = this . heights ;
var maxValue = data [ 0 ] ;
var minValue = data [ 0 ] ;
for ( var i = 0 ; i !== data . length ; i ++ ) {
var v = data [ i ] ;
if ( v > maxValue ) {
maxValue = v ;
}
if ( v < minValue ) {
minValue = v ;
}
}
this . maxValue = maxValue ;
this . minValue = minValue ;
} ;
/ * *
* @ method computeMomentOfInertia
* @ param { Number } mass
* @ return { Number }
* /
Heightfield . prototype . computeMomentOfInertia = function ( mass ) {
return Number . MAX _VALUE ;
} ;
Heightfield . prototype . updateBoundingRadius = function ( ) {
this . boundingRadius = Number . MAX _VALUE ;
} ;
Heightfield . prototype . updateArea = function ( ) {
var data = this . heights ,
area = 0 ;
for ( var i = 0 ; i < data . length - 1 ; i ++ ) {
area += ( data [ i ] + data [ i + 1 ] ) / 2 * this . elementWidth ;
}
this . area = area ;
} ;
var points = [
vec2 . create ( ) ,
vec2 . create ( ) ,
vec2 . create ( ) ,
vec2 . create ( )
] ;
/ * *
* @ method computeAABB
* @ param { AABB } out The resulting AABB .
* @ param { Array } position
* @ param { Number } angle
* /
Heightfield . prototype . computeAABB = function ( out , position , angle ) {
vec2 . set ( points [ 0 ] , 0 , this . maxValue ) ;
vec2 . set ( points [ 1 ] , this . elementWidth * this . heights . length , this . maxValue ) ;
vec2 . set ( points [ 2 ] , this . elementWidth * this . heights . length , this . minValue ) ;
vec2 . set ( points [ 3 ] , 0 , this . minValue ) ;
out . setFromPoints ( points , position , angle ) ;
} ;
/ * *
* Get a line segment in the heightfield
* @ method getLineSegment
* @ param { array } start Where to store the resulting start point
* @ param { array } end Where to store the resulting end point
* @ param { number } i
* /
Heightfield . prototype . getLineSegment = function ( start , end , i ) {
var data = this . heights ;
var width = this . elementWidth ;
vec2 . set ( start , i * width , data [ i ] ) ;
vec2 . set ( end , ( i + 1 ) * width , data [ i + 1 ] ) ;
} ;
Heightfield . prototype . getSegmentIndex = function ( position ) {
return Math . floor ( position [ 0 ] / this . elementWidth ) ;
} ;
Heightfield . prototype . getClampedSegmentIndex = function ( position ) {
var i = this . getSegmentIndex ( position ) ;
i = Math . min ( this . heights . length , Math . max ( i , 0 ) ) ; // clamp
return i ;
} ;
var intersectHeightfield _hitPointWorld = vec2 . create ( ) ;
var intersectHeightfield _worldNormal = vec2 . create ( ) ;
var intersectHeightfield _l0 = vec2 . create ( ) ;
var intersectHeightfield _l1 = vec2 . create ( ) ;
var intersectHeightfield _localFrom = vec2 . create ( ) ;
var intersectHeightfield _localTo = vec2 . create ( ) ;
var intersectHeightfield _unit _y = vec2 . fromValues ( 0 , 1 ) ;
// Returns 1 if the lines intersect, otherwise 0.
function getLineSegmentsIntersection ( out , p0 , p1 , p2 , p3 ) {
var s1 _x , s1 _y , s2 _x , s2 _y ;
s1 _x = p1 [ 0 ] - p0 [ 0 ] ;
s1 _y = p1 [ 1 ] - p0 [ 1 ] ;
s2 _x = p3 [ 0 ] - p2 [ 0 ] ;
s2 _y = p3 [ 1 ] - p2 [ 1 ] ;
var s , t ;
s = ( - s1 _y * ( p0 [ 0 ] - p2 [ 0 ] ) + s1 _x * ( p0 [ 1 ] - p2 [ 1 ] ) ) / ( - s2 _x * s1 _y + s1 _x * s2 _y ) ;
t = ( s2 _x * ( p0 [ 1 ] - p2 [ 1 ] ) - s2 _y * ( p0 [ 0 ] - p2 [ 0 ] ) ) / ( - s2 _x * s1 _y + s1 _x * s2 _y ) ;
if ( s >= 0 && s <= 1 && t >= 0 && t <= 1 ) { // Collision detected
var intX = p0 [ 0 ] + ( t * s1 _x ) ;
var intY = p0 [ 1 ] + ( t * s1 _y ) ;
out [ 0 ] = intX ;
out [ 1 ] = intY ;
return t ;
}
return - 1 ; // No collision
}
/ * *
* @ method raycast
* @ param { RayResult } result
* @ param { Ray } ray
* @ param { array } position
* @ param { number } angle
* /
Heightfield . prototype . raycast = function ( result , ray , position , angle ) {
var from = ray . from ;
var to = ray . to ;
var direction = ray . direction ;
var hitPointWorld = intersectHeightfield _hitPointWorld ;
var worldNormal = intersectHeightfield _worldNormal ;
var l0 = intersectHeightfield _l0 ;
var l1 = intersectHeightfield _l1 ;
var localFrom = intersectHeightfield _localFrom ;
var localTo = intersectHeightfield _localTo ;
// get local ray start and end
vec2 . toLocalFrame ( localFrom , from , position , angle ) ;
vec2 . toLocalFrame ( localTo , to , position , angle ) ;
// Get the segment range
var i0 = this . getClampedSegmentIndex ( localFrom ) ;
var i1 = this . getClampedSegmentIndex ( localTo ) ;
if ( i0 > i1 ) {
var tmp = i0 ;
i0 = i1 ;
i1 = tmp ;
}
// The segments
for ( var i = 0 ; i < this . heights . length - 1 ; i ++ ) {
this . getLineSegment ( l0 , l1 , i ) ;
var t = vec2 . getLineSegmentsIntersectionFraction ( localFrom , localTo , l0 , l1 ) ;
if ( t >= 0 ) {
vec2 . sub ( worldNormal , l1 , l0 ) ;
vec2 . rotate ( worldNormal , worldNormal , angle + Math . PI / 2 ) ;
vec2 . normalize ( worldNormal , worldNormal ) ;
ray . reportIntersection ( result , t , worldNormal , - 1 ) ;
if ( result . shouldStop ( ray ) ) {
return ;
}
}
}
} ;
} , { "../math/vec2" : 30 , "../utils/Utils" : 57 , "./Shape" : 45 } ] , 42 : [ function ( _dereq _ , module , exports ) {
var Shape = _dereq _ ( './Shape' )
, vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = Line ;
/ * *
* Line shape class . The line shape is along the x direction , and stretches from [ - length / 2 , 0 ] to [ length / 2 , 0 ] .
* @ class Line
* @ param { object } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* @ param { Number } [ options . length = 1 ] The total length of the line
* @ extends Shape
* @ constructor
* /
function Line ( options ) {
if ( typeof ( arguments [ 0 ] ) === 'number' ) {
options = {
length : arguments [ 0 ]
} ;
console . warn ( 'The Line constructor signature has changed. Please use the following format: new Line({ length: 1, ... })' ) ;
}
options = options || { } ;
/ * *
* Length of this line
* @ property { Number } length
* @ default 1
* /
this . length = options . length || 1 ;
options . type = Shape . LINE ;
Shape . call ( this , options ) ;
}
Line . prototype = new Shape ( ) ;
Line . prototype . constructor = Line ;
Line . prototype . computeMomentOfInertia = function ( mass ) {
return mass * Math . pow ( this . length , 2 ) / 12 ;
} ;
Line . prototype . updateBoundingRadius = function ( ) {
this . boundingRadius = this . length / 2 ;
} ;
var points = [ vec2 . create ( ) , vec2 . create ( ) ] ;
/ * *
* @ method computeAABB
* @ param { AABB } out The resulting AABB .
* @ param { Array } position
* @ param { Number } angle
* /
Line . prototype . computeAABB = function ( out , position , angle ) {
var l2 = this . length / 2 ;
vec2 . set ( points [ 0 ] , - l2 , 0 ) ;
vec2 . set ( points [ 1 ] , l2 , 0 ) ;
out . setFromPoints ( points , position , angle , 0 ) ;
} ;
var raycast _hitPoint = vec2 . create ( ) ;
var raycast _normal = vec2 . create ( ) ;
var raycast _l0 = vec2 . create ( ) ;
var raycast _l1 = vec2 . create ( ) ;
var raycast _unit _y = vec2 . fromValues ( 0 , 1 ) ;
/ * *
* @ method raycast
* @ param { RaycastResult } result
* @ param { Ray } ray
* @ param { number } angle
* @ param { array } position
* /
Line . prototype . raycast = function ( result , ray , position , angle ) {
var from = ray . from ;
var to = ray . to ;
var l0 = raycast _l0 ;
var l1 = raycast _l1 ;
// get start and end of the line
var halfLen = this . length / 2 ;
vec2 . set ( l0 , - halfLen , 0 ) ;
vec2 . set ( l1 , halfLen , 0 ) ;
vec2 . toGlobalFrame ( l0 , l0 , position , angle ) ;
vec2 . toGlobalFrame ( l1 , l1 , position , angle ) ;
var fraction = vec2 . getLineSegmentsIntersectionFraction ( l0 , l1 , from , to ) ;
if ( fraction >= 0 ) {
var normal = raycast _normal ;
vec2 . rotate ( normal , raycast _unit _y , angle ) ; // todo: this should depend on which side the ray comes from
ray . reportIntersection ( result , fraction , normal , - 1 ) ;
}
} ;
} , { "../math/vec2" : 30 , "./Shape" : 45 } ] , 43 : [ function ( _dereq _ , module , exports ) {
var Shape = _dereq _ ( './Shape' )
, vec2 = _dereq _ ( '../math/vec2' ) ;
module . exports = Particle ;
/ * *
* Particle shape class .
* @ class Particle
* @ constructor
* @ param { object } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* @ extends Shape
* /
function Particle ( options ) {
options = options || { } ;
options . type = Shape . PARTICLE ;
Shape . call ( this , options ) ;
}
Particle . prototype = new Shape ( ) ;
Particle . prototype . constructor = Particle ;
Particle . prototype . computeMomentOfInertia = function ( mass ) {
return 0 ; // Can't rotate a particle
} ;
Particle . prototype . updateBoundingRadius = function ( ) {
this . boundingRadius = 0 ;
} ;
/ * *
* @ method computeAABB
* @ param { AABB } out
* @ param { Array } position
* @ param { Number } angle
* /
Particle . prototype . computeAABB = function ( out , position , angle ) {
vec2 . copy ( out . lowerBound , position ) ;
vec2 . copy ( out . upperBound , position ) ;
} ;
} , { "../math/vec2" : 30 , "./Shape" : 45 } ] , 44 : [ function ( _dereq _ , module , exports ) {
var Shape = _dereq _ ( './Shape' )
, vec2 = _dereq _ ( '../math/vec2' )
, Utils = _dereq _ ( '../utils/Utils' ) ;
module . exports = Plane ;
/ * *
* Plane shape class . The plane is facing in the Y direction .
* @ class Plane
* @ extends Shape
* @ constructor
* @ param { object } [ options ] ( Note that this options object will be passed on to the { { # crossLink "Shape" } } { { / c r o s s L i n k } } c o n s t r u c t o r . )
* /
function Plane ( options ) {
options = options || { } ;
options . type = Shape . PLANE ;
Shape . call ( this , options ) ;
}
Plane . prototype = new Shape ( ) ;
Plane . prototype . constructor = Plane ;
/ * *
* Compute moment of inertia
* @ method computeMomentOfInertia
* /
Plane . prototype . computeMomentOfInertia = function ( mass ) {
return 0 ; // Plane is infinite. The inertia should therefore be infinty but by convention we set 0 here
} ;
/ * *
* Update the bounding radius
* @ method updateBoundingRadius
* /
Plane . prototype . updateBoundingRadius = function ( ) {
this . boundingRadius = Number . MAX _VALUE ;
} ;
/ * *
* @ method computeAABB
* @ param { AABB } out
* @ param { Array } position
* @ param { Number } angle
* /
Plane . prototype . computeAABB = function ( out , position , angle ) {
var a = angle % ( 2 * Math . PI ) ;
var set = vec2 . set ;
2016-09-21 22:22:08 +00:00
var max = 1e7 ;
2016-02-12 16:00:36 +00:00
var lowerBound = out . lowerBound ;
var upperBound = out . upperBound ;
2016-09-21 22:22:08 +00:00
// Set max bounds
set ( lowerBound , - max , - max ) ;
set ( upperBound , max , max ) ;
2016-02-12 16:00:36 +00:00
if ( a === 0 ) {
// y goes from -inf to 0
2016-09-21 22:22:08 +00:00
upperBound [ 1 ] = 0 ;
// set(lowerBound, -max, -max);
// set(upperBound, max, 0);
2016-02-12 16:00:36 +00:00
} else if ( a === Math . PI / 2 ) {
// x goes from 0 to inf
2016-09-21 22:22:08 +00:00
lowerBound [ 0 ] = 0 ;
// set(lowerBound, 0, -max);
// set(upperBound, max, max);
2016-02-12 16:00:36 +00:00
} else if ( a === Math . PI ) {
// y goes from 0 to inf
2016-09-21 22:22:08 +00:00
lowerBound [ 1 ] = 0 ;
// set(lowerBound, -max, 0);
// set(upperBound, max, max);
2016-02-12 16:00:36 +00:00
} else if ( a === 3 * Math . PI / 2 ) {
// x goes from -inf to 0
2016-09-21 22:22:08 +00:00
upperBound [ 0 ] = 0 ;
// set(lowerBound, -max, -max);
// set(upperBound, 0, max);
2016-02-12 16:00:36 +00:00
}
} ;
Plane . prototype . updateArea = function ( ) {
this . area = Number . MAX _VALUE ;
} ;
var intersectPlane _planePointToFrom = vec2 . create ( ) ;
var intersectPlane _dir _scaled _with _t = vec2 . create ( ) ;
var intersectPlane _hitPoint = vec2 . create ( ) ;
var intersectPlane _normal = vec2 . create ( ) ;
var intersectPlane _len = vec2 . create ( ) ;
/ * *
* @ method raycast
* @ param { RayResult } result
* @ param { Ray } ray
* @ param { array } position
* @ param { number } angle
* /
Plane . prototype . raycast = function ( result , ray , position , angle ) {
var from = ray . from ;
var to = ray . to ;
var direction = ray . direction ;
var planePointToFrom = intersectPlane _planePointToFrom ;
var dir _scaled _with _t = intersectPlane _dir _scaled _with _t ;
var hitPoint = intersectPlane _hitPoint ;
var normal = intersectPlane _normal ;
var len = intersectPlane _len ;
// Get plane normal
vec2 . set ( normal , 0 , 1 ) ;
vec2 . rotate ( normal , normal , angle ) ;
vec2 . sub ( len , from , position ) ;
var planeToFrom = vec2 . dot ( len , normal ) ;
vec2 . sub ( len , to , position ) ;
var planeToTo = vec2 . dot ( len , normal ) ;
if ( planeToFrom * planeToTo > 0 ) {
// "from" and "to" are on the same side of the plane... bail out
return ;
}
if ( vec2 . squaredDistance ( from , to ) < planeToFrom * planeToFrom ) {
return ;
}
var n _dot _dir = vec2 . dot ( normal , direction ) ;
vec2 . sub ( planePointToFrom , from , position ) ;
var t = - vec2 . dot ( normal , planePointToFrom ) / n _dot _dir / ray . length ;
ray . reportIntersection ( result , t , normal , - 1 ) ;
} ;
} , { "../math/vec2" : 30 , "../utils/Utils" : 57 , "./Shape" : 45 } ] , 45 : [ function ( _dereq _ , module , exports ) {
module . exports = Shape ;
var vec2 = _dereq _ ( '../math/vec2' ) ;
/ * *
* Base class for shapes .
* @ class Shape
* @ constructor
* @ param { object } [ options ]
* @ param { array } [ options . position ]
* @ param { number } [ options . angle = 0 ]
* @ param { number } [ options . collisionGroup = 1 ]
* @ param { number } [ options . collisionMask = 1 ]
* @ param { boolean } [ options . sensor = false ]
* @ param { boolean } [ options . collisionResponse = true ]
* @ param { object } [ options . type = 0 ]
* /
function Shape ( options ) {
options = options || { } ;
/ * *
* The body this shape is attached to . A shape can only be attached to a single body .
* @ property { Body } body
* /
this . body = null ;
/ * *
* Body - local position of the shape .
* @ property { Array } position
* /
this . position = vec2 . fromValues ( 0 , 0 ) ;
if ( options . position ) {
vec2 . copy ( this . position , options . position ) ;
}
/ * *
* Body - local angle of the shape .
* @ property { number } angle
* /
this . angle = options . angle || 0 ;
/ * *
* The type of the shape . One of :
*
* * { { # crossLink "Shape/CIRCLE:property" } } Shape . CIRCLE { { / c r o s s L i n k } }
* * { { # crossLink "Shape/PARTICLE:property" } } Shape . PARTICLE { { / c r o s s L i n k } }
* * { { # crossLink "Shape/PLANE:property" } } Shape . PLANE { { / c r o s s L i n k } }
* * { { # crossLink "Shape/CONVEX:property" } } Shape . CONVEX { { / c r o s s L i n k } }
* * { { # crossLink "Shape/LINE:property" } } Shape . LINE { { / c r o s s L i n k } }
* * { { # crossLink "Shape/BOX:property" } } Shape . BOX { { / c r o s s L i n k } }
* * { { # crossLink "Shape/CAPSULE:property" } } Shape . CAPSULE { { / c r o s s L i n k } }
* * { { # crossLink "Shape/HEIGHTFIELD:property" } } Shape . HEIGHTFIELD { { / c r o s s L i n k } }
*
* @ property { number } type
* /
this . type = options . type || 0 ;
/ * *
* Shape object identifier .
* @ type { Number }
* @ property id
* /
this . id = Shape . idCounter ++ ;
/ * *
* Bounding circle radius of this shape
* @ property boundingRadius
* @ type { Number }
* /
this . boundingRadius = 0 ;
/ * *
* Collision group that this shape belongs to ( bit mask ) . See < a href = "http://www.aurelienribon.com/blog/2011/07/box2d-tutorial-collision-filtering/" > this tutorial < / a > .
* @ property collisionGroup
* @ type { Number }
* @ example
* // Setup bits for each available group
* var PLAYER = Math . pow ( 2 , 0 ) ,
* ENEMY = Math . pow ( 2 , 1 ) ,
* GROUND = Math . pow ( 2 , 2 )
*
* // Put shapes into their groups
* player1Shape . collisionGroup = PLAYER ;
* player2Shape . collisionGroup = PLAYER ;
* enemyShape . collisionGroup = ENEMY ;
* groundShape . collisionGroup = GROUND ;
*
* // Assign groups that each shape collide with.
* // Note that the players can collide with ground and enemies, but not with other players.
* player1Shape . collisionMask = ENEMY | GROUND ;
* player2Shape . collisionMask = ENEMY | GROUND ;
* enemyShape . collisionMask = PLAYER | GROUND ;
* groundShape . collisionMask = PLAYER | ENEMY ;
*
* @ example
* // How collision check is done
* if ( shapeA . collisionGroup & shapeB . collisionMask ) != 0 && ( shapeB . collisionGroup & shapeA . collisionMask ) != 0 ) {
* // The shapes will collide
* }
* /
this . collisionGroup = options . collisionGroup !== undefined ? options . collisionGroup : 1 ;
/ * *
* Whether to produce contact forces when in contact with other bodies . Note that contacts will be generated , but they will be disabled . That means that this shape will move through other body shapes , but it will still trigger contact events , etc .
* @ property { Boolean } collisionResponse
* /
this . collisionResponse = options . collisionResponse !== undefined ? options . collisionResponse : true ;
/ * *
* Collision mask of this shape . See . collisionGroup .
* @ property collisionMask
* @ type { Number }
* /
this . collisionMask = options . collisionMask !== undefined ? options . collisionMask : 1 ;
/ * *
* Material to use in collisions for this Shape . If this is set to null , the world will use default material properties instead .
* @ property material
* @ type { Material }
* /
this . material = options . material || null ;
/ * *
* Area of this shape .
* @ property area
* @ type { Number }
* /
this . area = 0 ;
/ * *
* Set to true if you want this shape to be a sensor . A sensor does not generate contacts , but it still reports contact events . This is good if you want to know if a shape is overlapping another shape , without them generating contacts .
* @ property { Boolean } sensor
* /
this . sensor = options . sensor !== undefined ? options . sensor : false ;
if ( this . type ) {
this . updateBoundingRadius ( ) ;
}
this . updateArea ( ) ;
}
Shape . idCounter = 0 ;
/ * *
* @ static
* @ property { Number } CIRCLE
* /
Shape . CIRCLE = 1 ;
/ * *
* @ static
* @ property { Number } PARTICLE
* /
Shape . PARTICLE = 2 ;
/ * *
* @ static
* @ property { Number } PLANE
* /
Shape . PLANE = 4 ;
/ * *
* @ static
* @ property { Number } CONVEX
* /
Shape . CONVEX = 8 ;
/ * *
* @ static
* @ property { Number } LINE
* /
Shape . LINE = 16 ;
/ * *
* @ static
* @ property { Number } BOX
* /
Shape . BOX = 32 ;
Object . defineProperty ( Shape , 'RECTANGLE' , {
get : function ( ) {
console . warn ( 'Shape.RECTANGLE is deprecated, use Shape.BOX instead.' ) ;
return Shape . BOX ;
}
} ) ;
/ * *
* @ static
* @ property { Number } CAPSULE
* /
Shape . CAPSULE = 64 ;
/ * *
* @ static
* @ property { Number } HEIGHTFIELD
* /
Shape . HEIGHTFIELD = 128 ;
/ * *
* Should return the moment of inertia around the Z axis of the body given the total mass . See < a href = "http://en.wikipedia.org/wiki/List_of_moments_of_inertia" > Wikipedia ' s list of moments of inertia < / a > .
* @ method computeMomentOfInertia
* @ param { Number } mass
* @ return { Number } If the inertia is infinity or if the object simply isn ' t possible to rotate , return 0.
* /
Shape . prototype . computeMomentOfInertia = function ( mass ) { } ;
/ * *
* Returns the bounding circle radius of this shape .
* @ method updateBoundingRadius
* @ return { Number }
* /
Shape . prototype . updateBoundingRadius = function ( ) { } ;
/ * *
* Update the . area property of the shape .
* @ method updateArea
* /
Shape . prototype . updateArea = function ( ) {
// To be implemented in all subclasses
} ;
/ * *
* Compute the world axis - aligned bounding box ( AABB ) of this shape .
* @ method computeAABB
* @ param { AABB } out The resulting AABB .
* @ param { Array } position World position of the shape .
* @ param { Number } angle World angle of the shape .
* /
Shape . prototype . computeAABB = function ( out , position , angle ) {
// To be implemented in each subclass
} ;
/ * *
* Perform raycasting on this shape .
* @ method raycast
* @ param { RayResult } result Where to store the resulting data .
* @ param { Ray } ray The Ray that you want to use for raycasting .
* @ param { array } position World position of the shape ( the . position property will be ignored ) .
* @ param { number } angle World angle of the shape ( the . angle property will be ignored ) .
* /
Shape . prototype . raycast = function ( result , ray , position , angle ) {
// To be implemented in each subclass
} ;
} , { "../math/vec2" : 30 } ] , 46 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' )
, Solver = _dereq _ ( './Solver' )
, Utils = _dereq _ ( '../utils/Utils' )
, FrictionEquation = _dereq _ ( '../equations/FrictionEquation' ) ;
module . exports = GSSolver ;
/ * *
* Iterative Gauss - Seidel constraint equation solver .
*
* @ class GSSolver
* @ constructor
* @ extends Solver
* @ param { Object } [ options ]
* @ param { Number } [ options . iterations = 10 ]
* @ param { Number } [ options . tolerance = 0 ]
* /
function GSSolver ( options ) {
Solver . call ( this , options , Solver . GS ) ;
options = options || { } ;
/ * *
* The max number of iterations to do when solving . More gives better results , but is more expensive .
* @ property iterations
* @ type { Number }
* /
this . iterations = options . iterations || 10 ;
/ * *
* The error tolerance , per constraint . If the total error is below this limit , the solver will stop iterating . Set to zero for as good solution as possible , but to something larger than zero to make computations faster .
* @ property tolerance
* @ type { Number }
* @ default 1e-7
* /
this . tolerance = options . tolerance || 1e-7 ;
this . arrayStep = 30 ;
this . lambda = new Utils . ARRAY _TYPE ( this . arrayStep ) ;
this . Bs = new Utils . ARRAY _TYPE ( this . arrayStep ) ;
this . invCs = new Utils . ARRAY _TYPE ( this . arrayStep ) ;
/ * *
* Set to true to set all right hand side terms to zero when solving . Can be handy for a few applications .
* @ property useZeroRHS
* @ type { Boolean }
2016-09-21 22:22:08 +00:00
* @ todo Remove , not used
2016-02-12 16:00:36 +00:00
* /
this . useZeroRHS = false ;
/ * *
2016-09-21 22:22:08 +00:00
* Number of solver iterations that are used to approximate normal forces used for friction ( F _friction = mu * F _normal ) . These friction forces will override any other friction forces that are set . If you set frictionIterations = 0 , then this feature will be disabled .
*
* Use only frictionIterations > 0 if the approximated normal force ( F _normal = mass * gravity ) is not good enough . Examples of where it can happen is in space games where gravity is zero , or in tall stacks where the normal force is large at bottom but small at top .
*
2016-02-12 16:00:36 +00:00
* @ property frictionIterations
* @ type { Number }
2016-09-21 22:22:08 +00:00
* @ default 0
2016-02-12 16:00:36 +00:00
* /
2016-09-21 22:22:08 +00:00
this . frictionIterations = options . frictionIterations !== undefined ? 0 : options . frictionIterations ;
2016-02-12 16:00:36 +00:00
/ * *
* The number of iterations that were made during the last solve . If . tolerance is zero , this value will always be equal to . iterations , but if . tolerance is larger than zero , and the solver can quit early , then this number will be somewhere between 1 and . iterations .
* @ property { Number } usedIterations
* /
this . usedIterations = 0 ;
}
GSSolver . prototype = new Solver ( ) ;
GSSolver . prototype . constructor = GSSolver ;
function setArrayZero ( array ) {
var l = array . length ;
while ( l -- ) {
array [ l ] = + 0.0 ;
}
}
/ * *
* Solve the system of equations
* @ method solve
* @ param { Number } h Time step
* @ param { World } world World to solve
* /
GSSolver . prototype . solve = function ( h , world ) {
this . sortEquations ( ) ;
var iter = 0 ,
maxIter = this . iterations ,
maxFrictionIter = this . frictionIterations ,
equations = this . equations ,
Neq = equations . length ,
tolSquared = Math . pow ( this . tolerance * Neq , 2 ) ,
bodies = world . bodies ,
Nbodies = world . bodies . length ,
add = vec2 . add ,
set = vec2 . set ,
useZeroRHS = this . useZeroRHS ,
lambda = this . lambda ;
this . usedIterations = 0 ;
if ( Neq ) {
for ( var i = 0 ; i !== Nbodies ; i ++ ) {
var b = bodies [ i ] ;
// Update solve mass
b . updateSolveMassProperties ( ) ;
}
}
// Things that does not change during iteration can be computed once
if ( lambda . length < Neq ) {
lambda = this . lambda = new Utils . ARRAY _TYPE ( Neq + this . arrayStep ) ;
this . Bs = new Utils . ARRAY _TYPE ( Neq + this . arrayStep ) ;
this . invCs = new Utils . ARRAY _TYPE ( Neq + this . arrayStep ) ;
}
setArrayZero ( lambda ) ;
var invCs = this . invCs ,
Bs = this . Bs ,
lambda = this . lambda ;
for ( var i = 0 ; i !== equations . length ; i ++ ) {
var c = equations [ i ] ;
if ( c . timeStep !== h || c . needsUpdate ) {
c . timeStep = h ;
c . update ( ) ;
}
Bs [ i ] = c . computeB ( c . a , c . b , h ) ;
invCs [ i ] = c . computeInvC ( c . epsilon ) ;
}
var q , B , c , deltalambdaTot , i , j ;
if ( Neq !== 0 ) {
for ( i = 0 ; i !== Nbodies ; i ++ ) {
var b = bodies [ i ] ;
// Reset vlambda
b . resetConstraintVelocity ( ) ;
}
if ( maxFrictionIter ) {
// Iterate over contact equations to get normal forces
for ( iter = 0 ; iter !== maxFrictionIter ; iter ++ ) {
// Accumulate the total error for each iteration.
deltalambdaTot = 0.0 ;
for ( j = 0 ; j !== Neq ; j ++ ) {
c = equations [ j ] ;
var deltalambda = GSSolver . iterateEquation ( j , c , c . epsilon , Bs , invCs , lambda , useZeroRHS , h , iter ) ;
deltalambdaTot += Math . abs ( deltalambda ) ;
}
this . usedIterations ++ ;
// If the total error is small enough - stop iterate
if ( deltalambdaTot * deltalambdaTot <= tolSquared ) {
break ;
}
}
GSSolver . updateMultipliers ( equations , lambda , 1 / h ) ;
// Set computed friction force
for ( j = 0 ; j !== Neq ; j ++ ) {
var eq = equations [ j ] ;
if ( eq instanceof FrictionEquation ) {
var f = 0.0 ;
for ( var k = 0 ; k !== eq . contactEquations . length ; k ++ ) {
f += eq . contactEquations [ k ] . multiplier ;
}
f *= eq . frictionCoefficient / eq . contactEquations . length ;
eq . maxForce = f ;
eq . minForce = - f ;
}
}
}
// Iterate over all equations
for ( iter = 0 ; iter !== maxIter ; iter ++ ) {
// Accumulate the total error for each iteration.
deltalambdaTot = 0.0 ;
for ( j = 0 ; j !== Neq ; j ++ ) {
c = equations [ j ] ;
var deltalambda = GSSolver . iterateEquation ( j , c , c . epsilon , Bs , invCs , lambda , useZeroRHS , h , iter ) ;
deltalambdaTot += Math . abs ( deltalambda ) ;
}
this . usedIterations ++ ;
// If the total error is small enough - stop iterate
if ( deltalambdaTot * deltalambdaTot <= tolSquared ) {
break ;
}
}
// Add result to velocity
for ( i = 0 ; i !== Nbodies ; i ++ ) {
bodies [ i ] . addConstraintVelocity ( ) ;
}
GSSolver . updateMultipliers ( equations , lambda , 1 / h ) ;
}
} ;
// Sets the .multiplier property of each equation
GSSolver . updateMultipliers = function ( equations , lambda , invDt ) {
// Set the .multiplier property of each equation
var l = equations . length ;
while ( l -- ) {
equations [ l ] . multiplier = lambda [ l ] * invDt ;
}
} ;
GSSolver . iterateEquation = function ( j , eq , eps , Bs , invCs , lambda , useZeroRHS , dt , iter ) {
// Compute iteration
var B = Bs [ j ] ,
invC = invCs [ j ] ,
lambdaj = lambda [ j ] ,
GWlambda = eq . computeGWlambda ( ) ;
var maxForce = eq . maxForce ,
minForce = eq . minForce ;
if ( useZeroRHS ) {
B = 0 ;
}
var deltalambda = invC * ( B - GWlambda - eps * lambdaj ) ;
// Clamp if we are not within the min/max interval
var lambdaj _plus _deltalambda = lambdaj + deltalambda ;
if ( lambdaj _plus _deltalambda < minForce * dt ) {
deltalambda = minForce * dt - lambdaj ;
} else if ( lambdaj _plus _deltalambda > maxForce * dt ) {
deltalambda = maxForce * dt - lambdaj ;
}
lambda [ j ] += deltalambda ;
eq . addToWlambda ( deltalambda ) ;
return deltalambda ;
} ;
} , { "../equations/FrictionEquation" : 23 , "../math/vec2" : 30 , "../utils/Utils" : 57 , "./Solver" : 47 } ] , 47 : [ function ( _dereq _ , module , exports ) {
var Utils = _dereq _ ( '../utils/Utils' )
, EventEmitter = _dereq _ ( '../events/EventEmitter' ) ;
module . exports = Solver ;
/ * *
* Base class for constraint solvers .
* @ class Solver
* @ constructor
* @ extends EventEmitter
* /
function Solver ( options , type ) {
options = options || { } ;
EventEmitter . call ( this ) ;
this . type = type ;
/ * *
* Current equations in the solver .
*
* @ property equations
* @ type { Array }
* /
this . equations = [ ] ;
/ * *
* Function that is used to sort all equations before each solve .
* @ property equationSortFunction
* @ type { function | boolean }
* /
this . equationSortFunction = options . equationSortFunction || false ;
}
Solver . prototype = new EventEmitter ( ) ;
Solver . prototype . constructor = Solver ;
/ * *
* Method to be implemented in each subclass
* @ method solve
* @ param { Number } dt
* @ param { World } world
* /
Solver . prototype . solve = function ( dt , world ) {
throw new Error ( "Solver.solve should be implemented by subclasses!" ) ;
} ;
var mockWorld = { bodies : [ ] } ;
/ * *
* Solves all constraints in an island .
* @ method solveIsland
* @ param { Number } dt
* @ param { Island } island
* /
Solver . prototype . solveIsland = function ( dt , island ) {
this . removeAllEquations ( ) ;
if ( island . equations . length ) {
// Add equations to solver
this . addEquations ( island . equations ) ;
mockWorld . bodies . length = 0 ;
island . getBodies ( mockWorld . bodies ) ;
// Solve
if ( mockWorld . bodies . length ) {
this . solve ( dt , mockWorld ) ;
}
}
} ;
/ * *
* Sort all equations using the . equationSortFunction . Should be called by subclasses before solving .
* @ method sortEquations
* /
Solver . prototype . sortEquations = function ( ) {
if ( this . equationSortFunction ) {
this . equations . sort ( this . equationSortFunction ) ;
}
} ;
/ * *
* Add an equation to be solved .
*
* @ method addEquation
* @ param { Equation } eq
* /
Solver . prototype . addEquation = function ( eq ) {
if ( eq . enabled ) {
this . equations . push ( eq ) ;
}
} ;
/ * *
* Add equations . Same as . addEquation , but this time the argument is an array of Equations
*
* @ method addEquations
* @ param { Array } eqs
* /
Solver . prototype . addEquations = function ( eqs ) {
//Utils.appendArray(this.equations,eqs);
for ( var i = 0 , N = eqs . length ; i !== N ; i ++ ) {
var eq = eqs [ i ] ;
if ( eq . enabled ) {
this . equations . push ( eq ) ;
}
}
} ;
/ * *
* Remove an equation .
*
* @ method removeEquation
* @ param { Equation } eq
* /
Solver . prototype . removeEquation = function ( eq ) {
var i = this . equations . indexOf ( eq ) ;
if ( i !== - 1 ) {
this . equations . splice ( i , 1 ) ;
}
} ;
/ * *
* Remove all currently added equations .
*
* @ method removeAllEquations
* /
Solver . prototype . removeAllEquations = function ( ) {
this . equations . length = 0 ;
} ;
Solver . GS = 1 ;
Solver . ISLAND = 2 ;
} , { "../events/EventEmitter" : 26 , "../utils/Utils" : 57 } ] , 48 : [ function ( _dereq _ , module , exports ) {
var ContactEquation = _dereq _ ( '../equations/ContactEquation' ) ;
var Pool = _dereq _ ( './Pool' ) ;
module . exports = ContactEquationPool ;
/ * *
* @ class
* /
function ContactEquationPool ( ) {
Pool . apply ( this , arguments ) ;
}
ContactEquationPool . prototype = new Pool ( ) ;
ContactEquationPool . prototype . constructor = ContactEquationPool ;
/ * *
* @ method create
* @ return { ContactEquation }
* /
ContactEquationPool . prototype . create = function ( ) {
return new ContactEquation ( ) ;
} ;
/ * *
* @ method destroy
* @ param { ContactEquation } equation
* @ return { ContactEquationPool }
* /
ContactEquationPool . prototype . destroy = function ( equation ) {
equation . bodyA = equation . bodyB = null ;
return this ;
} ;
} , { "../equations/ContactEquation" : 21 , "./Pool" : 55 } ] , 49 : [ function ( _dereq _ , module , exports ) {
var FrictionEquation = _dereq _ ( '../equations/FrictionEquation' ) ;
var Pool = _dereq _ ( './Pool' ) ;
module . exports = FrictionEquationPool ;
/ * *
* @ class
* /
function FrictionEquationPool ( ) {
Pool . apply ( this , arguments ) ;
}
FrictionEquationPool . prototype = new Pool ( ) ;
FrictionEquationPool . prototype . constructor = FrictionEquationPool ;
/ * *
* @ method create
* @ return { FrictionEquation }
* /
FrictionEquationPool . prototype . create = function ( ) {
return new FrictionEquation ( ) ;
} ;
/ * *
* @ method destroy
* @ param { FrictionEquation } equation
* @ return { FrictionEquationPool }
* /
FrictionEquationPool . prototype . destroy = function ( equation ) {
equation . bodyA = equation . bodyB = null ;
return this ;
} ;
} , { "../equations/FrictionEquation" : 23 , "./Pool" : 55 } ] , 50 : [ function ( _dereq _ , module , exports ) {
var IslandNode = _dereq _ ( '../world/IslandNode' ) ;
var Pool = _dereq _ ( './Pool' ) ;
module . exports = IslandNodePool ;
/ * *
* @ class
* /
function IslandNodePool ( ) {
Pool . apply ( this , arguments ) ;
}
IslandNodePool . prototype = new Pool ( ) ;
IslandNodePool . prototype . constructor = IslandNodePool ;
/ * *
* @ method create
* @ return { IslandNode }
* /
IslandNodePool . prototype . create = function ( ) {
return new IslandNode ( ) ;
} ;
/ * *
* @ method destroy
* @ param { IslandNode } node
* @ return { IslandNodePool }
* /
IslandNodePool . prototype . destroy = function ( node ) {
node . reset ( ) ;
return this ;
} ;
} , { "../world/IslandNode" : 60 , "./Pool" : 55 } ] , 51 : [ function ( _dereq _ , module , exports ) {
var Island = _dereq _ ( '../world/Island' ) ;
var Pool = _dereq _ ( './Pool' ) ;
module . exports = IslandPool ;
/ * *
* @ class
* /
function IslandPool ( ) {
Pool . apply ( this , arguments ) ;
}
IslandPool . prototype = new Pool ( ) ;
IslandPool . prototype . constructor = IslandPool ;
/ * *
* @ method create
* @ return { Island }
* /
IslandPool . prototype . create = function ( ) {
return new Island ( ) ;
} ;
/ * *
* @ method destroy
* @ param { Island } island
* @ return { IslandPool }
* /
IslandPool . prototype . destroy = function ( island ) {
island . reset ( ) ;
return this ;
} ;
} , { "../world/Island" : 58 , "./Pool" : 55 } ] , 52 : [ function ( _dereq _ , module , exports ) {
var TupleDictionary = _dereq _ ( './TupleDictionary' ) ;
var OverlapKeeperRecord = _dereq _ ( './OverlapKeeperRecord' ) ;
var OverlapKeeperRecordPool = _dereq _ ( './OverlapKeeperRecordPool' ) ;
var Utils = _dereq _ ( './Utils' ) ;
module . exports = OverlapKeeper ;
/ * *
* Keeps track of overlaps in the current state and the last step state .
* @ class OverlapKeeper
* @ constructor
* /
function OverlapKeeper ( ) {
this . overlappingShapesLastState = new TupleDictionary ( ) ;
this . overlappingShapesCurrentState = new TupleDictionary ( ) ;
this . recordPool = new OverlapKeeperRecordPool ( { size : 16 } ) ;
this . tmpDict = new TupleDictionary ( ) ;
this . tmpArray1 = [ ] ;
}
/ * *
* Ticks one step forward in time . This will move the current overlap state to the "old" overlap state , and create a new one as current .
* @ method tick
* /
OverlapKeeper . prototype . tick = function ( ) {
var last = this . overlappingShapesLastState ;
var current = this . overlappingShapesCurrentState ;
// Save old objects into pool
var l = last . keys . length ;
while ( l -- ) {
var key = last . keys [ l ] ;
var lastObject = last . getByKey ( key ) ;
var currentObject = current . getByKey ( key ) ;
if ( lastObject ) {
// The record is only used in the "last" dict, and will be removed. We might as well pool it.
this . recordPool . release ( lastObject ) ;
}
}
// Clear last object
last . reset ( ) ;
// Transfer from new object to old
last . copy ( current ) ;
// Clear current object
current . reset ( ) ;
} ;
/ * *
* @ method setOverlapping
* @ param { Body } bodyA
* @ param { Body } shapeA
* @ param { Body } bodyB
* @ param { Body } shapeB
* /
OverlapKeeper . prototype . setOverlapping = function ( bodyA , shapeA , bodyB , shapeB ) {
var last = this . overlappingShapesLastState ;
var current = this . overlappingShapesCurrentState ;
// Store current contact state
if ( ! current . get ( shapeA . id , shapeB . id ) ) {
var data = this . recordPool . get ( ) ;
data . set ( bodyA , shapeA , bodyB , shapeB ) ;
current . set ( shapeA . id , shapeB . id , data ) ;
}
} ;
OverlapKeeper . prototype . getNewOverlaps = function ( result ) {
return this . getDiff ( this . overlappingShapesLastState , this . overlappingShapesCurrentState , result ) ;
} ;
OverlapKeeper . prototype . getEndOverlaps = function ( result ) {
return this . getDiff ( this . overlappingShapesCurrentState , this . overlappingShapesLastState , result ) ;
} ;
/ * *
* Checks if two bodies are currently overlapping .
* @ method bodiesAreOverlapping
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ return { boolean }
* /
OverlapKeeper . prototype . bodiesAreOverlapping = function ( bodyA , bodyB ) {
var current = this . overlappingShapesCurrentState ;
var l = current . keys . length ;
while ( l -- ) {
var key = current . keys [ l ] ;
var data = current . data [ key ] ;
if ( ( data . bodyA === bodyA && data . bodyB === bodyB ) || data . bodyA === bodyB && data . bodyB === bodyA ) {
return true ;
}
}
return false ;
} ;
OverlapKeeper . prototype . getDiff = function ( dictA , dictB , result ) {
var result = result || [ ] ;
var last = dictA ;
var current = dictB ;
result . length = 0 ;
var l = current . keys . length ;
while ( l -- ) {
var key = current . keys [ l ] ;
var data = current . data [ key ] ;
if ( ! data ) {
throw new Error ( 'Key ' + key + ' had no data!' ) ;
}
var lastData = last . data [ key ] ;
if ( ! lastData ) {
// Not overlapping in last state, but in current.
result . push ( data ) ;
}
}
return result ;
} ;
OverlapKeeper . prototype . isNewOverlap = function ( shapeA , shapeB ) {
var idA = shapeA . id | 0 ,
idB = shapeB . id | 0 ;
var last = this . overlappingShapesLastState ;
var current = this . overlappingShapesCurrentState ;
// Not in last but in new
return ! ! ! last . get ( idA , idB ) && ! ! current . get ( idA , idB ) ;
} ;
OverlapKeeper . prototype . getNewBodyOverlaps = function ( result ) {
this . tmpArray1 . length = 0 ;
var overlaps = this . getNewOverlaps ( this . tmpArray1 ) ;
return this . getBodyDiff ( overlaps , result ) ;
} ;
OverlapKeeper . prototype . getEndBodyOverlaps = function ( result ) {
this . tmpArray1 . length = 0 ;
var overlaps = this . getEndOverlaps ( this . tmpArray1 ) ;
return this . getBodyDiff ( overlaps , result ) ;
} ;
OverlapKeeper . prototype . getBodyDiff = function ( overlaps , result ) {
result = result || [ ] ;
var accumulator = this . tmpDict ;
var l = overlaps . length ;
while ( l -- ) {
var data = overlaps [ l ] ;
// Since we use body id's for the accumulator, these will be a subset of the original one
accumulator . set ( data . bodyA . id | 0 , data . bodyB . id | 0 , data ) ;
}
l = accumulator . keys . length ;
while ( l -- ) {
var data = accumulator . getByKey ( accumulator . keys [ l ] ) ;
if ( data ) {
result . push ( data . bodyA , data . bodyB ) ;
}
}
accumulator . reset ( ) ;
return result ;
} ;
} , { "./OverlapKeeperRecord" : 53 , "./OverlapKeeperRecordPool" : 54 , "./TupleDictionary" : 56 , "./Utils" : 57 } ] , 53 : [ function ( _dereq _ , module , exports ) {
module . exports = OverlapKeeperRecord ;
/ * *
* Overlap data container for the OverlapKeeper
* @ class OverlapKeeperRecord
* @ constructor
* @ param { Body } bodyA
* @ param { Shape } shapeA
* @ param { Body } bodyB
* @ param { Shape } shapeB
* /
function OverlapKeeperRecord ( bodyA , shapeA , bodyB , shapeB ) {
/ * *
* @ property { Shape } shapeA
* /
this . shapeA = shapeA ;
/ * *
* @ property { Shape } shapeB
* /
this . shapeB = shapeB ;
/ * *
* @ property { Body } bodyA
* /
this . bodyA = bodyA ;
/ * *
* @ property { Body } bodyB
* /
this . bodyB = bodyB ;
}
/ * *
* Set the data for the record
* @ method set
* @ param { Body } bodyA
* @ param { Shape } shapeA
* @ param { Body } bodyB
* @ param { Shape } shapeB
* /
OverlapKeeperRecord . prototype . set = function ( bodyA , shapeA , bodyB , shapeB ) {
OverlapKeeperRecord . call ( this , bodyA , shapeA , bodyB , shapeB ) ;
} ;
} , { } ] , 54 : [ function ( _dereq _ , module , exports ) {
var OverlapKeeperRecord = _dereq _ ( './OverlapKeeperRecord' ) ;
var Pool = _dereq _ ( './Pool' ) ;
module . exports = OverlapKeeperRecordPool ;
/ * *
* @ class
* /
function OverlapKeeperRecordPool ( ) {
Pool . apply ( this , arguments ) ;
}
OverlapKeeperRecordPool . prototype = new Pool ( ) ;
OverlapKeeperRecordPool . prototype . constructor = OverlapKeeperRecordPool ;
/ * *
* @ method create
* @ return { OverlapKeeperRecord }
* /
OverlapKeeperRecordPool . prototype . create = function ( ) {
return new OverlapKeeperRecord ( ) ;
} ;
/ * *
* @ method destroy
* @ param { OverlapKeeperRecord } record
* @ return { OverlapKeeperRecordPool }
* /
OverlapKeeperRecordPool . prototype . destroy = function ( record ) {
record . bodyA = record . bodyB = record . shapeA = record . shapeB = null ;
return this ;
} ;
} , { "./OverlapKeeperRecord" : 53 , "./Pool" : 55 } ] , 55 : [ function ( _dereq _ , module , exports ) {
module . exports = Pool ;
/ * *
* @ class Object pooling utility .
* /
function Pool ( options ) {
options = options || { } ;
/ * *
* @ property { Array } objects
* @ type { Array }
* /
this . objects = [ ] ;
if ( options . size !== undefined ) {
this . resize ( options . size ) ;
}
}
/ * *
* @ method resize
* @ param { number } size
* @ return { Pool } Self , for chaining
* /
Pool . prototype . resize = function ( size ) {
var objects = this . objects ;
while ( objects . length > size ) {
objects . pop ( ) ;
}
while ( objects . length < size ) {
objects . push ( this . create ( ) ) ;
}
return this ;
} ;
/ * *
* Get an object from the pool or create a new instance .
* @ method get
* @ return { Object }
* /
Pool . prototype . get = function ( ) {
var objects = this . objects ;
return objects . length ? objects . pop ( ) : this . create ( ) ;
} ;
/ * *
* Clean up and put the object back into the pool for later use .
* @ method release
* @ param { Object } object
* @ return { Pool } Self for chaining
* /
Pool . prototype . release = function ( object ) {
this . destroy ( object ) ;
this . objects . push ( object ) ;
return this ;
} ;
} , { } ] , 56 : [ function ( _dereq _ , module , exports ) {
var Utils = _dereq _ ( './Utils' ) ;
module . exports = TupleDictionary ;
/ * *
* @ class TupleDictionary
* @ constructor
* /
function TupleDictionary ( ) {
/ * *
* The data storage
* @ property data
* @ type { Object }
* /
this . data = { } ;
/ * *
* Keys that are currently used .
* @ property { Array } keys
* /
this . keys = [ ] ;
}
/ * *
* Generate a key given two integers
* @ method getKey
* @ param { number } i
* @ param { number } j
* @ return { string }
* /
TupleDictionary . prototype . getKey = function ( id1 , id2 ) {
id1 = id1 | 0 ;
id2 = id2 | 0 ;
if ( ( id1 | 0 ) === ( id2 | 0 ) ) {
return - 1 ;
}
// valid for values < 2^16
return ( ( id1 | 0 ) > ( id2 | 0 ) ?
( id1 << 16 ) | ( id2 & 0xFFFF ) :
( id2 << 16 ) | ( id1 & 0xFFFF ) ) | 0
;
} ;
/ * *
* @ method getByKey
* @ param { Number } key
* @ return { Object }
* /
TupleDictionary . prototype . getByKey = function ( key ) {
key = key | 0 ;
return this . data [ key ] ;
} ;
/ * *
* @ method get
* @ param { Number } i
* @ param { Number } j
* @ return { Number }
* /
TupleDictionary . prototype . get = function ( i , j ) {
return this . data [ this . getKey ( i , j ) ] ;
} ;
/ * *
* Set a value .
* @ method set
* @ param { Number } i
* @ param { Number } j
* @ param { Number } value
* /
TupleDictionary . prototype . set = function ( i , j , value ) {
if ( ! value ) {
throw new Error ( "No data!" ) ;
}
var key = this . getKey ( i , j ) ;
// Check if key already exists
if ( ! this . data [ key ] ) {
this . keys . push ( key ) ;
}
this . data [ key ] = value ;
return key ;
} ;
/ * *
* Remove all data .
* @ method reset
* /
TupleDictionary . prototype . reset = function ( ) {
var data = this . data ,
keys = this . keys ;
var l = keys . length ;
while ( l -- ) {
delete data [ keys [ l ] ] ;
}
keys . length = 0 ;
} ;
/ * *
* Copy another TupleDictionary . Note that all data in this dictionary will be removed .
* @ method copy
* @ param { TupleDictionary } dict The TupleDictionary to copy into this one .
* /
TupleDictionary . prototype . copy = function ( dict ) {
this . reset ( ) ;
Utils . appendArray ( this . keys , dict . keys ) ;
var l = dict . keys . length ;
while ( l -- ) {
var key = dict . keys [ l ] ;
this . data [ key ] = dict . data [ key ] ;
}
} ;
} , { "./Utils" : 57 } ] , 57 : [ function ( _dereq _ , module , exports ) {
/* global P2_ARRAY_TYPE */
module . exports = Utils ;
/ * *
* Misc utility functions
* @ class Utils
* @ constructor
* /
function Utils ( ) { }
/ * *
* Append the values in array b to the array a . See < a href = "http://stackoverflow.com/questions/1374126/how-to-append-an-array-to-an-existing-javascript-array/1374131#1374131" > this < / a > f o r a n e x p l a n a t i o n .
* @ method appendArray
* @ static
* @ param { Array } a
* @ param { Array } b
* /
Utils . appendArray = function ( a , b ) {
if ( b . length < 150000 ) {
a . push . apply ( a , b ) ;
} else {
for ( var i = 0 , len = b . length ; i !== len ; ++ i ) {
a . push ( b [ i ] ) ;
}
}
} ;
/ * *
* Garbage free Array . splice ( ) . Does not allocate a new array .
* @ method splice
* @ static
* @ param { Array } array
* @ param { Number } index
* @ param { Number } howmany
* /
Utils . splice = function ( array , index , howmany ) {
howmany = howmany || 1 ;
for ( var i = index , len = array . length - howmany ; i < len ; i ++ ) {
array [ i ] = array [ i + howmany ] ;
}
array . length = len ;
} ;
/ * *
* The array type to use for internal numeric computations throughout the library . Float32Array is used if it is available , but falls back on Array . If you want to set array type manually , inject it via the global variable P2 _ARRAY _TYPE . See example below .
* @ static
* @ property { function } ARRAY _TYPE
* @ example
* < script >
* <!-- Inject your preferred array type before loading p2 . js -- >
* P2 _ARRAY _TYPE = Array ;
* < / s c r i p t >
* < script src = "p2.js" > < / s c r i p t >
* /
if ( typeof P2 _ARRAY _TYPE !== 'undefined' ) {
Utils . ARRAY _TYPE = P2 _ARRAY _TYPE ;
} else if ( typeof Float32Array !== 'undefined' ) {
Utils . ARRAY _TYPE = Float32Array ;
} else {
Utils . ARRAY _TYPE = Array ;
}
/ * *
* Extend an object with the properties of another
* @ static
* @ method extend
* @ param { object } a
* @ param { object } b
* /
Utils . extend = function ( a , b ) {
for ( var key in b ) {
a [ key ] = b [ key ] ;
}
} ;
/ * *
* Extend an options object with default values .
* @ static
* @ method defaults
* @ param { object } options The options object . May be falsy : in this case , a new object is created and returned .
* @ param { object } defaults An object containing default values .
* @ return { object } The modified options object .
* /
Utils . defaults = function ( options , defaults ) {
options = options || { } ;
for ( var key in defaults ) {
if ( ! ( key in options ) ) {
options [ key ] = defaults [ key ] ;
}
}
return options ;
} ;
} , { } ] , 58 : [ function ( _dereq _ , module , exports ) {
var Body = _dereq _ ( '../objects/Body' ) ;
module . exports = Island ;
/ * *
* An island of bodies connected with equations .
* @ class Island
* @ constructor
* /
function Island ( ) {
/ * *
* Current equations in this island .
* @ property equations
* @ type { Array }
* /
this . equations = [ ] ;
/ * *
* Current bodies in this island .
* @ property bodies
* @ type { Array }
* /
this . bodies = [ ] ;
}
/ * *
* Clean this island from bodies and equations .
* @ method reset
* /
Island . prototype . reset = function ( ) {
this . equations . length = this . bodies . length = 0 ;
} ;
var bodyIds = [ ] ;
/ * *
* Get all unique bodies in this island .
* @ method getBodies
* @ return { Array } An array of Body
* /
Island . prototype . getBodies = function ( result ) {
var bodies = result || [ ] ,
eqs = this . equations ;
bodyIds . length = 0 ;
for ( var i = 0 ; i !== eqs . length ; i ++ ) {
var eq = eqs [ i ] ;
if ( bodyIds . indexOf ( eq . bodyA . id ) === - 1 ) {
bodies . push ( eq . bodyA ) ;
bodyIds . push ( eq . bodyA . id ) ;
}
if ( bodyIds . indexOf ( eq . bodyB . id ) === - 1 ) {
bodies . push ( eq . bodyB ) ;
bodyIds . push ( eq . bodyB . id ) ;
}
}
return bodies ;
} ;
/ * *
* Check if the entire island wants to sleep .
* @ method wantsToSleep
* @ return { Boolean }
* /
Island . prototype . wantsToSleep = function ( ) {
for ( var i = 0 ; i < this . bodies . length ; i ++ ) {
var b = this . bodies [ i ] ;
if ( b . type === Body . DYNAMIC && ! b . wantsToSleep ) {
return false ;
}
}
return true ;
} ;
/ * *
* Make all bodies in the island sleep .
* @ method sleep
* /
Island . prototype . sleep = function ( ) {
for ( var i = 0 ; i < this . bodies . length ; i ++ ) {
var b = this . bodies [ i ] ;
b . sleep ( ) ;
}
return true ;
} ;
} , { "../objects/Body" : 31 } ] , 59 : [ function ( _dereq _ , module , exports ) {
var vec2 = _dereq _ ( '../math/vec2' )
, Island = _dereq _ ( './Island' )
, IslandNode = _dereq _ ( './IslandNode' )
, IslandNodePool = _dereq _ ( './../utils/IslandNodePool' )
, IslandPool = _dereq _ ( './../utils/IslandPool' )
, Body = _dereq _ ( '../objects/Body' ) ;
module . exports = IslandManager ;
/ * *
* Splits the system of bodies and equations into independent islands
*
* @ class IslandManager
* @ constructor
* @ param { Object } [ options ]
* @ extends Solver
* /
function IslandManager ( options ) {
/ * *
* @ property nodePool
* @ type { IslandNodePool }
* /
this . nodePool = new IslandNodePool ( { size : 16 } ) ;
/ * *
* @ property islandPool
* @ type { IslandPool }
* /
this . islandPool = new IslandPool ( { size : 8 } ) ;
/ * *
* The equations to split . Manually fill this array before running . split ( ) .
* @ property { Array } equations
* /
this . equations = [ ] ;
/ * *
* The resulting { { # crossLink "Island" } } { { / c r o s s L i n k } } s .
* @ property { Array } islands
* /
this . islands = [ ] ;
/ * *
* The resulting graph nodes .
* @ property { Array } nodes
* /
this . nodes = [ ] ;
/ * *
* The node queue , used when traversing the graph of nodes .
* @ private
* @ property { Array } queue
* /
this . queue = [ ] ;
}
/ * *
* Get an unvisited node from a list of nodes .
* @ static
* @ method getUnvisitedNode
* @ param { Array } nodes
* @ return { IslandNode | boolean } The node if found , else false .
* /
IslandManager . getUnvisitedNode = function ( nodes ) {
var Nnodes = nodes . length ;
for ( var i = 0 ; i !== Nnodes ; i ++ ) {
var node = nodes [ i ] ;
if ( ! node . visited && node . body . type === Body . DYNAMIC ) {
return node ;
}
}
return false ;
} ;
/ * *
* Visit a node .
* @ method visit
* @ param { IslandNode } node
* @ param { Array } bds
* @ param { Array } eqs
* /
IslandManager . prototype . visit = function ( node , bds , eqs ) {
bds . push ( node . body ) ;
var Neqs = node . equations . length ;
for ( var i = 0 ; i !== Neqs ; i ++ ) {
var eq = node . equations [ i ] ;
if ( eqs . indexOf ( eq ) === - 1 ) { // Already added?
eqs . push ( eq ) ;
}
}
} ;
/ * *
* Runs the search algorithm , starting at a root node . The resulting bodies and equations will be stored in the provided arrays .
* @ method bfs
* @ param { IslandNode } root The node to start from
* @ param { Array } bds An array to append resulting Bodies to .
* @ param { Array } eqs An array to append resulting Equations to .
* /
IslandManager . prototype . bfs = function ( root , bds , eqs ) {
// Reset the visit queue
var queue = this . queue ;
queue . length = 0 ;
// Add root node to queue
queue . push ( root ) ;
root . visited = true ;
this . visit ( root , bds , eqs ) ;
// Process all queued nodes
while ( queue . length ) {
// Get next node in the queue
var node = queue . pop ( ) ;
// Visit unvisited neighboring nodes
var child ;
while ( ( child = IslandManager . getUnvisitedNode ( node . neighbors ) ) ) {
child . visited = true ;
this . visit ( child , bds , eqs ) ;
// Only visit the children of this node if it's dynamic
if ( child . body . type === Body . DYNAMIC ) {
queue . push ( child ) ;
}
}
}
} ;
/ * *
* Split the world into independent islands . The result is stored in . islands .
* @ method split
* @ param { World } world
* @ return { Array } The generated islands
* /
IslandManager . prototype . split = function ( world ) {
var bodies = world . bodies ,
nodes = this . nodes ,
equations = this . equations ;
// Move old nodes to the node pool
while ( nodes . length ) {
this . nodePool . release ( nodes . pop ( ) ) ;
}
// Create needed nodes, reuse if possible
for ( var i = 0 ; i !== bodies . length ; i ++ ) {
var node = this . nodePool . get ( ) ;
node . body = bodies [ i ] ;
nodes . push ( node ) ;
// if(this.nodePool.length){
// var node = this.nodePool.pop();
// node.reset();
// node.body = bodies[i];
// nodes.push(node);
// } else {
// nodes.push(new IslandNode(bodies[i]));
// }
}
// Add connectivity data. Each equation connects 2 bodies.
for ( var k = 0 ; k !== equations . length ; k ++ ) {
var eq = equations [ k ] ,
i = bodies . indexOf ( eq . bodyA ) ,
j = bodies . indexOf ( eq . bodyB ) ,
ni = nodes [ i ] ,
nj = nodes [ j ] ;
ni . neighbors . push ( nj ) ;
nj . neighbors . push ( ni ) ;
ni . equations . push ( eq ) ;
nj . equations . push ( eq ) ;
}
// Move old islands to the island pool
var islands = this . islands ;
for ( var i = 0 ; i < islands . length ; i ++ ) {
this . islandPool . release ( islands [ i ] ) ;
}
islands . length = 0 ;
// Get islands
var child ;
while ( ( child = IslandManager . getUnvisitedNode ( nodes ) ) ) {
// Create new island
var island = this . islandPool . get ( ) ;
// Get all equations and bodies in this island
this . bfs ( child , island . bodies , island . equations ) ;
islands . push ( island ) ;
}
return islands ;
} ;
} , { "../math/vec2" : 30 , "../objects/Body" : 31 , "./../utils/IslandNodePool" : 50 , "./../utils/IslandPool" : 51 , "./Island" : 58 , "./IslandNode" : 60 } ] , 60 : [ function ( _dereq _ , module , exports ) {
module . exports = IslandNode ;
/ * *
* Holds a body and keeps track of some additional properties needed for graph traversal .
* @ class IslandNode
* @ constructor
* @ param { Body } body
* /
function IslandNode ( body ) {
/ * *
* The body that is contained in this node .
* @ property { Body } body
* /
this . body = body ;
/ * *
* Neighboring IslandNodes
* @ property { Array } neighbors
* /
this . neighbors = [ ] ;
/ * *
* Equations connected to this node .
* @ property { Array } equations
* /
this . equations = [ ] ;
/ * *
* If this node was visiting during the graph traversal .
* @ property visited
* @ type { Boolean }
* /
this . visited = false ;
}
/ * *
* Clean this node from bodies and equations .
* @ method reset
* /
IslandNode . prototype . reset = function ( ) {
this . equations . length = 0 ;
this . neighbors . length = 0 ;
this . visited = false ;
this . body = null ;
} ;
} , { } ] , 61 : [ function ( _dereq _ , module , exports ) {
var GSSolver = _dereq _ ( '../solver/GSSolver' )
, Solver = _dereq _ ( '../solver/Solver' )
, Ray = _dereq _ ( '../collision/Ray' )
, vec2 = _dereq _ ( '../math/vec2' )
, Circle = _dereq _ ( '../shapes/Circle' )
, Convex = _dereq _ ( '../shapes/Convex' )
, Line = _dereq _ ( '../shapes/Line' )
, Plane = _dereq _ ( '../shapes/Plane' )
, Capsule = _dereq _ ( '../shapes/Capsule' )
, Particle = _dereq _ ( '../shapes/Particle' )
, EventEmitter = _dereq _ ( '../events/EventEmitter' )
, Body = _dereq _ ( '../objects/Body' )
, Shape = _dereq _ ( '../shapes/Shape' )
, LinearSpring = _dereq _ ( '../objects/LinearSpring' )
, Material = _dereq _ ( '../material/Material' )
, ContactMaterial = _dereq _ ( '../material/ContactMaterial' )
, DistanceConstraint = _dereq _ ( '../constraints/DistanceConstraint' )
, Constraint = _dereq _ ( '../constraints/Constraint' )
, LockConstraint = _dereq _ ( '../constraints/LockConstraint' )
, RevoluteConstraint = _dereq _ ( '../constraints/RevoluteConstraint' )
, PrismaticConstraint = _dereq _ ( '../constraints/PrismaticConstraint' )
, GearConstraint = _dereq _ ( '../constraints/GearConstraint' )
, pkg = _dereq _ ( '../../package.json' )
, Broadphase = _dereq _ ( '../collision/Broadphase' )
, AABB = _dereq _ ( '../collision/AABB' )
, SAPBroadphase = _dereq _ ( '../collision/SAPBroadphase' )
, Narrowphase = _dereq _ ( '../collision/Narrowphase' )
, Utils = _dereq _ ( '../utils/Utils' )
, OverlapKeeper = _dereq _ ( '../utils/OverlapKeeper' )
, IslandManager = _dereq _ ( './IslandManager' )
, RotationalSpring = _dereq _ ( '../objects/RotationalSpring' ) ;
module . exports = World ;
/ * *
* The dynamics world , where all bodies and constraints live .
*
* @ class World
* @ constructor
* @ param { Object } [ options ]
* @ param { Solver } [ options . solver ] Defaults to GSSolver .
* @ param { Array } [ options . gravity ] Defaults to y = - 9.78 .
* @ param { Broadphase } [ options . broadphase ] Defaults to SAPBroadphase
* @ param { Boolean } [ options . islandSplit = true ]
* @ extends EventEmitter
*
* @ example
* var world = new World ( {
* gravity : [ 0 , - 10 ] ,
* broadphase : new SAPBroadphase ( )
* } ) ;
* world . addBody ( new Body ( ) ) ;
* /
function World ( options ) {
EventEmitter . apply ( this ) ;
options = options || { } ;
/ * *
* All springs in the world . To add a spring to the world , use { { # crossLink "World/addSpring:method" } } { { / c r o s s L i n k } } .
*
* @ property springs
* @ type { Array }
* /
this . springs = [ ] ;
/ * *
* All bodies in the world . To add a body to the world , use { { # crossLink "World/addBody:method" } } { { / c r o s s L i n k } } .
* @ property { Array } bodies
* /
this . bodies = [ ] ;
/ * *
* Disabled body collision pairs . See { { # crossLink "World/disableBodyCollision:method" } } .
* @ private
* @ property { Array } disabledBodyCollisionPairs
* /
this . disabledBodyCollisionPairs = [ ] ;
/ * *
* The solver used to satisfy constraints and contacts . Default is { { # crossLink "GSSolver" } } { { / c r o s s L i n k } } .
* @ property { Solver } solver
* /
this . solver = options . solver || new GSSolver ( ) ;
/ * *
* The narrowphase to use to generate contacts .
*
* @ property narrowphase
* @ type { Narrowphase }
* /
this . narrowphase = new Narrowphase ( this ) ;
/ * *
* The island manager of this world .
* @ property { IslandManager } islandManager
* /
this . islandManager = new IslandManager ( ) ;
/ * *
* Gravity in the world . This is applied on all bodies in the beginning of each step ( ) .
*
* @ property gravity
* @ type { Array }
* /
this . gravity = vec2 . fromValues ( 0 , - 9.78 ) ;
if ( options . gravity ) {
vec2 . copy ( this . gravity , options . gravity ) ;
}
/ * *
* Gravity to use when approximating the friction max force ( mu * mass * gravity ) .
* @ property { Number } frictionGravity
* /
this . frictionGravity = vec2 . length ( this . gravity ) || 10 ;
/ * *
* Set to true if you want . frictionGravity to be automatically set to the length of . gravity .
* @ property { Boolean } useWorldGravityAsFrictionGravity
* @ default true
* /
this . useWorldGravityAsFrictionGravity = true ;
/ * *
* If the length of . gravity is zero , and . useWorldGravityAsFrictionGravity = true , then switch to using . frictionGravity for friction instead . This fallback is useful for gravityless games .
* @ property { Boolean } useFrictionGravityOnZeroGravity
* @ default true
* /
this . useFrictionGravityOnZeroGravity = true ;
/ * *
* The broadphase algorithm to use .
*
* @ property broadphase
* @ type { Broadphase }
* /
this . broadphase = options . broadphase || new SAPBroadphase ( ) ;
this . broadphase . setWorld ( this ) ;
/ * *
* User - added constraints .
*
* @ property constraints
* @ type { Array }
* /
this . constraints = [ ] ;
/ * *
* Dummy default material in the world , used in . defaultContactMaterial
* @ property { Material } defaultMaterial
* /
this . defaultMaterial = new Material ( ) ;
/ * *
* The default contact material to use , if no contact material was set for the colliding materials .
* @ property { ContactMaterial } defaultContactMaterial
* /
this . defaultContactMaterial = new ContactMaterial ( this . defaultMaterial , this . defaultMaterial ) ;
/ * *
* For keeping track of what time step size we used last step
* @ property lastTimeStep
* @ type { Number }
* /
this . lastTimeStep = 1 / 60 ;
/ * *
* Enable to automatically apply spring forces each step .
* @ property applySpringForces
* @ type { Boolean }
* @ default true
* /
this . applySpringForces = true ;
/ * *
* Enable to automatically apply body damping each step .
* @ property applyDamping
* @ type { Boolean }
* @ default true
* /
this . applyDamping = true ;
/ * *
* Enable to automatically apply gravity each step .
* @ property applyGravity
* @ type { Boolean }
* @ default true
* /
this . applyGravity = true ;
/ * *
* Enable / disable constraint solving in each step .
* @ property solveConstraints
* @ type { Boolean }
* @ default true
* /
this . solveConstraints = true ;
/ * *
* The ContactMaterials added to the World .
* @ property contactMaterials
* @ type { Array }
* /
this . contactMaterials = [ ] ;
/ * *
* World time .
* @ property time
* @ type { Number }
* /
this . time = 0.0 ;
this . accumulator = 0 ;
/ * *
* Is true during step ( ) .
* @ property { Boolean } stepping
* /
this . stepping = false ;
/ * *
* Bodies that are scheduled to be removed at the end of the step .
* @ property { Array } bodiesToBeRemoved
* @ private
* /
this . bodiesToBeRemoved = [ ] ;
/ * *
* Whether to enable island splitting . Island splitting can be an advantage for both precision and performance . See { { # crossLink "IslandManager" } } { { / c r o s s L i n k } } .
* @ property { Boolean } islandSplit
* @ default true
* /
this . islandSplit = typeof ( options . islandSplit ) !== "undefined" ? ! ! options . islandSplit : true ;
/ * *
* Set to true if you want to the world to emit the "impact" event . Turning this off could improve performance .
* @ property emitImpactEvent
* @ type { Boolean }
* @ default true
* /
this . emitImpactEvent = true ;
// Id counters
this . _constraintIdCounter = 0 ;
this . _bodyIdCounter = 0 ;
/ * *
* Fired after the step ( ) .
* @ event postStep
* /
this . postStepEvent = {
type : "postStep"
} ;
/ * *
* Fired when a body is added to the world .
* @ event addBody
* @ param { Body } body
* /
this . addBodyEvent = {
type : "addBody" ,
body : null
} ;
/ * *
* Fired when a body is removed from the world .
* @ event removeBody
* @ param { Body } body
* /
this . removeBodyEvent = {
type : "removeBody" ,
body : null
} ;
/ * *
* Fired when a spring is added to the world .
* @ event addSpring
* @ param { Spring } spring
* /
this . addSpringEvent = {
type : "addSpring" ,
spring : null
} ;
/ * *
* Fired when a first contact is created between two bodies . This event is fired after the step has been done .
* @ event impact
* @ param { Body } bodyA
* @ param { Body } bodyB
* /
this . impactEvent = {
type : "impact" ,
bodyA : null ,
bodyB : null ,
shapeA : null ,
shapeB : null ,
contactEquation : null
} ;
/ * *
* Fired after the Broadphase has collected collision pairs in the world .
* Inside the event handler , you can modify the pairs array as you like , to
* prevent collisions between objects that you don ' t want .
* @ event postBroadphase
* @ param { Array } pairs An array of collision pairs . If this array is [ body1 , body2 , body3 , body4 ] , then the body pairs 1 , 2 and 3 , 4 would advance to narrowphase .
* /
this . postBroadphaseEvent = {
type : "postBroadphase" ,
pairs : null
} ;
/ * *
* How to deactivate bodies during simulation . Possible modes are : { { # crossLink "World/NO_SLEEPING:property" } } World . NO _SLEEPING { { / c r o s s L i n k } } , { { # c r o s s L i n k " W o r l d / B O D Y _ S L E E P I N G : p r o p e r t y " } } W o r l d . B O D Y _ S L E E P I N G { { / c r o s s L i n k } } a n d { { # c r o s s L i n k " W o r l d / I S L A N D _ S L E E P I N G : p r o p e r t y " } } W o r l d . I S L A N D _ S L E E P I N G { { / c r o s s L i n k } } .
* If sleeping is enabled , you might need to { { # crossLink "Body/wakeUp:method" } } wake up { { / c r o s s L i n k } } t h e b o d i e s i f t h e y f a l l a s l e e p w h e n t h e y s h o u l d n ' t . I f y o u w a n t t o e n a b l e s l e e p i n g i n t h e w o r l d , b u t w a n t t o d i s a b l e i t f o r a p a r t i c u l a r b o d y , s e e { { # c r o s s L i n k " B o d y / a l l o w S l e e p : p r o p e r t y " } } B o d y . a l l o w S l e e p { { / c r o s s L i n k } } .
* @ property sleepMode
* @ type { number }
* @ default World . NO _SLEEPING
* /
this . sleepMode = World . NO _SLEEPING ;
/ * *
* Fired when two shapes starts start to overlap . Fired in the narrowphase , during step .
* @ event beginContact
* @ param { Shape } shapeA
* @ param { Shape } shapeB
* @ param { Body } bodyA
* @ param { Body } bodyB
* @ param { Array } contactEquations
* /
this . beginContactEvent = {
type : "beginContact" ,
shapeA : null ,
shapeB : null ,
bodyA : null ,
bodyB : null ,
contactEquations : [ ]
} ;
/ * *
* Fired when two shapes stop overlapping , after the narrowphase ( during step ) .
* @ event endContact
* @ param { Shape } shapeA
* @ param { Shape } shapeB
* @ param { Body } bodyA
* @ param { Body } bodyB
* /
this . endContactEvent = {
type : "endContact" ,
shapeA : null ,
shapeB : null ,
bodyA : null ,
bodyB : null
} ;
/ * *
* Fired just before equations are added to the solver to be solved . Can be used to control what equations goes into the solver .
* @ event preSolve
* @ param { Array } contactEquations An array of contacts to be solved .
* @ param { Array } frictionEquations An array of friction equations to be solved .
* /
this . preSolveEvent = {
type : "preSolve" ,
contactEquations : null ,
frictionEquations : null
} ;
// For keeping track of overlapping shapes
this . overlappingShapesLastState = { keys : [ ] } ;
this . overlappingShapesCurrentState = { keys : [ ] } ;
/ * *
* @ property { OverlapKeeper } overlapKeeper
* /
this . overlapKeeper = new OverlapKeeper ( ) ;
}
World . prototype = new Object ( EventEmitter . prototype ) ;
World . prototype . constructor = World ;
/ * *
* Never deactivate bodies .
* @ static
* @ property { number } NO _SLEEPING
* /
World . NO _SLEEPING = 1 ;
/ * *
* Deactivate individual bodies if they are sleepy .
* @ static
* @ property { number } BODY _SLEEPING
* /
World . BODY _SLEEPING = 2 ;
/ * *
* Deactivates bodies that are in contact , if all of them are sleepy . Note that you must enable { { # crossLink "World/islandSplit:property" } } . islandSplit { { / c r o s s L i n k } } f o r t h i s t o w o r k .
* @ static
* @ property { number } ISLAND _SLEEPING
* /
World . ISLAND _SLEEPING = 4 ;
/ * *
* Add a constraint to the simulation .
*
* @ method addConstraint
* @ param { Constraint } constraint
* @ example
* var constraint = new LockConstraint ( bodyA , bodyB ) ;
* world . addConstraint ( constraint ) ;
* /
World . prototype . addConstraint = function ( constraint ) {
this . constraints . push ( constraint ) ;
} ;
/ * *
* Add a ContactMaterial to the simulation .
* @ method addContactMaterial
* @ param { ContactMaterial } contactMaterial
* /
World . prototype . addContactMaterial = function ( contactMaterial ) {
this . contactMaterials . push ( contactMaterial ) ;
} ;
/ * *
* Removes a contact material
*
* @ method removeContactMaterial
* @ param { ContactMaterial } cm
* /
World . prototype . removeContactMaterial = function ( cm ) {
var idx = this . contactMaterials . indexOf ( cm ) ;
if ( idx !== - 1 ) {
Utils . splice ( this . contactMaterials , idx , 1 ) ;
}
} ;
/ * *
* Get a contact material given two materials
* @ method getContactMaterial
* @ param { Material } materialA
* @ param { Material } materialB
* @ return { ContactMaterial } The matching ContactMaterial , or false on fail .
* @ todo Use faster hash map to lookup from material id ' s
* /
World . prototype . getContactMaterial = function ( materialA , materialB ) {
var cmats = this . contactMaterials ;
for ( var i = 0 , N = cmats . length ; i !== N ; i ++ ) {
var cm = cmats [ i ] ;
if ( ( cm . materialA . id === materialA . id ) && ( cm . materialB . id === materialB . id ) ||
( cm . materialA . id === materialB . id ) && ( cm . materialB . id === materialA . id ) ) {
return cm ;
}
}
return false ;
} ;
/ * *
* Removes a constraint
*
* @ method removeConstraint
* @ param { Constraint } constraint
* /
World . prototype . removeConstraint = function ( constraint ) {
var idx = this . constraints . indexOf ( constraint ) ;
if ( idx !== - 1 ) {
Utils . splice ( this . constraints , idx , 1 ) ;
}
} ;
var step _r = vec2 . create ( ) ,
step _runit = vec2 . create ( ) ,
step _u = vec2 . create ( ) ,
step _f = vec2 . create ( ) ,
step _fhMinv = vec2 . create ( ) ,
step _velodt = vec2 . create ( ) ,
step _mg = vec2 . create ( ) ,
xiw = vec2 . fromValues ( 0 , 0 ) ,
xjw = vec2 . fromValues ( 0 , 0 ) ,
zero = vec2 . fromValues ( 0 , 0 ) ,
interpvelo = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Step the physics world forward in time .
*
* There are two modes . The simple mode is fixed timestepping without interpolation . In this case you only use the first argument . The second case uses interpolation . In that you also provide the time since the function was last used , as well as the maximum fixed timesteps to take .
*
* @ method step
* @ param { Number } dt The fixed time step size to use .
* @ param { Number } [ timeSinceLastCalled = 0 ] The time elapsed since the function was last called .
* @ param { Number } [ maxSubSteps = 10 ] Maximum number of fixed steps to take per function call .
*
* @ example
* // Simple fixed timestepping without interpolation
* var fixedTimeStep = 1 / 60 ;
* var world = new World ( ) ;
* var body = new Body ( { mass : 1 } ) ;
* world . addBody ( body ) ;
*
* function animate ( ) {
* requestAnimationFrame ( animate ) ;
* world . step ( fixedTimeStep ) ;
* renderBody ( body . position , body . angle ) ;
* }
*
* // Start animation loop
* requestAnimationFrame ( animate ) ;
*
* @ example
* // Fixed timestepping with interpolation
* var maxSubSteps = 10 ;
* var lastTimeSeconds ;
*
* function animate ( t ) {
* requestAnimationFrame ( animate ) ;
* timeSeconds = t / 1000 ;
* lastTimeSeconds = lastTimeSeconds || timeSeconds ;
*
* deltaTime = timeSeconds - lastTimeSeconds ;
* world . step ( fixedTimeStep , deltaTime , maxSubSteps ) ;
*
* renderBody ( body . interpolatedPosition , body . interpolatedAngle ) ;
* }
*
* // Start animation loop
* requestAnimationFrame ( animate ) ;
*
* @ see http : //bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
* /
World . prototype . step = function ( dt , timeSinceLastCalled , maxSubSteps ) {
maxSubSteps = maxSubSteps || 10 ;
timeSinceLastCalled = timeSinceLastCalled || 0 ;
if ( timeSinceLastCalled === 0 ) { // Fixed, simple stepping
this . internalStep ( dt ) ;
// Increment time
this . time += dt ;
} else {
this . accumulator += timeSinceLastCalled ;
var substeps = 0 ;
while ( this . accumulator >= dt && substeps < maxSubSteps ) {
// Do fixed steps to catch up
this . internalStep ( dt ) ;
this . time += dt ;
this . accumulator -= dt ;
substeps ++ ;
}
var t = ( this . accumulator % dt ) / dt ;
for ( var j = 0 ; j !== this . bodies . length ; j ++ ) {
var b = this . bodies [ j ] ;
vec2 . lerp ( b . interpolatedPosition , b . previousPosition , b . position , t ) ;
b . interpolatedAngle = b . previousAngle + t * ( b . angle - b . previousAngle ) ;
}
}
} ;
var endOverlaps = [ ] ;
/ * *
* Make a fixed step .
* @ method internalStep
* @ param { number } dt
* @ private
* /
World . prototype . internalStep = function ( dt ) {
this . stepping = true ;
var that = this ,
Nsprings = this . springs . length ,
springs = this . springs ,
bodies = this . bodies ,
g = this . gravity ,
solver = this . solver ,
Nbodies = this . bodies . length ,
broadphase = this . broadphase ,
np = this . narrowphase ,
constraints = this . constraints ,
t0 , t1 ,
fhMinv = step _fhMinv ,
velodt = step _velodt ,
mg = step _mg ,
scale = vec2 . scale ,
add = vec2 . add ,
rotate = vec2 . rotate ,
islandManager = this . islandManager ;
this . overlapKeeper . tick ( ) ;
this . lastTimeStep = dt ;
// Update approximate friction gravity.
if ( this . useWorldGravityAsFrictionGravity ) {
var gravityLen = vec2 . length ( this . gravity ) ;
if ( ! ( gravityLen === 0 && this . useFrictionGravityOnZeroGravity ) ) {
// Nonzero gravity. Use it.
this . frictionGravity = gravityLen ;
}
}
// Add gravity to bodies
if ( this . applyGravity ) {
for ( var i = 0 ; i !== Nbodies ; i ++ ) {
var b = bodies [ i ] ,
fi = b . force ;
if ( b . type !== Body . DYNAMIC || b . sleepState === Body . SLEEPING ) {
continue ;
}
vec2 . scale ( mg , g , b . mass * b . gravityScale ) ; // F=m*g
add ( fi , fi , mg ) ;
}
}
// Add spring forces
if ( this . applySpringForces ) {
for ( var i = 0 ; i !== Nsprings ; i ++ ) {
var s = springs [ i ] ;
s . applyForce ( ) ;
}
}
if ( this . applyDamping ) {
for ( var i = 0 ; i !== Nbodies ; i ++ ) {
var b = bodies [ i ] ;
if ( b . type === Body . DYNAMIC ) {
b . applyDamping ( dt ) ;
}
}
}
// Broadphase
var result = broadphase . getCollisionPairs ( this ) ;
// Remove ignored collision pairs
var ignoredPairs = this . disabledBodyCollisionPairs ;
for ( var i = ignoredPairs . length - 2 ; i >= 0 ; i -= 2 ) {
for ( var j = result . length - 2 ; j >= 0 ; j -= 2 ) {
if ( ( ignoredPairs [ i ] === result [ j ] && ignoredPairs [ i + 1 ] === result [ j + 1 ] ) ||
( ignoredPairs [ i + 1 ] === result [ j ] && ignoredPairs [ i ] === result [ j + 1 ] ) ) {
result . splice ( j , 2 ) ;
}
}
}
// Remove constrained pairs with collideConnected == false
var Nconstraints = constraints . length ;
for ( i = 0 ; i !== Nconstraints ; i ++ ) {
var c = constraints [ i ] ;
if ( ! c . collideConnected ) {
for ( var j = result . length - 2 ; j >= 0 ; j -= 2 ) {
if ( ( c . bodyA === result [ j ] && c . bodyB === result [ j + 1 ] ) ||
( c . bodyB === result [ j ] && c . bodyA === result [ j + 1 ] ) ) {
result . splice ( j , 2 ) ;
}
}
}
}
// postBroadphase event
this . postBroadphaseEvent . pairs = result ;
this . emit ( this . postBroadphaseEvent ) ;
this . postBroadphaseEvent . pairs = null ;
// Narrowphase
np . reset ( this ) ;
for ( var i = 0 , Nresults = result . length ; i !== Nresults ; i += 2 ) {
var bi = result [ i ] ,
bj = result [ i + 1 ] ;
// Loop over all shapes of body i
for ( var k = 0 , Nshapesi = bi . shapes . length ; k !== Nshapesi ; k ++ ) {
var si = bi . shapes [ k ] ,
xi = si . position ,
ai = si . angle ;
// All shapes of body j
for ( var l = 0 , Nshapesj = bj . shapes . length ; l !== Nshapesj ; l ++ ) {
var sj = bj . shapes [ l ] ,
xj = sj . position ,
aj = sj . angle ;
var cm = this . defaultContactMaterial ;
if ( si . material && sj . material ) {
var tmp = this . getContactMaterial ( si . material , sj . material ) ;
if ( tmp ) {
cm = tmp ;
}
}
this . runNarrowphase ( np , bi , si , xi , ai , bj , sj , xj , aj , cm , this . frictionGravity ) ;
}
}
}
// Wake up bodies
for ( var i = 0 ; i !== Nbodies ; i ++ ) {
var body = bodies [ i ] ;
if ( body . _wakeUpAfterNarrowphase ) {
body . wakeUp ( ) ;
body . _wakeUpAfterNarrowphase = false ;
}
}
// Emit end overlap events
if ( this . has ( 'endContact' ) ) {
this . overlapKeeper . getEndOverlaps ( endOverlaps ) ;
var e = this . endContactEvent ;
var l = endOverlaps . length ;
while ( l -- ) {
var data = endOverlaps [ l ] ;
e . shapeA = data . shapeA ;
e . shapeB = data . shapeB ;
e . bodyA = data . bodyA ;
e . bodyB = data . bodyB ;
this . emit ( e ) ;
}
endOverlaps . length = 0 ;
}
var preSolveEvent = this . preSolveEvent ;
preSolveEvent . contactEquations = np . contactEquations ;
preSolveEvent . frictionEquations = np . frictionEquations ;
this . emit ( preSolveEvent ) ;
preSolveEvent . contactEquations = preSolveEvent . frictionEquations = null ;
// update constraint equations
var Nconstraints = constraints . length ;
for ( i = 0 ; i !== Nconstraints ; i ++ ) {
constraints [ i ] . update ( ) ;
}
if ( np . contactEquations . length || np . frictionEquations . length || Nconstraints ) {
if ( this . islandSplit ) {
// Split into islands
islandManager . equations . length = 0 ;
Utils . appendArray ( islandManager . equations , np . contactEquations ) ;
Utils . appendArray ( islandManager . equations , np . frictionEquations ) ;
for ( i = 0 ; i !== Nconstraints ; i ++ ) {
Utils . appendArray ( islandManager . equations , constraints [ i ] . equations ) ;
}
islandManager . split ( this ) ;
for ( var i = 0 ; i !== islandManager . islands . length ; i ++ ) {
var island = islandManager . islands [ i ] ;
if ( island . equations . length ) {
solver . solveIsland ( dt , island ) ;
}
}
} else {
// Add contact equations to solver
solver . addEquations ( np . contactEquations ) ;
solver . addEquations ( np . frictionEquations ) ;
// Add user-defined constraint equations
for ( i = 0 ; i !== Nconstraints ; i ++ ) {
solver . addEquations ( constraints [ i ] . equations ) ;
}
if ( this . solveConstraints ) {
solver . solve ( dt , this ) ;
}
solver . removeAllEquations ( ) ;
}
}
// Step forward
for ( var i = 0 ; i !== Nbodies ; i ++ ) {
var body = bodies [ i ] ;
// if(body.sleepState !== Body.SLEEPING && body.type !== Body.STATIC){
body . integrate ( dt ) ;
// }
}
// Reset force
for ( var i = 0 ; i !== Nbodies ; i ++ ) {
bodies [ i ] . setZeroForce ( ) ;
}
// Emit impact event
if ( this . emitImpactEvent && this . has ( 'impact' ) ) {
var ev = this . impactEvent ;
for ( var i = 0 ; i !== np . contactEquations . length ; i ++ ) {
var eq = np . contactEquations [ i ] ;
if ( eq . firstImpact ) {
ev . bodyA = eq . bodyA ;
ev . bodyB = eq . bodyB ;
ev . shapeA = eq . shapeA ;
ev . shapeB = eq . shapeB ;
ev . contactEquation = eq ;
this . emit ( ev ) ;
}
}
}
// Sleeping update
if ( this . sleepMode === World . BODY _SLEEPING ) {
for ( i = 0 ; i !== Nbodies ; i ++ ) {
bodies [ i ] . sleepTick ( this . time , false , dt ) ;
}
} else if ( this . sleepMode === World . ISLAND _SLEEPING && this . islandSplit ) {
// Tell all bodies to sleep tick but dont sleep yet
for ( i = 0 ; i !== Nbodies ; i ++ ) {
bodies [ i ] . sleepTick ( this . time , true , dt ) ;
}
// Sleep islands
for ( var i = 0 ; i < this . islandManager . islands . length ; i ++ ) {
var island = this . islandManager . islands [ i ] ;
if ( island . wantsToSleep ( ) ) {
island . sleep ( ) ;
}
}
}
this . stepping = false ;
// Remove bodies that are scheduled for removal
var bodiesToBeRemoved = this . bodiesToBeRemoved ;
for ( var i = 0 ; i !== bodiesToBeRemoved . length ; i ++ ) {
this . removeBody ( bodiesToBeRemoved [ i ] ) ;
}
bodiesToBeRemoved . length = 0 ;
this . emit ( this . postStepEvent ) ;
} ;
/ * *
* Runs narrowphase for the shape pair i and j .
* @ method runNarrowphase
* @ param { Narrowphase } np
* @ param { Body } bi
* @ param { Shape } si
* @ param { Array } xi
* @ param { Number } ai
* @ param { Body } bj
* @ param { Shape } sj
* @ param { Array } xj
* @ param { Number } aj
* @ param { Number } mu
* /
World . prototype . runNarrowphase = function ( np , bi , si , xi , ai , bj , sj , xj , aj , cm , glen ) {
// Check collision groups and masks
if ( ! ( ( si . collisionGroup & sj . collisionMask ) !== 0 && ( sj . collisionGroup & si . collisionMask ) !== 0 ) ) {
return ;
}
// Get world position and angle of each shape
vec2 . rotate ( xiw , xi , bi . angle ) ;
vec2 . rotate ( xjw , xj , bj . angle ) ;
vec2 . add ( xiw , xiw , bi . position ) ;
vec2 . add ( xjw , xjw , bj . position ) ;
var aiw = ai + bi . angle ;
var ajw = aj + bj . angle ;
np . enableFriction = cm . friction > 0 ;
np . frictionCoefficient = cm . friction ;
var reducedMass ;
if ( bi . type === Body . STATIC || bi . type === Body . KINEMATIC ) {
reducedMass = bj . mass ;
} else if ( bj . type === Body . STATIC || bj . type === Body . KINEMATIC ) {
reducedMass = bi . mass ;
} else {
reducedMass = ( bi . mass * bj . mass ) / ( bi . mass + bj . mass ) ;
}
np . slipForce = cm . friction * glen * reducedMass ;
np . restitution = cm . restitution ;
np . surfaceVelocity = cm . surfaceVelocity ;
np . frictionStiffness = cm . frictionStiffness ;
np . frictionRelaxation = cm . frictionRelaxation ;
np . stiffness = cm . stiffness ;
np . relaxation = cm . relaxation ;
np . contactSkinSize = cm . contactSkinSize ;
np . enabledEquations = bi . collisionResponse && bj . collisionResponse && si . collisionResponse && sj . collisionResponse ;
var resolver = np [ si . type | sj . type ] ,
numContacts = 0 ;
if ( resolver ) {
var sensor = si . sensor || sj . sensor ;
var numFrictionBefore = np . frictionEquations . length ;
if ( si . type < sj . type ) {
numContacts = resolver . call ( np , bi , si , xiw , aiw , bj , sj , xjw , ajw , sensor ) ;
} else {
numContacts = resolver . call ( np , bj , sj , xjw , ajw , bi , si , xiw , aiw , sensor ) ;
}
var numFrictionEquations = np . frictionEquations . length - numFrictionBefore ;
if ( numContacts ) {
if ( bi . allowSleep &&
bi . type === Body . DYNAMIC &&
bi . sleepState === Body . SLEEPING &&
bj . sleepState === Body . AWAKE &&
bj . type !== Body . STATIC
) {
var speedSquaredB = vec2 . squaredLength ( bj . velocity ) + Math . pow ( bj . angularVelocity , 2 ) ;
var speedLimitSquaredB = Math . pow ( bj . sleepSpeedLimit , 2 ) ;
if ( speedSquaredB >= speedLimitSquaredB * 2 ) {
bi . _wakeUpAfterNarrowphase = true ;
}
}
if ( bj . allowSleep &&
bj . type === Body . DYNAMIC &&
bj . sleepState === Body . SLEEPING &&
bi . sleepState === Body . AWAKE &&
bi . type !== Body . STATIC
) {
var speedSquaredA = vec2 . squaredLength ( bi . velocity ) + Math . pow ( bi . angularVelocity , 2 ) ;
var speedLimitSquaredA = Math . pow ( bi . sleepSpeedLimit , 2 ) ;
if ( speedSquaredA >= speedLimitSquaredA * 2 ) {
bj . _wakeUpAfterNarrowphase = true ;
}
}
this . overlapKeeper . setOverlapping ( bi , si , bj , sj ) ;
if ( this . has ( 'beginContact' ) && this . overlapKeeper . isNewOverlap ( si , sj ) ) {
// Report new shape overlap
var e = this . beginContactEvent ;
e . shapeA = si ;
e . shapeB = sj ;
e . bodyA = bi ;
e . bodyB = bj ;
// Reset contact equations
e . contactEquations . length = 0 ;
if ( typeof ( numContacts ) === "number" ) {
for ( var i = np . contactEquations . length - numContacts ; i < np . contactEquations . length ; i ++ ) {
e . contactEquations . push ( np . contactEquations [ i ] ) ;
}
}
this . emit ( e ) ;
}
// divide the max friction force by the number of contacts
if ( typeof ( numContacts ) === "number" && numFrictionEquations > 1 ) { // Why divide by 1?
for ( var i = np . frictionEquations . length - numFrictionEquations ; i < np . frictionEquations . length ; i ++ ) {
var f = np . frictionEquations [ i ] ;
f . setSlipForce ( f . getSlipForce ( ) / numFrictionEquations ) ;
}
}
}
}
} ;
/ * *
* Add a spring to the simulation
*
* @ method addSpring
* @ param { Spring } spring
* /
World . prototype . addSpring = function ( spring ) {
this . springs . push ( spring ) ;
var evt = this . addSpringEvent ;
evt . spring = spring ;
this . emit ( evt ) ;
evt . spring = null ;
} ;
/ * *
* Remove a spring
*
* @ method removeSpring
* @ param { Spring } spring
* /
World . prototype . removeSpring = function ( spring ) {
var idx = this . springs . indexOf ( spring ) ;
if ( idx !== - 1 ) {
Utils . splice ( this . springs , idx , 1 ) ;
}
} ;
/ * *
* Add a body to the simulation
*
* @ method addBody
* @ param { Body } body
*
* @ example
* var world = new World ( ) ,
* body = new Body ( ) ;
* world . addBody ( body ) ;
* @ todo What if this is done during step ?
* /
World . prototype . addBody = function ( body ) {
if ( this . bodies . indexOf ( body ) === - 1 ) {
this . bodies . push ( body ) ;
body . world = this ;
var evt = this . addBodyEvent ;
evt . body = body ;
this . emit ( evt ) ;
evt . body = null ;
}
} ;
/ * *
* Remove a body from the simulation . If this method is called during step ( ) , the body removal is scheduled to after the step .
*
* @ method removeBody
* @ param { Body } body
* /
World . prototype . removeBody = function ( body ) {
if ( this . stepping ) {
this . bodiesToBeRemoved . push ( body ) ;
} else {
body . world = null ;
var idx = this . bodies . indexOf ( body ) ;
if ( idx !== - 1 ) {
Utils . splice ( this . bodies , idx , 1 ) ;
this . removeBodyEvent . body = body ;
body . resetConstraintVelocity ( ) ;
this . emit ( this . removeBodyEvent ) ;
this . removeBodyEvent . body = null ;
}
}
} ;
/ * *
* Get a body by its id .
* @ method getBodyById
* @ param { number } id
* @ return { Body } The body , or false if it was not found .
* /
World . prototype . getBodyById = function ( id ) {
var bodies = this . bodies ;
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var b = bodies [ i ] ;
if ( b . id === id ) {
return b ;
}
}
return false ;
} ;
/ * *
* Disable collision between two bodies
* @ method disableBodyCollision
* @ param { Body } bodyA
* @ param { Body } bodyB
* /
World . prototype . disableBodyCollision = function ( bodyA , bodyB ) {
this . disabledBodyCollisionPairs . push ( bodyA , bodyB ) ;
} ;
/ * *
* Enable collisions between the given two bodies
* @ method enableBodyCollision
* @ param { Body } bodyA
* @ param { Body } bodyB
* /
World . prototype . enableBodyCollision = function ( bodyA , bodyB ) {
var pairs = this . disabledBodyCollisionPairs ;
for ( var i = 0 ; i < pairs . length ; i += 2 ) {
if ( ( pairs [ i ] === bodyA && pairs [ i + 1 ] === bodyB ) || ( pairs [ i + 1 ] === bodyA && pairs [ i ] === bodyB ) ) {
pairs . splice ( i , 2 ) ;
return ;
}
}
} ;
/ * *
* Resets the World , removes all bodies , constraints and springs .
*
* @ method clear
* /
World . prototype . clear = function ( ) {
this . time = 0 ;
// Remove all solver equations
if ( this . solver && this . solver . equations . length ) {
this . solver . removeAllEquations ( ) ;
}
// Remove all constraints
var cs = this . constraints ;
for ( var i = cs . length - 1 ; i >= 0 ; i -- ) {
this . removeConstraint ( cs [ i ] ) ;
}
// Remove all bodies
var bodies = this . bodies ;
for ( var i = bodies . length - 1 ; i >= 0 ; i -- ) {
this . removeBody ( bodies [ i ] ) ;
}
// Remove all springs
var springs = this . springs ;
for ( var i = springs . length - 1 ; i >= 0 ; i -- ) {
this . removeSpring ( springs [ i ] ) ;
}
// Remove all contact materials
var cms = this . contactMaterials ;
for ( var i = cms . length - 1 ; i >= 0 ; i -- ) {
this . removeContactMaterial ( cms [ i ] ) ;
}
World . apply ( this ) ;
} ;
var hitTest _tmp1 = vec2 . create ( ) ,
hitTest _zero = vec2 . fromValues ( 0 , 0 ) ,
hitTest _tmp2 = vec2 . fromValues ( 0 , 0 ) ;
/ * *
* Test if a world point overlaps bodies
* @ method hitTest
* @ param { Array } worldPoint Point to use for intersection tests
* @ param { Array } bodies A list of objects to check for intersection
* @ param { Number } precision Used for matching against particles and lines . Adds some margin to these infinitesimal objects .
* @ return { Array } Array of bodies that overlap the point
* @ todo Should use an api similar to the raycast function
* @ todo Should probably implement a . containsPoint method for all shapes . Would be more efficient
2016-09-21 22:22:08 +00:00
* @ todo Should use the broadphase
2016-02-12 16:00:36 +00:00
* /
World . prototype . hitTest = function ( worldPoint , bodies , precision ) {
precision = precision || 0 ;
// Create a dummy particle body with a particle shape to test against the bodies
var pb = new Body ( { position : worldPoint } ) ,
ps = new Particle ( ) ,
px = worldPoint ,
pa = 0 ,
x = hitTest _tmp1 ,
zero = hitTest _zero ,
tmp = hitTest _tmp2 ;
pb . addShape ( ps ) ;
var n = this . narrowphase ,
result = [ ] ;
// Check bodies
for ( var i = 0 , N = bodies . length ; i !== N ; i ++ ) {
var b = bodies [ i ] ;
for ( var j = 0 , NS = b . shapes . length ; j !== NS ; j ++ ) {
var s = b . shapes [ j ] ;
// Get shape world position + angle
vec2 . rotate ( x , s . position , b . angle ) ;
vec2 . add ( x , x , b . position ) ;
var a = s . angle + b . angle ;
if ( ( s instanceof Circle && n . circleParticle ( b , s , x , a , pb , ps , px , pa , true ) ) ||
( s instanceof Convex && n . particleConvex ( pb , ps , px , pa , b , s , x , a , true ) ) ||
( s instanceof Plane && n . particlePlane ( pb , ps , px , pa , b , s , x , a , true ) ) ||
( s instanceof Capsule && n . particleCapsule ( pb , ps , px , pa , b , s , x , a , true ) ) ||
( s instanceof Particle && vec2 . squaredLength ( vec2 . sub ( tmp , x , worldPoint ) ) < precision * precision )
) {
result . push ( b ) ;
}
}
}
return result ;
} ;
/ * *
* Set the stiffness for all equations and contact materials .
* @ method setGlobalStiffness
* @ param { Number } stiffness
* /
World . prototype . setGlobalStiffness = function ( stiffness ) {
// Set for all constraints
var constraints = this . constraints ;
for ( var i = 0 ; i !== constraints . length ; i ++ ) {
var c = constraints [ i ] ;
for ( var j = 0 ; j !== c . equations . length ; j ++ ) {
var eq = c . equations [ j ] ;
eq . stiffness = stiffness ;
eq . needsUpdate = true ;
}
}
// Set for all contact materials
var contactMaterials = this . contactMaterials ;
for ( var i = 0 ; i !== contactMaterials . length ; i ++ ) {
var c = contactMaterials [ i ] ;
c . stiffness = c . frictionStiffness = stiffness ;
}
// Set for default contact material
var c = this . defaultContactMaterial ;
c . stiffness = c . frictionStiffness = stiffness ;
} ;
/ * *
* Set the relaxation for all equations and contact materials .
* @ method setGlobalRelaxation
* @ param { Number } relaxation
* /
World . prototype . setGlobalRelaxation = function ( relaxation ) {
// Set for all constraints
for ( var i = 0 ; i !== this . constraints . length ; i ++ ) {
var c = this . constraints [ i ] ;
for ( var j = 0 ; j !== c . equations . length ; j ++ ) {
var eq = c . equations [ j ] ;
eq . relaxation = relaxation ;
eq . needsUpdate = true ;
}
}
// Set for all contact materials
for ( var i = 0 ; i !== this . contactMaterials . length ; i ++ ) {
var c = this . contactMaterials [ i ] ;
c . relaxation = c . frictionRelaxation = relaxation ;
}
// Set for default contact material
var c = this . defaultContactMaterial ;
c . relaxation = c . frictionRelaxation = relaxation ;
} ;
var tmpAABB = new AABB ( ) ;
var tmpArray = [ ] ;
/ * *
* Ray cast against all bodies in the world .
* @ method raycast
* @ param { RaycastResult } result
* @ param { Ray } ray
* @ return { boolean } True if any body was hit .
*
* @ example
* var ray = new Ray ( {
* mode : Ray . CLOSEST , // or ANY
* from : [ 0 , 0 ] ,
* to : [ 10 , 0 ] ,
* } ) ;
* var result = new RaycastResult ( ) ;
* world . raycast ( result , ray ) ;
*
* // Get the hit point
* var hitPoint = vec2 . create ( ) ;
* result . getHitPoint ( hitPoint , ray ) ;
* console . log ( 'Hit point: ' , hitPoint [ 0 ] , hitPoint [ 1 ] , ' at distance ' + result . getHitDistance ( ray ) ) ;
*
* @ example
* var ray = new Ray ( {
* mode : Ray . ALL ,
* from : [ 0 , 0 ] ,
* to : [ 10 , 0 ] ,
* callback : function ( result ) {
*
* // Print some info about the hit
* console . log ( 'Hit body and shape: ' , result . body , result . shape ) ;
*
* // Get the hit point
* var hitPoint = vec2 . create ( ) ;
* result . getHitPoint ( hitPoint , ray ) ;
* console . log ( 'Hit point: ' , hitPoint [ 0 ] , hitPoint [ 1 ] , ' at distance ' + result . getHitDistance ( ray ) ) ;
*
* // If you are happy with the hits you got this far, you can stop the traversal here:
* result . stop ( ) ;
* }
* } ) ;
* var result = new RaycastResult ( ) ;
* world . raycast ( result , ray ) ;
* /
World . prototype . raycast = function ( result , ray ) {
// Get all bodies within the ray AABB
ray . getAABB ( tmpAABB ) ;
this . broadphase . aabbQuery ( this , tmpAABB , tmpArray ) ;
ray . intersectBodies ( result , tmpArray ) ;
tmpArray . length = 0 ;
return result . hasHit ( ) ;
} ;
} , { "../../package.json" : 6 , "../collision/AABB" : 7 , "../collision/Broadphase" : 8 , "../collision/Narrowphase" : 10 , "../collision/Ray" : 11 , "../collision/SAPBroadphase" : 13 , "../constraints/Constraint" : 14 , "../constraints/DistanceConstraint" : 15 , "../constraints/GearConstraint" : 16 , "../constraints/LockConstraint" : 17 , "../constraints/PrismaticConstraint" : 18 , "../constraints/RevoluteConstraint" : 19 , "../events/EventEmitter" : 26 , "../material/ContactMaterial" : 27 , "../material/Material" : 28 , "../math/vec2" : 30 , "../objects/Body" : 31 , "../objects/LinearSpring" : 32 , "../objects/RotationalSpring" : 33 , "../shapes/Capsule" : 38 , "../shapes/Circle" : 39 , "../shapes/Convex" : 40 , "../shapes/Line" : 42 , "../shapes/Particle" : 43 , "../shapes/Plane" : 44 , "../shapes/Shape" : 45 , "../solver/GSSolver" : 46 , "../solver/Solver" : 47 , "../utils/OverlapKeeper" : 52 , "../utils/Utils" : 57 , "./IslandManager" : 59 } ] } , { } , [ 36 ] )
( 36 )
} ) ;