2014-02-19 09:15:05 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* matter . js 0.8 . 0 - edge 2014 - 06 - 09
2014-02-19 09:15:05 -05:00
* http : //brm.io/matter-js/
* License : MIT
* /
/ * *
* The MIT License ( MIT )
*
* Copyright ( c ) 2014 Liam Brummitt
*
* 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 ( ) {
var Matter = { } ;
// Begin Matter namespace closure
// All Matter modules are included below during build
// Outro.js then closes at the end of the file
// Begin src/body/Body.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Body ` module contains methods for creating and manipulating body models .
* A ` Matter.Body ` is a rigid body that can be simulated by a ` Matter.Engine ` .
* Factories for commonly used body configurations ( such as rectangles , circles and other polygons ) can be found in the module ` Matter.Bodies ` .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
2014-06-09 14:40:24 -04:00
2014-02-28 20:10:08 -05:00
* @ class Body
* /
2014-02-19 09:15:05 -05:00
var Body = { } ;
( function ( ) {
2014-06-09 14:40:24 -04:00
Body . _inertiaScale = 4 ;
2014-05-01 09:09:06 -04:00
var _nextGroupId = 1 ;
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new rigid body model . The options parameter is an object that specifies any properties you wish to override the defaults .
* All properties have default values , and many are pre - calculated automatically based on other properties .
* See the properites section below for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method create
* @ param { } options
* @ return { body } body
* /
2014-02-19 09:15:05 -05:00
Body . create = function ( options ) {
var defaults = {
2014-05-01 09:09:06 -04:00
id : Common . nextId ( ) ,
2014-03-30 14:45:30 -04:00
type : 'body' ,
2014-05-01 09:09:06 -04:00
label : 'Body' ,
2014-02-19 09:15:05 -05:00
angle : 0 ,
2014-05-05 14:32:51 -04:00
vertices : Vertices . fromPath ( 'L 0 0 L 40 0 L 40 40 L 0 40' ) ,
2014-02-19 09:15:05 -05:00
position : { x : 0 , y : 0 } ,
force : { x : 0 , y : 0 } ,
torque : 0 ,
positionImpulse : { x : 0 , y : 0 } ,
2014-03-30 14:45:30 -04:00
constraintImpulse : { x : 0 , y : 0 , angle : 0 } ,
2014-02-19 09:15:05 -05:00
speed : 0 ,
angularSpeed : 0 ,
velocity : { x : 0 , y : 0 } ,
angularVelocity : 0 ,
isStatic : false ,
isSleeping : false ,
motion : 0 ,
sleepThreshold : 60 ,
density : 0.001 ,
restitution : 0 ,
friction : 0.1 ,
frictionAir : 0.01 ,
groupId : 0 ,
2014-03-22 13:51:49 -04:00
slop : 0.05 ,
2014-05-01 09:09:06 -04:00
timeScale : 1 ,
2014-03-22 13:51:49 -04:00
render : {
visible : true ,
2014-03-30 14:45:30 -04:00
sprite : {
xScale : 1 ,
yScale : 1
} ,
2014-03-22 13:51:49 -04:00
lineWidth : 1.5
}
2014-02-19 09:15:05 -05:00
} ;
var body = Common . extend ( defaults , options ) ;
2014-06-09 14:40:24 -04:00
_initProperties ( body , options ) ;
2014-02-19 09:15:05 -05:00
return body ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the next unique groupID number .
2014-02-28 20:10:08 -05:00
* @ method nextGroupId
* @ return { Number } Unique groupID
* /
2014-02-19 09:15:05 -05:00
Body . nextGroupId = function ( ) {
return _nextGroupId ++ ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Initialises body properties .
2014-05-01 09:09:06 -04:00
* @ method _initProperties
* @ private
2014-02-28 20:10:08 -05:00
* @ param { body } body
2014-06-09 14:40:24 -04:00
* @ param { } options
2014-02-28 20:10:08 -05:00
* /
2014-06-09 14:40:24 -04:00
var _initProperties = function ( body , options ) {
// init required properties
body . bounds = body . bounds || Bounds . create ( body . vertices ) ;
body . positionPrev = body . positionPrev || Vector . clone ( body . position ) ;
2014-02-19 09:15:05 -05:00
body . anglePrev = body . anglePrev || body . angle ;
2014-06-09 14:40:24 -04:00
// must use setters for the more complicated properties
Body . setVertices ( body , body . vertices ) ;
Body . setStatic ( body , body . isStatic ) ;
Sleeping . set ( body , body . isSleeping ) ;
2014-02-19 09:15:05 -05:00
Vertices . rotate ( body . vertices , body . angle , body . position ) ;
Axes . rotate ( body . axes , body . angle ) ;
2014-06-09 14:40:24 -04:00
// allow options to override the automatically calculated properties
body . axes = options . axes || body . axes ;
body . area = options . area || body . area ;
body . mass = options . mass || body . mass ;
body . inertia = options . inertia || body . inertia ;
body . inverseMass = 1 / body . mass ;
body . inverseInertia = 1 / body . inertia ;
// render properties
var defaultFillStyle = ( body . isStatic ? '#eeeeee' : Common . choose ( [ '#556270' , '#4ECDC4' , '#C7F464' , '#FF6B6B' , '#C44D58' ] ) ) ,
defaultStrokeStyle = Common . shadeColor ( defaultFillStyle , - 20 ) ;
body . render . fillStyle = body . render . fillStyle || defaultFillStyle ;
body . render . strokeStyle = body . render . strokeStyle || defaultStrokeStyle ;
2014-05-01 09:09:06 -04:00
} ;
/ * *
2014-06-09 14:40:24 -04:00
* Sets the body as static , including isStatic flag and setting mass and inertia to Infinity .
2014-05-01 09:09:06 -04:00
* @ method setStatic
2014-06-09 14:40:24 -04:00
* @ param { body } body
2014-05-01 09:09:06 -04:00
* @ param { bool } isStatic
* /
Body . setStatic = function ( body , isStatic ) {
body . isStatic = isStatic ;
if ( isStatic ) {
2014-02-19 09:15:05 -05:00
body . restitution = 0 ;
body . friction = 1 ;
body . mass = body . inertia = body . density = Infinity ;
body . inverseMass = body . inverseInertia = 0 ;
2014-03-22 13:51:49 -04:00
body . render . lineWidth = 1 ;
2014-02-19 09:15:05 -05:00
2014-05-01 09:09:06 -04:00
body . positionPrev . x = body . position . x ;
body . positionPrev . y = body . position . y ;
body . anglePrev = body . angle ;
body . angularVelocity = 0 ;
body . speed = 0 ;
body . angularSpeed = 0 ;
body . motion = 0 ;
}
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Sets the body ' s vertices and updates body properties accordingly , including inertia , area and mass ( with respect to ` body.density ` ) .
* Vertices will be automatically transformed to be orientated around their centre of mass as the origin .
* They are then automatically translated to world space based on ` body.position ` .
*
* The ` vertices ` argument should be passed as an array of ` Matter.Vector ` points ( or a ` Matter.Vertices ` array ) .
* Vertices must form a convex hull , concave hulls are not supported .
*
* @ method setVertices
* @ param { body } body
* @ param { vector [ ] } vertices
* /
Body . setVertices = function ( body , vertices ) {
// change vertices
if ( vertices [ 0 ] . body === body ) {
body . vertices = vertices ;
} else {
body . vertices = Vertices . create ( vertices , body ) ;
}
// update properties
body . axes = Axes . fromVertices ( body . vertices ) ;
body . area = Vertices . area ( body . vertices ) ;
body . mass = body . density * body . area ;
body . inverseMass = 1 / body . mass ;
// orient vertices around the centre of mass at origin (0, 0)
var centre = Vertices . centre ( body . vertices ) ;
Vertices . translate ( body . vertices , centre , - 1 ) ;
// update inertia while vertices are at origin (0, 0)
body . inertia = Body . _inertiaScale * Vertices . inertia ( body . vertices , body . mass ) ;
body . inverseInertia = 1 / body . inertia ;
// update geometry
Vertices . translate ( body . vertices , body . position ) ;
Bounds . update ( body . bounds , body . vertices , body . velocity ) ;
} ;
/ * *
* Sets the position of the body instantly . Velocity , angle , force etc . are unchanged .
* @ method setPosition
* @ param { body } body
* @ param { vector } position
* /
Body . setPosition = function ( body , position ) {
var delta = Vector . sub ( position , body . position ) ;
body . position . x = position . x ;
body . position . y = position . y ;
body . positionPrev . x += delta . x ;
body . positionPrev . y += delta . y ;
Vertices . translate ( body . vertices , delta ) ;
Bounds . update ( body . bounds , body . vertices , body . velocity ) ;
} ;
/ * *
* Sets the angle of the body instantly . Angular velocity , position , force etc . are unchanged .
* @ method setAngle
* @ param { body } body
* @ param { number } angle
* /
Body . setAngle = function ( body , angle ) {
var delta = angle - body . angle ;
body . angle = angle ;
body . anglePrev += delta ;
Vertices . rotate ( body . vertices , delta , body . position ) ;
Axes . rotate ( body . axes , delta ) ;
Bounds . update ( body . bounds , body . vertices , body . velocity ) ;
} ;
/ * *
* Sets the linear velocity of the body instantly . Position , angle , force etc . are unchanged . See also ` Body.applyForce ` .
* @ method setVelocity
* @ param { body } body
* @ param { vector } velocity
* /
Body . setVelocity = function ( body , velocity ) {
body . positionPrev . x = body . position . x - velocity . x ;
body . positionPrev . y = body . position . y - velocity . y ;
body . velocity . x = velocity . x ;
body . velocity . y = velocity . y ;
body . speed = Vector . magnitude ( body . velocity ) ;
} ;
/ * *
* Sets the angular velocity of the body instantly . Position , angle , force etc . are unchanged . See also ` Body.applyForce ` .
* @ method setAngularVelocity
* @ param { body } body
* @ param { number } velocity
* /
Body . setAngularVelocity = function ( body , velocity ) {
body . anglePrev = body . angle - velocity ;
body . angularVelocity = velocity ;
body . angularSpeed = Math . abs ( body . angularVelocity ) ;
} ;
/ * *
* Moves a body by a given vector relative to its current position , without imparting any velocity .
* @ method translate
* @ param { body } body
* @ param { vector } translation
* /
Body . translate = function ( body , translation ) {
Body . setPosition ( body , Vector . add ( body . position , translation ) ) ;
} ;
/ * *
* Rotates a body by a given angle relative to its current angle , without imparting any angular velocity .
* @ method rotate
* @ param { body } body
* @ param { number } rotation
* /
Body . rotate = function ( body , rotation ) {
Body . setAngle ( body , body . angle + angle ) ;
} ;
/ * *
* Scales the body , including updating physical properties ( mass , area , axes , inertia ) , from a world - space point ( default is body centre ) .
* @ method scale
* @ param { body } body
* @ param { number } scaleX
* @ param { number } scaleY
* @ param { vector } [ point ]
* /
Body . scale = function ( body , scaleX , scaleY , point ) {
// scale vertices
Vertices . scale ( body . vertices , scaleX , scaleY , point ) ;
// update properties
body . axes = Axes . fromVertices ( body . vertices ) ;
body . area = Vertices . area ( body . vertices ) ;
body . mass = body . density * body . area ;
body . inverseMass = 1 / body . mass ;
// update inertia (requires vertices to be at origin)
Vertices . translate ( body . vertices , { x : - body . position . x , y : - body . position . y } ) ;
body . inertia = Vertices . inertia ( body . vertices , body . mass ) ;
body . inverseInertia = 1 / body . inertia ;
Vertices . translate ( body . vertices , { x : body . position . x , y : body . position . y } ) ;
// update bounds
Bounds . update ( body . bounds , body . vertices , body . velocity ) ;
} ;
/ * *
* Zeroes the ` body.force ` and ` body.torque ` force buffers .
2014-02-28 20:10:08 -05:00
* @ method resetForcesAll
* @ param { body [ ] } bodies
* /
2014-03-11 06:29:14 -04:00
Body . resetForcesAll = function ( bodies ) {
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
// reset force buffers
body . force . x = 0 ;
body . force . y = 0 ;
body . torque = 0 ;
2014-03-11 06:29:14 -04:00
}
} ;
/ * *
2014-06-09 14:40:24 -04:00
* Applys a mass dependant force to all given bodies .
2014-03-11 06:29:14 -04:00
* @ method applyGravityAll
* @ param { body [ ] } bodies
* @ param { vector } gravity
* /
Body . applyGravityAll = function ( bodies , gravity ) {
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( body . isStatic || body . isSleeping )
continue ;
2014-02-19 09:15:05 -05:00
// apply gravity
body . force . y += body . mass * gravity . y * 0.001 ;
body . force . x += body . mass * gravity . x * 0.001 ;
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Applys ` Body.update ` to all given ` bodies ` .
2014-02-28 20:10:08 -05:00
* @ method updateAll
* @ param { body [ ] } bodies
2014-06-09 14:40:24 -04:00
* @ param { number } deltaTime
* The amount of time elapsed between updates
2014-05-05 14:32:51 -04:00
* @ param { number } timeScale
2014-06-09 14:40:24 -04:00
* @ param { number } correction
* The Verlet correction factor ( deltaTime / lastDeltaTime )
2014-02-28 20:10:08 -05:00
* @ param { bounds } worldBounds
* /
2014-05-05 14:32:51 -04:00
Body . updateAll = function ( bodies , deltaTime , timeScale , correction , worldBounds ) {
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( body . isStatic || body . isSleeping )
continue ;
// don't update out of world bodies
// TODO: viewports
if ( body . bounds . max . x < worldBounds . min . x || body . bounds . min . x > worldBounds . max . x
|| body . bounds . max . y < worldBounds . min . y || body . bounds . min . y > worldBounds . max . y )
continue ;
2014-05-05 14:32:51 -04:00
Body . update ( body , deltaTime , timeScale , correction ) ;
2014-02-19 09:15:05 -05:00
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Performs a simulation step for the given ` body ` , including updating position and angle using Verlet integration .
2014-02-28 20:10:08 -05:00
* @ method update
* @ param { body } body
* @ param { number } deltaTime
2014-05-05 14:32:51 -04:00
* @ param { number } timeScale
2014-02-28 20:10:08 -05:00
* @ param { number } correction
* /
2014-05-05 14:32:51 -04:00
Body . update = function ( body , deltaTime , timeScale , correction ) {
var deltaTimeSquared = Math . pow ( deltaTime * timeScale * body . timeScale , 2 ) ;
2014-02-19 09:15:05 -05:00
// from the previous step
2014-05-05 14:32:51 -04:00
var frictionAir = 1 - body . frictionAir * timeScale * body . timeScale ,
2014-02-19 09:15:05 -05:00
velocityPrevX = body . position . x - body . positionPrev . x ,
velocityPrevY = body . position . y - body . positionPrev . y ;
// update velocity with verlet integration
body . velocity . x = ( velocityPrevX * frictionAir * correction ) + ( body . force . x / body . mass ) * deltaTimeSquared ;
body . velocity . y = ( velocityPrevY * frictionAir * correction ) + ( body . force . y / body . mass ) * deltaTimeSquared ;
body . positionPrev . x = body . position . x ;
body . positionPrev . y = body . position . y ;
body . position . x += body . velocity . x ;
body . position . y += body . velocity . y ;
// update angular velocity with verlet integration
body . angularVelocity = ( ( body . angle - body . anglePrev ) * frictionAir * correction ) + ( body . torque / body . inertia ) * deltaTimeSquared ;
body . anglePrev = body . angle ;
body . angle += body . angularVelocity ;
// track speed and acceleration
body . speed = Vector . magnitude ( body . velocity ) ;
body . angularSpeed = Math . abs ( body . angularVelocity ) ;
// transform the body geometry
Vertices . translate ( body . vertices , body . velocity ) ;
2014-04-01 08:47:17 -04:00
if ( body . angularVelocity !== 0 ) {
Vertices . rotate ( body . vertices , body . angularVelocity , body . position ) ;
Axes . rotate ( body . axes , body . angularVelocity ) ;
}
2014-02-19 09:15:05 -05:00
Bounds . update ( body . bounds , body . vertices , body . velocity ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Applies a force to a body from a given world - space position , including resulting torque .
2014-02-28 20:10:08 -05:00
* @ method applyForce
* @ param { body } body
* @ param { vector } position
* @ param { vector } force
* /
2014-02-19 09:15:05 -05:00
Body . applyForce = function ( body , position , force ) {
body . force . x += force . x ;
body . force . y += force . y ;
var offset = { x : position . x - body . position . x , y : position . y - body . position . y } ;
body . torque += ( offset . x * force . y - offset . y * force . x ) * body . inverseInertia ;
} ;
2014-06-09 14:40:24 -04:00
/ *
*
* Properties Documentation
*
* /
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* An integer ` Number ` uniquely identifying number generated in ` Body.create ` by ` Common.nextId ` .
*
* @ property id
* @ type number
2014-02-28 20:10:08 -05:00
* /
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* A ` String ` denoting the type of object .
*
* @ property type
* @ type string
* @ default "body"
2014-02-28 20:10:08 -05:00
* /
2014-02-19 09:15:05 -05:00
2014-05-01 09:09:06 -04:00
/ * *
2014-06-09 14:40:24 -04:00
* An arbitrary ` String ` name to help the user identify and manage bodies .
*
* @ property label
* @ type string
* @ default "Body"
2014-05-01 09:09:06 -04:00
* /
2014-06-09 14:40:24 -04:00
/ * *
* A ` Number ` specifying the angle of the body , in radians .
*
* @ property angle
* @ type number
* @ default 0
* /
2014-05-01 09:09:06 -04:00
2014-06-09 14:40:24 -04:00
/ * *
* An array of ` Vector ` objects that specify the convex hull of the rigid body .
* These should be provided about the origin ` (0, 0) ` . E . g .
*
* [ { x : 0 , y : 0 } , { x : 25 , y : 50 } , { x : 50 , y : 0 } ]
*
* When passed via ` Body.create ` , the verticies are translated relative to ` body.position ` ( i . e . world - space , and constantly updated by ` Body.update ` during simulation ) .
* The ` Vector ` objects are also augmented with additional properties required for efficient collision detection .
*
* Other properties such as ` inertia ` and ` bounds ` are automatically calculated from the passed vertices ( unless provided via ` options ` ) .
* Concave hulls are not currently supported . The module ` Matter.Vertices ` contains useful methods for working with vertices .
*
* @ property vertices
* @ type vector [ ]
* /
2014-05-01 09:09:06 -04:00
2014-06-09 14:40:24 -04:00
/ * *
* A ` Vector ` that specifies the current world - space position of the body .
*
* @ property position
* @ type vector
* @ default { x : 0 , y : 0 }
* /
/ * *
* A ` Vector ` that specifies the force to apply in the current step . It is zeroed after every ` Body.update ` . See also ` Body.applyForce ` .
*
* @ property force
* @ type vector
* @ default { x : 0 , y : 0 }
* /
/ * *
* A ` Number ` that specifies the torque ( turning force ) to apply in the current step . It is zeroed after every ` Body.update ` .
*
* @ property torque
* @ type number
* @ default 0
* /
/ * *
* A ` Number ` that _measures _ the current speed of the body after the last ` Body.update ` . It is read - only and always positive ( it ' s the magnitude of ` body.velocity ` ) .
*
* @ readOnly
* @ property speed
* @ type number
* @ default 0
* /
/ * *
* A ` Number ` that _measures _ the current angular speed of the body after the last ` Body.update ` . It is read - only and always positive ( it ' s the magnitude of ` body.angularVelocity ` ) .
*
* @ readOnly
* @ property angularSpeed
* @ type number
* @ default 0
* /
/ * *
* A ` Vector ` that _measures _ the current velocity of the body after the last ` Body.update ` . It is read - only .
* If you need to modify a body 's velocity directly, you should either apply a force or simply change the body' s ` position ` ( as the engine uses position - Verlet integration ) .
*
* @ readOnly
* @ property velocity
* @ type vector
* @ default { x : 0 , y : 0 }
* /
/ * *
* A ` Number ` that _measures _ the current angular velocity of the body after the last ` Body.update ` . It is read - only .
* If you need to modify a body 's angular velocity directly, you should apply a torque or simply change the body' s ` angle ` ( as the engine uses position - Verlet integration ) .
*
* @ readOnly
* @ property angularVelocity
* @ type number
* @ default 0
* /
/ * *
* A flag that indicates whether a body is considered static . A static body can never change position or angle and is completely fixed .
* If you need to set a body as static after its creation , you should use ` Body.setStatic ` as this requires more than just setting this flag .
*
* @ property isStatic
* @ type boolean
* @ default false
* /
/ * *
* A flag that indicates whether the body is considered sleeping . A sleeping body acts similar to a static body , except it is only temporary and can be awoken .
* If you need to set a body as sleeping , you should use ` Sleeping.set ` as this requires more than just setting this flag .
*
* @ property isSleeping
* @ type boolean
* @ default false
* /
/ * *
* A ` Number ` that _measures _ the amount of movement a body currently has ( a combination of ` speed ` and ` angularSpeed ` ) . It is read - only and always positive .
* It is used and updated by the ` Matter.Sleeping ` module during simulation to decide if a body has come to rest .
*
* @ readOnly
* @ property motion
* @ type number
* @ default 0
* /
/ * *
* A ` Number ` that defines the number of updates in which this body must have near - zero velocity before it is set as sleeping by the ` Matter.Sleeping ` module ( if sleeping is enabled by the engine ) .
*
* @ property sleepThreshold
* @ type number
* @ default 60
* /
/ * *
* A ` Number ` that defines the density of the body , that is its mass per unit area .
* If you pass the density via ` Body.create ` the ` mass ` property is automatically calculated for you based on the size ( area ) of the object .
* This is generally preferable to simply setting mass and allows for more intuitive definition of materials ( e . g . rock has a higher density than wood ) .
*
* @ property density
* @ type number
* @ default 0.001
* /
/ * *
* A ` Number ` that defines the mass of the body , although it may be more appropriate to specify the ` density ` property instead .
* If you modify this value , you must also modify the ` body.inverseMass ` property ( ` 1 / mass ` ) .
*
* @ property mass
* @ type number
* /
/ * *
* A ` Number ` that defines the inverse mass of the body ( ` 1 / mass ` ) .
* If you modify this value , you must also modify the ` body.mass ` property .
*
* @ property inverseMass
* @ type number
* /
/ * *
* A ` Number ` that defines the moment of inertia ( i . e . second moment of area ) of the body .
* It is automatically calculated from the given convex hull ( ` vertices ` array ) and density in ` Body.create ` .
* If you modify this value , you must also modify the ` body.inverseInertia ` property ( ` 1 / inertia ` ) .
*
* @ property inertia
* @ type number
* /
/ * *
* A ` Number ` that defines the inverse moment of inertia of the body ( ` 1 / inertia ` ) .
* If you modify this value , you must also modify the ` body.inertia ` property .
*
* @ property inverseInertia
* @ type number
* /
/ * *
* A ` Number ` that defines the restitution ( elasticity ) of the body . The value is always positive and is in the range ` (0, 1) ` .
* A value of ` 0 ` means collisions may be perfectly inelastic and no bouncing may occur .
* A value of ` 0.8 ` means the body may bounce back with approximately 80 % of its kinetic energy .
* Note that collision response is based on _pairs _ of bodies , and that ` restitution ` values are _combined _ with the following formula :
*
* Math . max ( bodyA . restitution , bodyB . restitution )
*
* @ property restitution
* @ type number
* @ default 0
* /
/ * *
* A ` Number ` that defines the friction of the body . The value is always positive and is in the range ` (0, 1) ` .
* A value of ` 0 ` means that the body may slide indefinitely .
* A value of ` 1 ` means the body may come to a stop almost instantly after a force is applied .
*
* The effects of the value may be non - linear .
* High values may be unstable depending on the body .
* The engine uses a Coulomb friction model including static and kinetic friction .
* Note that collision response is based on _pairs _ of bodies , and that ` friction ` values are _combined _ with the following formula :
*
* Math . min ( bodyA . friction , bodyB . friction )
*
* @ property friction
* @ type number
* @ default 0.1
* /
/ * *
* A ` Number ` that defines the air friction of the body ( air resistance ) .
* A value of ` 0 ` means the body will never slow as it moves through space .
* The higher the value , the faster a body slows when moving through space .
* The effects of the value are non - linear .
*
* @ property frictionAir
* @ type number
* @ default 0.01
* /
/ * *
* An integer ` Number ` that specifies the collision group the body belongs to .
* Bodies with the same ` groupId ` are considered _as - one _ body and therefore do not interact .
* This allows for creation of segmented bodies that can self - intersect , such as a rope .
* The default value 0 means the body does not belong to a group , and can interact with all other bodies .
*
* @ property groupId
* @ type number
* @ default 0
* /
/ * *
* A ` Number ` that specifies a tollerance on how far a body is allowed to 'sink' or rotate into other bodies .
* Avoid changing this value unless you understand the purpose of ` slop ` in physics engines .
* The default should generally suffice , although very large bodies may require larger values for stable stacking .
*
* @ property slop
* @ type number
* @ default 0.05
* /
/ * *
* A ` Number ` that allows per - body time scaling , e . g . a force - field where bodies inside are in slow - motion , while others are at full speed .
*
* @ property timeScale
* @ type number
* @ default 1
* /
/ * *
* An ` Object ` that defines the rendering properties to be consumed by the module ` Matter.Render ` .
*
* @ property render
* @ type object
* /
/ * *
* A flag that indicates if the body should be rendered .
*
* @ property render . visible
* @ type boolean
* @ default true
* /
/ * *
* An ` Object ` that defines the sprite properties to use when rendering , if any .
*
* @ property render . sprite
* @ type object
* /
/ * *
* An ` String ` that defines the path to the image to use as the sprite texture , if any .
*
* @ property render . sprite . texture
* @ type string
* /
/ * *
* A ` Number ` that defines the scaling in the x - axis for the sprite , if any .
*
* @ property render . sprite . xScale
* @ type number
* @ default 1
* /
/ * *
* A ` Number ` that defines the scaling in the y - axis for the sprite , if any .
*
* @ property render . sprite . yScale
* @ type number
* @ default 1
* /
/ * *
* A ` Number ` that defines the line width to use when rendering the body outline ( if a sprite is not defined ) .
* A value of ` 0 ` means no outline will be rendered .
*
* @ property render . lineWidth
* @ type number
* @ default 1.5
* /
/ * *
* A ` String ` that defines the fill style to use when rendering the body ( if a sprite is not defined ) .
* It is the same as when using a canvas , so it accepts CSS style property values .
*
* @ property render . fillStyle
* @ type string
* @ default a random colour
* /
/ * *
* A ` String ` that defines the stroke style to use when rendering the body outline ( if a sprite is not defined ) .
* It is the same as when using a canvas , so it accepts CSS style property values .
*
* @ property render . strokeStyle
* @ type string
* @ default a random colour
* /
/ * *
* An array of unique axis vectors ( edge normals ) used for collision detection .
* These are automatically calculated from the given convex hull ( ` vertices ` array ) in ` Body.create ` .
* They are constantly updated by ` Body.update ` during the simulation .
*
* @ property axes
* @ type vector [ ]
* /
/ * *
* A ` Number ` that _measures _ the area of the body ' s convex hull , calculated at creation by ` Body.create ` .
*
* @ property area
* @ type string
* @ default
* /
/ * *
* A ` Bounds ` object that defines the AABB region for the body .
* It is automatically calculated from the given convex hull ( ` vertices ` array ) in ` Body.create ` and constantly updated by ` Body.update ` during simulation .
*
* @ property bounds
* @ type bounds
* /
2014-05-01 09:09:06 -04:00
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/body/Body.js
// Begin src/body/Composite.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Composite ` module contains methods for creating and manipulating composite bodies .
* A composite body is a collection of ` Matter.Body ` , ` Matter.Constraint ` and other ` Matter.Composite ` , therefore composites form a tree structure .
* It is important to use the functions in this module to modify composites , rather than directly modifying their properties .
* Note that the ` Matter.World ` object is also a type of ` Matter.Composite ` and as such all composite methods here can also operate on a ` Matter.World ` .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Composite
* /
2014-02-19 09:15:05 -05:00
// TODO: composite translate, rotate
var Composite = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new composite . The options parameter is an object that specifies any properties you wish to override the defaults .
* See the properites section below for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method create
2014-06-09 14:40:24 -04:00
* @ param { } [ options ]
2014-02-28 20:10:08 -05:00
* @ return { composite } A new composite
* /
2014-02-19 09:15:05 -05:00
Composite . create = function ( options ) {
2014-03-24 16:11:42 -04:00
return Common . extend ( {
2014-05-01 09:09:06 -04:00
id : Common . nextId ( ) ,
2014-03-30 14:45:30 -04:00
type : 'composite' ,
2014-03-24 16:11:42 -04:00
parent : null ,
isModified : false ,
bodies : [ ] ,
constraints : [ ] ,
2014-05-01 09:09:06 -04:00
composites : [ ] ,
label : 'Composite'
2014-03-24 16:11:42 -04:00
} , options ) ;
} ;
/ * *
* Sets the composite ' s ` isModified ` flag .
* If ` updateParents ` is true , all parents will be set ( default : false ) .
* If ` updateChildren ` is true , all children will be set ( default : false ) .
* @ method setModified
* @ param { composite } composite
* @ param { boolean } isModified
2014-06-09 14:40:24 -04:00
* @ param { boolean } [ updateParents = false ]
* @ param { boolean } [ updateChildren = false ]
2014-03-24 16:11:42 -04:00
* /
Composite . setModified = function ( composite , isModified , updateParents , updateChildren ) {
composite . isModified = isModified ;
if ( updateParents && composite . parent ) {
Composite . setModified ( composite . parent , isModified , updateParents , updateChildren ) ;
}
if ( updateChildren ) {
for ( var i = 0 ; i < composite . composites . length ; i ++ ) {
var childComposite = composite . composites [ i ] ;
Composite . setModified ( childComposite , isModified , updateParents , updateChildren ) ;
}
}
2014-02-19 09:15:05 -05:00
} ;
2014-03-30 14:45:30 -04:00
/ * *
* Generic add function . Adds one or many body ( s ) , constraint ( s ) or a composite ( s ) to the given composite .
* @ method add
* @ param { composite } composite
* @ param { } object
* @ return { composite } The original composite with the objects added
* /
Composite . add = function ( composite , object ) {
var objects = [ ] . concat ( object ) ;
for ( var i = 0 ; i < objects . length ; i ++ ) {
var obj = objects [ i ] ;
switch ( obj . type ) {
case 'body' :
Composite . addBody ( composite , obj ) ;
break ;
case 'constraint' :
Composite . addConstraint ( composite , obj ) ;
break ;
case 'composite' :
Composite . addComposite ( composite , obj ) ;
break ;
case 'mouseConstraint' :
Composite . addConstraint ( composite , obj . constraint ) ;
break ;
}
}
return composite ;
} ;
/ * *
* Generic remove function . Removes one or many body ( s ) , constraint ( s ) or a composite ( s ) to the given composite .
* Optionally searching its children recursively .
* @ method remove
* @ param { composite } composite
* @ param { } object
2014-06-09 14:40:24 -04:00
* @ param { boolean } [ deep = false ]
2014-03-30 14:45:30 -04:00
* @ return { composite } The original composite with the objects removed
* /
Composite . remove = function ( composite , object , deep ) {
var objects = [ ] . concat ( object ) ;
for ( var i = 0 ; i < objects . length ; i ++ ) {
var obj = objects [ i ] ;
switch ( obj . type ) {
case 'body' :
Composite . removeBody ( composite , obj , deep ) ;
break ;
case 'constraint' :
Composite . removeConstraint ( composite , obj , deep ) ;
break ;
case 'composite' :
Composite . removeComposite ( composite , obj , deep ) ;
break ;
case 'mouseConstraint' :
Composite . removeConstraint ( composite , obj . constraint ) ;
break ;
}
}
return composite ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Adds a composite to the given composite
2014-03-24 16:11:42 -04:00
* @ method addComposite
2014-02-28 20:10:08 -05:00
* @ param { composite } compositeA
* @ param { composite } compositeB
* @ return { composite } The original compositeA with the objects from compositeB added
* /
2014-03-24 16:11:42 -04:00
Composite . addComposite = function ( compositeA , compositeB ) {
compositeA . composites . push ( compositeB ) ;
compositeB . parent = compositeA ;
Composite . setModified ( compositeA , true , true , false ) ;
2014-02-19 09:15:05 -05:00
return compositeA ;
} ;
2014-03-30 14:45:30 -04:00
/ * *
* Removes a composite from the given composite , and optionally searching its children recursively
* @ method removeComposite
* @ param { composite } compositeA
* @ param { composite } compositeB
2014-06-09 14:40:24 -04:00
* @ param { boolean } [ deep = false ]
2014-03-30 14:45:30 -04:00
* @ return { composite } The original compositeA with the composite removed
* /
Composite . removeComposite = function ( compositeA , compositeB , deep ) {
var position = compositeA . composites . indexOf ( compositeB ) ;
if ( position !== - 1 ) {
Composite . removeCompositeAt ( compositeA , position ) ;
Composite . setModified ( compositeA , true , true , false ) ;
}
if ( deep ) {
for ( var i = 0 ; i < compositeA . composites . length ; i ++ ) {
Composite . removeComposite ( compositeA . composites [ i ] , compositeB , true ) ;
}
}
return compositeA ;
} ;
/ * *
* Removes a composite from the given composite
* @ method removeCompositeAt
* @ param { composite } composite
* @ param { number } position
* @ return { composite } The original composite with the composite removed
* /
Composite . removeCompositeAt = function ( composite , position ) {
composite . composites . splice ( position , 1 ) ;
Composite . setModified ( composite , true , true , false ) ;
return composite ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Adds a body to the given composite
2014-02-28 20:10:08 -05:00
* @ method addBody
* @ param { composite } composite
* @ param { body } body
* @ return { composite } The original composite with the body added
* /
2014-02-19 09:15:05 -05:00
Composite . addBody = function ( composite , body ) {
composite . bodies . push ( body ) ;
2014-03-24 16:11:42 -04:00
Composite . setModified ( composite , true , true , false ) ;
return composite ;
} ;
/ * *
* Removes a body from the given composite , and optionally searching its children recursively
* @ method removeBody
* @ param { composite } composite
* @ param { body } body
2014-06-09 14:40:24 -04:00
* @ param { boolean } [ deep = false ]
2014-03-24 16:11:42 -04:00
* @ return { composite } The original composite with the body removed
* /
Composite . removeBody = function ( composite , body , deep ) {
var position = composite . bodies . indexOf ( body ) ;
if ( position !== - 1 ) {
Composite . removeBodyAt ( composite , position ) ;
Composite . setModified ( composite , true , true , false ) ;
}
if ( deep ) {
for ( var i = 0 ; i < composite . composites . length ; i ++ ) {
Composite . removeBody ( composite . composites [ i ] , body , true ) ;
}
}
return composite ;
} ;
/ * *
* Removes a body from the given composite
* @ method removeBodyAt
* @ param { composite } composite
* @ param { number } position
* @ return { composite } The original composite with the body removed
* /
Composite . removeBodyAt = function ( composite , position ) {
composite . bodies . splice ( position , 1 ) ;
Composite . setModified ( composite , true , true , false ) ;
2014-02-19 09:15:05 -05:00
return composite ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Adds a constraint to the given composite
2014-02-28 20:10:08 -05:00
* @ method addConstraint
* @ param { composite } composite
* @ param { constraint } constraint
* @ return { composite } The original composite with the constraint added
* /
2014-02-19 09:15:05 -05:00
Composite . addConstraint = function ( composite , constraint ) {
composite . constraints . push ( constraint ) ;
2014-03-24 16:11:42 -04:00
Composite . setModified ( composite , true , true , false ) ;
2014-02-19 09:15:05 -05:00
return composite ;
} ;
2014-03-24 16:11:42 -04:00
/ * *
* Removes a constraint from the given composite , and optionally searching its children recursively
* @ method removeConstraint
* @ param { composite } composite
* @ param { constraint } constraint
2014-06-09 14:40:24 -04:00
* @ param { boolean } [ deep = false ]
2014-03-24 16:11:42 -04:00
* @ return { composite } The original composite with the constraint removed
* /
Composite . removeConstraint = function ( composite , constraint , deep ) {
var position = composite . constraints . indexOf ( constraint ) ;
if ( position !== - 1 ) {
Composite . removeConstraintAt ( composite , position ) ;
}
if ( deep ) {
for ( var i = 0 ; i < composite . composites . length ; i ++ ) {
Composite . removeConstraint ( composite . composites [ i ] , constraint , true ) ;
}
}
return composite ;
} ;
/ * *
* Removes a body from the given composite
* @ method removeConstraintAt
* @ param { composite } composite
* @ param { number } position
* @ return { composite } The original composite with the constraint removed
* /
Composite . removeConstraintAt = function ( composite , position ) {
composite . constraints . splice ( position , 1 ) ;
Composite . setModified ( composite , true , true , false ) ;
return composite ;
} ;
/ * *
* Removes all bodies , constraints and composites from the given composite
* Optionally clearing its children recursively
* @ method clear
* @ param { world } world
* @ param { boolean } keepStatic
2014-06-09 14:40:24 -04:00
* @ param { boolean } [ deep = false ]
2014-03-24 16:11:42 -04:00
* /
Composite . clear = function ( composite , keepStatic , deep ) {
if ( deep ) {
for ( var i = 0 ; i < composite . composites . length ; i ++ ) {
Composite . clear ( composite . composites [ i ] , keepStatic , true ) ;
}
}
if ( keepStatic ) {
composite . bodies = composite . bodies . filter ( function ( body ) { return body . isStatic ; } ) ;
} else {
composite . bodies . length = 0 ;
}
composite . constraints . length = 0 ;
composite . composites . length = 0 ;
Composite . setModified ( composite , true , true , false ) ;
return composite ;
} ;
/ * *
* Returns all bodies in the given composite , including all bodies in its children , recursively
* @ method allBodies
* @ param { composite } composite
* @ return { body [ ] } All the bodies
* /
Composite . allBodies = function ( composite ) {
var bodies = [ ] . concat ( composite . bodies ) ;
for ( var i = 0 ; i < composite . composites . length ; i ++ )
bodies = bodies . concat ( Composite . allBodies ( composite . composites [ i ] ) ) ;
return bodies ;
} ;
/ * *
* Returns all constraints in the given composite , including all constraints in its children , recursively
* @ method allConstraints
* @ param { composite } composite
* @ return { constraint [ ] } All the constraints
* /
Composite . allConstraints = function ( composite ) {
var constraints = [ ] . concat ( composite . constraints ) ;
for ( var i = 0 ; i < composite . composites . length ; i ++ )
constraints = constraints . concat ( Composite . allConstraints ( composite . composites [ i ] ) ) ;
return constraints ;
} ;
2014-03-30 14:45:30 -04:00
/ * *
* Returns all composites in the given composite , including all composites in its children , recursively
* @ method allComposites
* @ param { composite } composite
* @ return { composite [ ] } All the composites
* /
Composite . allComposites = function ( composite ) {
var composites = [ ] . concat ( composite . composites ) ;
for ( var i = 0 ; i < composite . composites . length ; i ++ )
composites = composites . concat ( Composite . allComposites ( composite . composites [ i ] ) ) ;
return composites ;
} ;
2014-05-01 09:09:06 -04:00
/ * *
* Searches the composite recursively for an object matching the type and id supplied , null if not found
* @ method get
* @ param { composite } composite
* @ param { number } id
* @ param { string } type
* @ return { object } The requested object , if found
* /
Composite . get = function ( composite , id , type ) {
var objects ,
object ;
switch ( type ) {
case 'body' :
objects = Composite . allBodies ( composite ) ;
break ;
case 'constraint' :
objects = Composite . allConstraints ( composite ) ;
break ;
case 'composite' :
objects = Composite . allComposites ( composite ) . concat ( composite ) ;
break ;
}
if ( ! objects )
return null ;
object = objects . filter ( function ( object ) {
return object . id . toString ( ) === id . toString ( ) ;
} ) ;
return object . length === 0 ? null : object [ 0 ] ;
} ;
/ * *
* Moves the given object ( s ) from compositeA to compositeB ( equal to a remove followed by an add )
* @ method move
* @ param { compositeA } compositeA
* @ param { object [ ] } objects
* @ param { compositeB } compositeB
* @ return { composite } Returns compositeA
* /
Composite . move = function ( compositeA , objects , compositeB ) {
Composite . remove ( compositeA , objects ) ;
Composite . add ( compositeB , objects ) ;
return compositeA ;
} ;
/ * *
2014-06-09 14:40:24 -04:00
* Assigns new ids for all objects in the composite , recursively
* @ method rebase
* @ param { composite } composite
* @ return { composite } Returns composite
* /
Composite . rebase = function ( composite ) {
var objects = Composite . allBodies ( composite )
. concat ( Composite . allConstraints ( composite ) )
. concat ( Composite . allComposites ( composite ) ) ;
for ( var i = 0 ; i < objects . length ; i ++ ) {
objects [ i ] . id = Common . nextId ( ) ;
}
Composite . setModified ( composite , true , true , false ) ;
return composite ;
} ;
/ *
*
* Properties Documentation
*
* /
/ * *
* An integer ` Number ` uniquely identifying number generated in ` Composite.create ` by ` Common.nextId ` .
*
* @ property id
* @ type number
* /
/ * *
* A ` String ` denoting the type of object .
*
* @ property type
* @ type string
* @ default "composite"
* /
/ * *
* An arbitrary ` String ` name to help the user identify and manage composites .
*
* @ property label
* @ type string
* @ default "Composite"
* /
/ * *
* A flag that specifies whether the composite has been modified during the current step .
* Most ` Matter.Composite ` methods will automatically set this flag to ` true ` to inform the engine of changes to be handled .
* If you need to change it manually , you should use the ` Composite.setModified ` method .
*
* @ property isModified
* @ type boolean
* @ default false
2014-05-01 09:09:06 -04:00
* /
2014-06-09 14:40:24 -04:00
/ * *
* The ` Composite ` that is the parent of this composite . It is automatically managed by the ` Matter.Composite ` methods .
*
* @ property parent
* @ type composite
* @ default null
* /
2014-05-01 09:09:06 -04:00
2014-06-09 14:40:24 -04:00
/ * *
* An array of ` Body ` that are _direct _ children of this composite .
* To add or remove bodies you should use ` Composite.add ` and ` Composite.remove ` methods rather than directly modifying this property .
* If you wish to recursively find all descendants , you should use the ` Composite.allBodies ` method .
*
* @ property bodies
* @ type body [ ]
* @ default [ ]
* /
2014-05-01 09:09:06 -04:00
2014-06-09 14:40:24 -04:00
/ * *
* An array of ` Constraint ` that are _direct _ children of this composite .
* To add or remove constraints you should use ` Composite.add ` and ` Composite.remove ` methods rather than directly modifying this property .
* If you wish to recursively find all descendants , you should use the ` Composite.allConstraints ` method .
*
* @ property constraints
* @ type constraint [ ]
* @ default [ ]
* /
/ * *
* An array of ` Composite ` that are _direct _ children of this composite .
* To add or remove composites you should use ` Composite.add ` and ` Composite.remove ` methods rather than directly modifying this property .
* If you wish to recursively find all descendants , you should use the ` Composite.allComposites ` method .
*
* @ property composites
* @ type composite [ ]
* @ default [ ]
* /
2014-05-01 09:09:06 -04:00
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/body/Composite.js
// Begin src/body/World.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.World ` module contains methods for creating and manipulating the world composite .
* A ` Matter.World ` is a ` Matter.Composite ` body , which is a collection of ` Matter.Body ` , ` Matter.Constraint ` and other ` Matter.Composite ` .
* A ` Matter.World ` has a few additional properties including ` gravity ` and ` bounds ` .
* It is important to use the functions in the ` Matter.Composite ` module to modify the world composite , rather than directly modifying its properties .
* There are also a few methods here that alias those in ` Matter.Composite ` for easier readability .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class World
* /
2014-02-19 09:15:05 -05:00
var World = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new world composite . The options parameter is an object that specifies any properties you wish to override the defaults .
* See the properites section below for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method create
* @ constructor
* @ param { } options
* @ return { world } A new world
* /
2014-02-19 09:15:05 -05:00
World . create = function ( options ) {
2014-03-24 16:11:42 -04:00
var composite = Composite . create ( ) ;
2014-02-19 09:15:05 -05:00
var defaults = {
2014-05-01 09:09:06 -04:00
label : 'World' ,
2014-02-19 09:15:05 -05:00
gravity : { x : 0 , y : 1 } ,
bounds : {
min : { x : 0 , y : 0 } ,
max : { x : 800 , y : 600 }
}
} ;
2014-03-24 16:11:42 -04:00
return Common . extend ( composite , defaults , options ) ;
2014-02-19 09:15:05 -05:00
} ;
2014-03-24 16:11:42 -04:00
// World is a Composite body
// see src/module/Outro.js for these aliases:
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* An alias for Composite . clear since World is also a Composite
2014-02-28 20:10:08 -05:00
* @ method clear
* @ param { world } world
* @ param { boolean } keepStatic
* /
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* An alias for Composite . add since World is also a Composite
2014-02-28 20:10:08 -05:00
* @ method addComposite
* @ param { world } world
* @ param { composite } composite
* @ return { world } The original world with the objects from composite added
* /
/ * *
2014-06-09 14:40:24 -04:00
* An alias for Composite . addBody since World is also a Composite
2014-02-28 20:10:08 -05:00
* @ method addBody
* @ param { world } world
* @ param { body } body
* @ return { world } The original world with the body added
* /
/ * *
2014-06-09 14:40:24 -04:00
* An alias for Composite . addConstraint since World is also a Composite
2014-02-28 20:10:08 -05:00
* @ method addConstraint
* @ param { world } world
* @ param { constraint } constraint
* @ return { world } The original world with the constraint added
* /
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/body/World.js
// Begin src/collision/Contact.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Contact
* /
2014-02-19 09:15:05 -05:00
var Contact = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method create
* @ param { vertex } vertex
* @ return { contact } A new contact
* /
2014-02-19 09:15:05 -05:00
Contact . create = function ( vertex ) {
return {
id : Contact . id ( vertex ) ,
vertex : vertex ,
normalImpulse : 0 ,
tangentImpulse : 0
} ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method id
* @ param { vertex } vertex
* @ return { Number } Unique contactID
* /
2014-02-19 09:15:05 -05:00
Contact . id = function ( vertex ) {
return vertex . body . id + '_' + vertex . index ;
} ;
} ) ( ) ;
; // End src/collision/Contact.js
// Begin src/collision/Detector.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Detector
* /
2014-02-19 09:15:05 -05:00
// TODO: speculative contacts
var Detector = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method collisions
2014-03-22 13:51:49 -04:00
* @ param { pair [ ] } broadphasePairs
* @ param { engine } engine
2014-02-28 20:10:08 -05:00
* @ return { array } collisions
* /
2014-03-22 13:51:49 -04:00
Detector . collisions = function ( broadphasePairs , engine ) {
var collisions = [ ] ,
metrics = engine . metrics ,
pairsTable = engine . pairs . table ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
for ( var i = 0 ; i < broadphasePairs . length ; i ++ ) {
var bodyA = broadphasePairs [ i ] [ 0 ] ,
bodyB = broadphasePairs [ i ] [ 1 ] ;
2014-02-19 09:15:05 -05:00
// NOTE: could share a function for the below, but may drop performance?
if ( bodyA . groupId && bodyB . groupId && bodyA . groupId === bodyB . groupId )
continue ;
if ( ( bodyA . isStatic || bodyA . isSleeping ) && ( bodyB . isStatic || bodyB . isSleeping ) )
continue ;
metrics . midphaseTests += 1 ;
// mid phase
if ( Bounds . overlaps ( bodyA . bounds , bodyB . bounds ) ) {
2014-03-22 13:51:49 -04:00
// find a previous collision we could reuse
var pairId = Pair . id ( bodyA , bodyB ) ,
2014-03-30 14:45:30 -04:00
pair = pairsTable [ pairId ] ,
2014-03-22 13:51:49 -04:00
previousCollision ;
if ( pair && pair . isActive ) {
previousCollision = pair . collision ;
} else {
previousCollision = null ;
}
2014-02-19 09:15:05 -05:00
// narrow phase
2014-03-22 13:51:49 -04:00
var collision = SAT . collides ( bodyA , bodyB , previousCollision ) ;
2014-02-19 09:15:05 -05:00
metrics . narrowphaseTests += 1 ;
2014-03-22 13:51:49 -04:00
if ( collision . reused )
metrics . narrowReuseCount += 1 ;
2014-02-19 09:15:05 -05:00
if ( collision . collided ) {
collisions . push ( collision ) ;
metrics . narrowDetections += 1 ;
}
}
}
return collisions ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method bruteForce
* @ param { body [ ] } bodies
2014-03-22 13:51:49 -04:00
* @ param { engine } engine
2014-02-28 20:10:08 -05:00
* @ return { array } collisions
* /
2014-03-22 13:51:49 -04:00
Detector . bruteForce = function ( bodies , engine ) {
var collisions = [ ] ,
metrics = engine . metrics ,
pairsTable = engine . pairs . table ;
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < bodies . length ; i ++ ) {
for ( var j = i + 1 ; j < bodies . length ; j ++ ) {
var bodyA = bodies [ i ] ,
bodyB = bodies [ j ] ;
// NOTE: could share a function for the below, but may drop performance?
if ( bodyA . groupId && bodyB . groupId && bodyA . groupId === bodyB . groupId )
continue ;
if ( ( bodyA . isStatic || bodyA . isSleeping ) && ( bodyB . isStatic || bodyB . isSleeping ) )
continue ;
metrics . midphaseTests += 1 ;
// mid phase
if ( Bounds . overlaps ( bodyA . bounds , bodyB . bounds ) ) {
2014-03-22 13:51:49 -04:00
// find a previous collision we could reuse
var pairId = Pair . id ( bodyA , bodyB ) ,
2014-03-30 14:45:30 -04:00
pair = pairsTable [ pairId ] ,
2014-03-22 13:51:49 -04:00
previousCollision ;
if ( pair && pair . isActive ) {
previousCollision = pair . collision ;
} else {
previousCollision = null ;
}
2014-02-19 09:15:05 -05:00
// narrow phase
2014-03-22 13:51:49 -04:00
var collision = SAT . collides ( bodyA , bodyB , previousCollision ) ;
2014-02-19 09:15:05 -05:00
metrics . narrowphaseTests += 1 ;
2014-03-22 13:51:49 -04:00
if ( collision . reused )
metrics . narrowReuseCount += 1 ;
2014-02-19 09:15:05 -05:00
if ( collision . collided ) {
collisions . push ( collision ) ;
metrics . narrowDetections += 1 ;
}
}
}
}
return collisions ;
} ;
} ) ( ) ;
; // End src/collision/Detector.js
// Begin src/collision/Grid.js
2014-02-28 20:10:08 -05:00
/ * *
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Grid
* /
2014-02-19 09:15:05 -05:00
var Grid = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method create
* @ param { number } bucketWidth
* @ param { number } bucketHeight
* @ return { grid } A new grid
* /
2014-02-19 09:15:05 -05:00
Grid . create = function ( bucketWidth , bucketHeight ) {
return {
buckets : { } ,
pairs : { } ,
pairsList : [ ] ,
bucketWidth : bucketWidth || 48 ,
bucketHeight : bucketHeight || 48
} ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method update
* @ param { grid } grid
* @ param { body [ ] } bodies
* @ param { engine } engine
* @ param { boolean } forceUpdate
* /
2014-02-19 09:15:05 -05:00
Grid . update = function ( grid , bodies , engine , forceUpdate ) {
var i , col , row ,
world = engine . world ,
buckets = grid . buckets ,
bucket ,
bucketId ,
metrics = engine . metrics ,
gridChanged = false ;
metrics . broadphaseTests = 0 ;
for ( i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
2014-03-24 16:11:42 -04:00
if ( body . isSleeping && ! forceUpdate )
2014-02-19 09:15:05 -05:00
continue ;
// don't update out of world bodies
if ( body . bounds . max . x < 0 || body . bounds . min . x > world . bounds . width
|| body . bounds . max . y < 0 || body . bounds . min . y > world . bounds . height )
continue ;
var newRegion = _getRegion ( grid , body ) ;
// if the body has changed grid region
if ( ! body . region || newRegion . id !== body . region . id || forceUpdate ) {
metrics . broadphaseTests += 1 ;
if ( ! body . region || forceUpdate )
body . region = newRegion ;
var union = _regionUnion ( newRegion , body . region ) ;
// update grid buckets affected by region change
// iterate over the union of both regions
for ( col = union . startCol ; col <= union . endCol ; col ++ ) {
for ( row = union . startRow ; row <= union . endRow ; row ++ ) {
bucketId = _getBucketId ( col , row ) ;
bucket = buckets [ bucketId ] ;
var isInsideNewRegion = ( col >= newRegion . startCol && col <= newRegion . endCol
&& row >= newRegion . startRow && row <= newRegion . endRow ) ;
var isInsideOldRegion = ( col >= body . region . startCol && col <= body . region . endCol
&& row >= body . region . startRow && row <= body . region . endRow ) ;
// remove from old region buckets
if ( ! isInsideNewRegion && isInsideOldRegion ) {
if ( isInsideOldRegion ) {
if ( bucket )
_bucketRemoveBody ( grid , bucket , body ) ;
}
}
// add to new region buckets
if ( body . region === newRegion || ( isInsideNewRegion && ! isInsideOldRegion ) || forceUpdate ) {
if ( ! bucket )
bucket = _createBucket ( buckets , bucketId ) ;
_bucketAddBody ( grid , bucket , body ) ;
}
}
}
// set the new region
body . region = newRegion ;
// flag changes so we can update pairs
gridChanged = true ;
}
}
// update pairs list only if pairs changed (i.e. a body changed region)
if ( gridChanged )
grid . pairsList = _createActivePairsList ( grid ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method clear
* @ param { grid } grid
* /
2014-02-19 09:15:05 -05:00
Grid . clear = function ( grid ) {
grid . buckets = { } ;
grid . pairs = { } ;
grid . pairsList = [ ] ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _regionUnion
* @ private
* @ param { } regionA
* @ param { } regionB
* @ return CallExpression
* /
2014-02-19 09:15:05 -05:00
var _regionUnion = function ( regionA , regionB ) {
var startCol = Math . min ( regionA . startCol , regionB . startCol ) ,
endCol = Math . max ( regionA . endCol , regionB . endCol ) ,
startRow = Math . min ( regionA . startRow , regionB . startRow ) ,
endRow = Math . max ( regionA . endRow , regionB . endRow ) ;
return _createRegion ( startCol , endCol , startRow , endRow ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _getRegion
* @ private
* @ param { } grid
* @ param { } body
* @ return CallExpression
* /
2014-02-19 09:15:05 -05:00
var _getRegion = function ( grid , body ) {
var bounds = body . bounds ,
startCol = Math . floor ( bounds . min . x / grid . bucketWidth ) ,
endCol = Math . floor ( bounds . max . x / grid . bucketWidth ) ,
startRow = Math . floor ( bounds . min . y / grid . bucketHeight ) ,
endRow = Math . floor ( bounds . max . y / grid . bucketHeight ) ;
return _createRegion ( startCol , endCol , startRow , endRow ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _createRegion
* @ private
* @ param { } startCol
* @ param { } endCol
* @ param { } startRow
* @ param { } endRow
* @ return ObjectExpression
* /
2014-02-19 09:15:05 -05:00
var _createRegion = function ( startCol , endCol , startRow , endRow ) {
return {
id : startCol + ',' + endCol + ',' + startRow + ',' + endRow ,
startCol : startCol ,
endCol : endCol ,
startRow : startRow ,
endRow : endRow
} ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _getBucketId
* @ private
* @ param { } column
* @ param { } row
* @ return BinaryExpression
* /
2014-02-19 09:15:05 -05:00
var _getBucketId = function ( column , row ) {
return column + ',' + row ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _createBucket
* @ private
* @ param { } buckets
* @ param { } bucketId
* @ return bucket
* /
2014-02-19 09:15:05 -05:00
var _createBucket = function ( buckets , bucketId ) {
var bucket = buckets [ bucketId ] = [ ] ;
return bucket ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _bucketAddBody
* @ private
* @ param { } grid
* @ param { } bucket
* @ param { } body
* /
2014-02-19 09:15:05 -05:00
var _bucketAddBody = function ( grid , bucket , body ) {
// add new pairs
for ( var i = 0 ; i < bucket . length ; i ++ ) {
var bodyB = bucket [ i ] ;
if ( body . id === bodyB . id || ( body . isStatic && bodyB . isStatic ) )
continue ;
// keep track of the number of buckets the pair exists in
// important for Grid.update to work
2014-03-22 13:51:49 -04:00
var pairId = Pair . id ( body , bodyB ) ,
pair = grid . pairs [ pairId ] ;
if ( pair ) {
pair [ 2 ] += 1 ;
2014-02-19 09:15:05 -05:00
} else {
grid . pairs [ pairId ] = [ body , bodyB , 1 ] ;
}
}
// add to bodies (after pairs, otherwise pairs with self)
bucket . push ( body ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _bucketRemoveBody
* @ private
* @ param { } grid
* @ param { } bucket
* @ param { } body
* /
2014-02-19 09:15:05 -05:00
var _bucketRemoveBody = function ( grid , bucket , body ) {
2014-03-22 13:51:49 -04:00
// remove from bucket
bucket . splice ( bucket . indexOf ( body ) , 1 ) ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
// update pair counts
for ( var i = 0 ; i < bucket . length ; i ++ ) {
// keep track of the number of buckets the pair exists in
// important for _createActivePairsList to work
var bodyB = bucket [ i ] ,
pairId = Pair . id ( body , bodyB ) ,
pair = grid . pairs [ pairId ] ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( pair )
pair [ 2 ] -= 1 ;
2014-02-19 09:15:05 -05:00
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _createActivePairsList
* @ private
* @ param { } grid
* @ return pairs
* /
2014-02-19 09:15:05 -05:00
var _createActivePairsList = function ( grid ) {
var pairKeys ,
pair ,
pairs = [ ] ;
// grid.pairs is used as a hashmap
pairKeys = Common . keys ( grid . pairs ) ;
// iterate over grid.pairs
for ( var k = 0 ; k < pairKeys . length ; k ++ ) {
pair = grid . pairs [ pairKeys [ k ] ] ;
// if pair exists in at least one bucket
// it is a pair that needs further collision testing so push it
2014-03-22 13:51:49 -04:00
if ( pair [ 2 ] > 0 ) {
2014-02-19 09:15:05 -05:00
pairs . push ( pair ) ;
2014-03-22 13:51:49 -04:00
} else {
delete grid . pairs [ pairKeys [ k ] ] ;
}
2014-02-19 09:15:05 -05:00
}
return pairs ;
} ;
} ) ( ) ;
; // End src/collision/Grid.js
2014-03-24 16:11:42 -04:00
// Begin src/collision/Pair.js
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Pair
* /
var Pair = { } ;
( function ( ) {
/ * *
* Description
* @ method create
* @ param { collision } collision
* @ return { pair } A new pair
* /
Pair . create = function ( collision , timestamp ) {
var bodyA = collision . bodyA ,
bodyB = collision . bodyB ;
var pair = {
id : Pair . id ( bodyA , bodyB ) ,
bodyA : bodyA ,
bodyB : bodyB ,
contacts : { } ,
activeContacts : [ ] ,
separation : 0 ,
isActive : true ,
timeCreated : timestamp ,
timeUpdated : timestamp ,
inverseMass : bodyA . inverseMass + bodyB . inverseMass ,
friction : Math . min ( bodyA . friction , bodyB . friction ) ,
restitution : Math . max ( bodyA . restitution , bodyB . restitution ) ,
slop : Math . max ( bodyA . slop , bodyB . slop )
} ;
Pair . update ( pair , collision , timestamp ) ;
return pair ;
} ;
/ * *
* Description
* @ method update
* @ param { pair } pair
* @ param { collision } collision
* /
Pair . update = function ( pair , collision , timestamp ) {
var contacts = pair . contacts ,
supports = collision . supports ,
activeContacts = pair . activeContacts ;
pair . collision = collision ;
activeContacts . length = 0 ;
if ( collision . collided ) {
for ( var i = 0 ; i < supports . length ; i ++ ) {
var support = supports [ i ] ,
2014-03-30 14:45:30 -04:00
contactId = Contact . id ( support ) ,
contact = contacts [ contactId ] ;
2014-03-24 16:11:42 -04:00
2014-03-30 14:45:30 -04:00
if ( contact ) {
activeContacts . push ( contact ) ;
2014-03-24 16:11:42 -04:00
} else {
activeContacts . push ( contacts [ contactId ] = Contact . create ( support ) ) ;
}
}
pair . separation = collision . depth ;
Pair . setActive ( pair , true , timestamp ) ;
} else {
if ( pair . isActive === true )
Pair . setActive ( pair , false , timestamp ) ;
}
} ;
/ * *
* Description
* @ method setActive
* @ param { pair } pair
* @ param { bool } isActive
* /
Pair . setActive = function ( pair , isActive , timestamp ) {
if ( isActive ) {
pair . isActive = true ;
pair . timeUpdated = timestamp ;
} else {
pair . isActive = false ;
pair . activeContacts . length = 0 ;
}
} ;
/ * *
* Description
* @ method id
* @ param { body } bodyA
* @ param { body } bodyB
* @ return { number } Unique pairId
* /
Pair . id = function ( bodyA , bodyB ) {
if ( bodyA . id < bodyB . id ) {
return bodyA . id + '_' + bodyB . id ;
} else {
return bodyB . id + '_' + bodyA . id ;
}
} ;
} ) ( ) ;
; // End src/collision/Pair.js
// Begin src/collision/Pairs.js
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
2014-03-24 16:11:42 -04:00
* @ class Pairs
2014-02-28 20:10:08 -05:00
* /
2014-03-24 16:11:42 -04:00
var Pairs = { } ;
2014-02-19 09:15:05 -05:00
( function ( ) {
2014-03-11 06:29:14 -04:00
var _pairMaxIdleLife = 1000 ;
2014-02-19 09:15:05 -05:00
2014-03-24 16:11:42 -04:00
/ * *
* Creates a new pairs structure
* @ method create
* @ param { object } options
* @ return { pairs } A new pairs structure
* /
Pairs . create = function ( options ) {
return Common . extend ( {
table : { } ,
list : [ ] ,
collisionStart : [ ] ,
collisionActive : [ ] ,
collisionEnd : [ ]
} , options ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-03-24 16:11:42 -04:00
* @ method update
2014-02-28 20:10:08 -05:00
* @ param { object } pairs
2014-03-11 06:29:14 -04:00
* @ param { collision [ ] } collisions
2014-02-28 20:10:08 -05:00
* /
2014-03-24 16:11:42 -04:00
Pairs . update = function ( pairs , collisions , timestamp ) {
2014-03-11 06:29:14 -04:00
var pairsList = pairs . list ,
pairsTable = pairs . table ,
collisionStart = pairs . collisionStart ,
collisionEnd = pairs . collisionEnd ,
collisionActive = pairs . collisionActive ,
activePairIds = [ ] ,
collision ,
pairId ,
pair ,
i ;
2014-02-19 09:15:05 -05:00
2014-03-11 06:29:14 -04:00
// clear collision state arrays, but maintain old reference
collisionStart . length = 0 ;
collisionEnd . length = 0 ;
collisionActive . length = 0 ;
2014-02-19 09:15:05 -05:00
for ( i = 0 ; i < collisions . length ; i ++ ) {
2014-03-11 06:29:14 -04:00
collision = collisions [ i ] ;
if ( collision . collided ) {
2014-02-19 09:15:05 -05:00
pairId = Pair . id ( collision . bodyA , collision . bodyB ) ;
2014-03-11 06:29:14 -04:00
activePairIds . push ( pairId ) ;
2014-03-30 14:45:30 -04:00
pair = pairsTable [ pairId ] ;
2014-03-11 06:29:14 -04:00
2014-03-30 14:45:30 -04:00
if ( pair ) {
2014-03-11 06:29:14 -04:00
// pair already exists (but may or may not be active)
if ( pair . isActive ) {
// pair exists and is active
collisionActive . push ( pair ) ;
} else {
// pair exists but was inactive, so a collision has just started again
collisionStart . push ( pair ) ;
}
// update the pair
2014-03-22 13:51:49 -04:00
Pair . update ( pair , collision , timestamp ) ;
2014-03-11 06:29:14 -04:00
} else {
// pair did not exist, create a new pair
2014-03-22 13:51:49 -04:00
pair = Pair . create ( collision , timestamp ) ;
2014-03-11 06:29:14 -04:00
pairsTable [ pairId ] = pair ;
// push the new pair
collisionStart . push ( pair ) ;
pairsList . push ( pair ) ;
}
}
}
// deactivate previously active pairs that are now inactive
for ( i = 0 ; i < pairsList . length ; i ++ ) {
pair = pairsList [ i ] ;
if ( pair . isActive && activePairIds . indexOf ( pair . id ) === - 1 ) {
2014-03-22 13:51:49 -04:00
Pair . setActive ( pair , false , timestamp ) ;
2014-03-11 06:29:14 -04:00
collisionEnd . push ( pair ) ;
2014-02-19 09:15:05 -05:00
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-03-24 16:11:42 -04:00
* @ method removeOld
2014-02-28 20:10:08 -05:00
* @ param { object } pairs
* /
2014-03-24 16:11:42 -04:00
Pairs . removeOld = function ( pairs , timestamp ) {
2014-03-11 06:29:14 -04:00
var pairsList = pairs . list ,
pairsTable = pairs . table ,
indexesToRemove = [ ] ,
pair ,
collision ,
pairIndex ,
2014-02-19 09:15:05 -05:00
i ;
2014-03-11 06:29:14 -04:00
2014-02-19 09:15:05 -05:00
for ( i = 0 ; i < pairsList . length ; i ++ ) {
2014-03-11 06:29:14 -04:00
pair = pairsList [ i ] ;
collision = pair . collision ;
2014-02-19 09:15:05 -05:00
// never remove sleeping pairs
if ( collision . bodyA . isSleeping || collision . bodyB . isSleeping ) {
2014-03-22 13:51:49 -04:00
pair . timeUpdated = timestamp ;
2014-02-19 09:15:05 -05:00
continue ;
}
2014-03-11 06:29:14 -04:00
// if pair is inactive for too long, mark it to be removed
2014-03-22 13:51:49 -04:00
if ( timestamp - pair . timeUpdated > _pairMaxIdleLife ) {
2014-03-11 06:29:14 -04:00
indexesToRemove . push ( i ) ;
2014-02-19 09:15:05 -05:00
}
}
2014-03-11 06:29:14 -04:00
// remove marked pairs
for ( i = 0 ; i < indexesToRemove . length ; i ++ ) {
2014-03-22 13:51:49 -04:00
pairIndex = indexesToRemove [ i ] - i ;
2014-03-11 06:29:14 -04:00
pair = pairsList [ pairIndex ] ;
delete pairsTable [ pair . id ] ;
pairsList . splice ( pairIndex , 1 ) ;
}
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-03-24 16:11:42 -04:00
* Clears the given pairs structure
2014-02-28 20:10:08 -05:00
* @ method create
2014-03-24 16:11:42 -04:00
* @ param { object } options
* @ param { pairs } pairs
* /
Pairs . clear = function ( pairs ) {
pairs . table = { } ;
pairs . list . length = 0 ;
pairs . collisionStart . length = 0 ;
pairs . collisionActive . length = 0 ;
pairs . collisionEnd . length = 0 ;
return pairs ;
2014-02-19 09:15:05 -05:00
} ;
} ) ( ) ;
2014-03-24 16:11:42 -04:00
; // End src/collision/Pairs.js
2014-02-19 09:15:05 -05:00
2014-05-01 09:09:06 -04:00
// Begin src/collision/Query.js
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Query ` module contains methods for performing collision queries .
2014-05-01 09:09:06 -04:00
*
* @ class Query
* /
var Query = { } ;
( function ( ) {
/ * *
* Casts a ray segment against a set of bodies and returns all collisions , ray width is optional . Intersection points are not provided .
* @ method ray
* @ param { body [ ] } bodies
* @ param { vector } startPoint
* @ param { vector } endPoint
2014-06-09 14:40:24 -04:00
* @ param { number } [ rayWidth ]
2014-05-01 09:09:06 -04:00
* @ return { object [ ] } Collisions
* /
Query . ray = function ( bodies , startPoint , endPoint , rayWidth ) {
rayWidth = rayWidth || Number . MIN _VALUE ;
var rayAngle = Vector . angle ( startPoint , endPoint ) ,
rayLength = Vector . magnitude ( Vector . sub ( startPoint , endPoint ) ) ,
rayX = ( endPoint . x + startPoint . x ) * 0.5 ,
rayY = ( endPoint . y + startPoint . y ) * 0.5 ,
ray = Bodies . rectangle ( rayX , rayY , rayLength , rayWidth , { angle : rayAngle } ) ,
collisions = [ ] ;
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var bodyA = bodies [ i ] ;
if ( Bounds . overlaps ( bodyA . bounds , ray . bounds ) ) {
var collision = SAT . collides ( bodyA , ray ) ;
if ( collision . collided ) {
collision . body = collision . bodyA = collision . bodyB = bodyA ;
collisions . push ( collision ) ;
}
}
}
return collisions ;
} ;
/ * *
2014-06-09 14:40:24 -04:00
* Returns all bodies whose bounds are inside ( or outside if set ) the given set of bounds , from the given set of bodies .
2014-05-01 09:09:06 -04:00
* @ method region
* @ param { body [ ] } bodies
* @ param { bounds } bounds
2014-06-09 14:40:24 -04:00
* @ param { bool } [ outside = false ]
2014-05-01 09:09:06 -04:00
* @ return { body [ ] } The bodies matching the query
* /
Query . region = function ( bodies , bounds , outside ) {
var result = [ ] ;
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ,
overlaps = Bounds . overlaps ( body . bounds , bounds ) ;
if ( ( overlaps && ! outside ) || ( ! overlaps && outside ) )
result . push ( body ) ;
}
return result ;
} ;
} ) ( ) ;
; // End src/collision/Query.js
2014-02-19 09:15:05 -05:00
// Begin src/collision/Resolver.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Resolver
* /
2014-02-19 09:15:05 -05:00
var Resolver = { } ;
( function ( ) {
var _restingThresh = 4 ,
_positionDampen = 0.2 ,
_positionWarming = 0.6 ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method solvePosition
* @ param { pair [ ] } pairs
2014-05-01 09:09:06 -04:00
* @ param { number } timeScale
2014-02-28 20:10:08 -05:00
* /
2014-05-01 09:09:06 -04:00
Resolver . solvePosition = function ( pairs , timeScale ) {
2014-02-19 09:15:05 -05:00
var i ,
pair ,
collision ,
bodyA ,
bodyB ,
vertex ,
vertexCorrected ,
normal ,
bodyBtoA ;
// find impulses required to resolve penetration
for ( i = 0 ; i < pairs . length ; i ++ ) {
pair = pairs [ i ] ;
if ( ! pair . isActive )
continue ;
collision = pair . collision ;
bodyA = collision . bodyA ;
bodyB = collision . bodyB ;
vertex = collision . supports [ 0 ] ;
vertexCorrected = collision . supportCorrected ;
normal = collision . normal ;
// get current separation between body edges involved in collision
bodyBtoA = Vector . sub ( Vector . add ( bodyB . positionImpulse , vertex ) ,
Vector . add ( bodyA . positionImpulse , vertexCorrected ) ) ;
pair . separation = Vector . dot ( normal , bodyBtoA ) ;
}
for ( i = 0 ; i < pairs . length ; i ++ ) {
pair = pairs [ i ] ;
if ( ! pair . isActive )
continue ;
collision = pair . collision ;
bodyA = collision . bodyA ;
bodyB = collision . bodyB ;
normal = collision . normal ;
2014-05-01 09:09:06 -04:00
positionImpulse = ( ( pair . separation * _positionDampen ) - pair . slop ) * timeScale ;
2014-02-19 09:15:05 -05:00
if ( bodyA . isStatic || bodyB . isStatic )
positionImpulse *= 2 ;
if ( ! ( bodyA . isStatic || bodyA . isSleeping ) ) {
bodyA . positionImpulse . x += normal . x * positionImpulse ;
bodyA . positionImpulse . y += normal . y * positionImpulse ;
}
if ( ! ( bodyB . isStatic || bodyB . isSleeping ) ) {
bodyB . positionImpulse . x -= normal . x * positionImpulse ;
bodyB . positionImpulse . y -= normal . y * positionImpulse ;
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method postSolvePosition
* @ param { body [ ] } bodies
* /
2014-02-19 09:15:05 -05:00
Resolver . postSolvePosition = function ( bodies ) {
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( body . positionImpulse . x !== 0 || body . positionImpulse . y !== 0 ) {
// move the body without changing velocity
body . position . x += body . positionImpulse . x ;
body . position . y += body . positionImpulse . y ;
body . positionPrev . x += body . positionImpulse . x ;
body . positionPrev . y += body . positionImpulse . y ;
// update body geometry
Vertices . translate ( body . vertices , body . positionImpulse ) ;
Bounds . update ( body . bounds , body . vertices , body . velocity ) ;
// dampen accumulator to warm the next step
body . positionImpulse . x *= _positionWarming ;
body . positionImpulse . y *= _positionWarming ;
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method preSolveVelocity
* @ param { pair [ ] } pairs
* /
2014-02-19 09:15:05 -05:00
Resolver . preSolveVelocity = function ( pairs ) {
var impulse = { } ,
i ,
j ,
pair ,
contacts ,
collision ,
bodyA ,
bodyB ,
normal ,
tangent ,
contact ,
contactVertex ,
normalImpulse ,
tangentImpulse ,
offset ;
for ( i = 0 ; i < pairs . length ; i ++ ) {
pair = pairs [ i ] ;
if ( ! pair . isActive )
continue ;
contacts = pair . activeContacts ;
collision = pair . collision ;
bodyA = collision . bodyA ;
bodyB = collision . bodyB ;
normal = collision . normal ;
tangent = collision . tangent ;
// resolve each contact
for ( j = 0 ; j < contacts . length ; j ++ ) {
contact = contacts [ j ] ;
contactVertex = contact . vertex ;
normalImpulse = contact . normalImpulse ;
tangentImpulse = contact . tangentImpulse ;
// total impulse from contact
impulse . x = ( normal . x * normalImpulse ) + ( tangent . x * tangentImpulse ) ;
impulse . y = ( normal . y * normalImpulse ) + ( tangent . y * tangentImpulse ) ;
// apply impulse from contact
if ( ! ( bodyA . isStatic || bodyA . isSleeping ) ) {
offset = Vector . sub ( contactVertex , bodyA . position ) ;
bodyA . positionPrev . x += impulse . x * bodyA . inverseMass ;
bodyA . positionPrev . y += impulse . y * bodyA . inverseMass ;
bodyA . anglePrev += Vector . cross ( offset , impulse ) * bodyA . inverseInertia ;
}
if ( ! ( bodyB . isStatic || bodyB . isSleeping ) ) {
offset = Vector . sub ( contactVertex , bodyB . position ) ;
bodyB . positionPrev . x -= impulse . x * bodyB . inverseMass ;
bodyB . positionPrev . y -= impulse . y * bodyB . inverseMass ;
bodyB . anglePrev -= Vector . cross ( offset , impulse ) * bodyB . inverseInertia ;
}
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method solveVelocity
* @ param { pair [ ] } pairs
* /
2014-05-05 14:32:51 -04:00
Resolver . solveVelocity = function ( pairs , timeScale ) {
var impulse = { } ,
timeScaleSquared = timeScale * timeScale ;
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < pairs . length ; i ++ ) {
var pair = pairs [ i ] ;
if ( ! pair . isActive )
continue ;
var collision = pair . collision ,
bodyA = collision . bodyA ,
bodyB = collision . bodyB ,
normal = collision . normal ,
tangent = collision . tangent ,
contacts = pair . activeContacts ,
contactShare = 1 / contacts . length ;
// update body velocities
bodyA . velocity . x = bodyA . position . x - bodyA . positionPrev . x ;
bodyA . velocity . y = bodyA . position . y - bodyA . positionPrev . y ;
bodyB . velocity . x = bodyB . position . x - bodyB . positionPrev . x ;
bodyB . velocity . y = bodyB . position . y - bodyB . positionPrev . y ;
bodyA . angularVelocity = bodyA . angle - bodyA . anglePrev ;
bodyB . angularVelocity = bodyB . angle - bodyB . anglePrev ;
// resolve each contact
for ( var j = 0 ; j < contacts . length ; j ++ ) {
var contact = contacts [ j ] ,
contactVertex = contact . vertex ,
offsetA = Vector . sub ( contactVertex , bodyA . position ) ,
offsetB = Vector . sub ( contactVertex , bodyB . position ) ,
velocityPointA = Vector . add ( bodyA . velocity , Vector . mult ( Vector . perp ( offsetA ) , bodyA . angularVelocity ) ) ,
velocityPointB = Vector . add ( bodyB . velocity , Vector . mult ( Vector . perp ( offsetB ) , bodyB . angularVelocity ) ) ,
relativeVelocity = Vector . sub ( velocityPointA , velocityPointB ) ,
normalVelocity = Vector . dot ( normal , relativeVelocity ) ;
var tangentVelocity = Vector . dot ( tangent , relativeVelocity ) ,
tangentSpeed = Math . abs ( tangentVelocity ) ,
tangentVelocityDirection = Common . sign ( tangentVelocity ) ;
// raw impulses
var normalImpulse = ( 1 + pair . restitution ) * normalVelocity ,
normalForce = Common . clamp ( pair . separation + normalVelocity , 0 , 1 ) ;
// coulomb friction
var tangentImpulse = tangentVelocity ;
2014-05-05 14:32:51 -04:00
if ( tangentSpeed > normalForce * pair . friction * timeScaleSquared )
tangentImpulse = normalForce * pair . friction * timeScaleSquared * tangentVelocityDirection ;
2014-02-19 09:15:05 -05:00
// modify impulses accounting for mass, inertia and offset
var oAcN = Vector . cross ( offsetA , normal ) ,
oBcN = Vector . cross ( offsetB , normal ) ,
share = contactShare / ( pair . inverseMass + bodyA . inverseInertia * oAcN * oAcN + bodyB . inverseInertia * oBcN * oBcN ) ;
normalImpulse *= share ;
tangentImpulse *= share ;
// handle high velocity and resting collisions separately
2014-05-05 14:32:51 -04:00
if ( normalVelocity < 0 && normalVelocity * normalVelocity > _restingThresh * timeScaleSquared ) {
2014-02-19 09:15:05 -05:00
// high velocity so clear cached contact impulse
contact . normalImpulse = 0 ;
contact . tangentImpulse = 0 ;
} else {
// solve resting collision constraints using Erin Catto's method (GDC08)
// impulse constraint, tends to 0
var contactNormalImpulse = contact . normalImpulse ;
contact . normalImpulse = Math . min ( contact . normalImpulse + normalImpulse , 0 ) ;
normalImpulse = contact . normalImpulse - contactNormalImpulse ;
// tangent impulse, tends to -maxFriction or maxFriction
var contactTangentImpulse = contact . tangentImpulse ;
contact . tangentImpulse = Common . clamp ( contact . tangentImpulse + tangentImpulse , - tangentSpeed , tangentSpeed ) ;
tangentImpulse = contact . tangentImpulse - contactTangentImpulse ;
}
// total impulse from contact
impulse . x = ( normal . x * normalImpulse ) + ( tangent . x * tangentImpulse ) ;
impulse . y = ( normal . y * normalImpulse ) + ( tangent . y * tangentImpulse ) ;
// apply impulse from contact
if ( ! ( bodyA . isStatic || bodyA . isSleeping ) ) {
bodyA . positionPrev . x += impulse . x * bodyA . inverseMass ;
bodyA . positionPrev . y += impulse . y * bodyA . inverseMass ;
bodyA . anglePrev += Vector . cross ( offsetA , impulse ) * bodyA . inverseInertia ;
}
if ( ! ( bodyB . isStatic || bodyB . isSleeping ) ) {
bodyB . positionPrev . x -= impulse . x * bodyB . inverseMass ;
bodyB . positionPrev . y -= impulse . y * bodyB . inverseMass ;
bodyB . anglePrev -= Vector . cross ( offsetB , impulse ) * bodyB . inverseInertia ;
}
}
}
} ;
} ) ( ) ;
; // End src/collision/Resolver.js
// Begin src/collision/SAT.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class SAT
* /
2014-02-19 09:15:05 -05:00
// TODO: true circles and curves
var SAT = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method collides
* @ param { body } bodyA
* @ param { body } bodyB
2014-03-22 13:51:49 -04:00
* @ param { collision } previousCollision
2014-02-28 20:10:08 -05:00
* @ return { collision } collision
* /
2014-03-22 13:51:49 -04:00
SAT . collides = function ( bodyA , bodyB , previousCollision ) {
2014-02-19 09:15:05 -05:00
var overlapAB ,
overlapBA ,
minOverlap ,
2014-03-22 13:51:49 -04:00
collision ,
prevCol = previousCollision ,
canReusePrevCol = false ;
if ( prevCol ) {
// estimate total motion
var motion = bodyA . speed * bodyA . speed + bodyA . angularSpeed * bodyA . angularSpeed
+ bodyB . speed * bodyB . speed + bodyB . angularSpeed * bodyB . angularSpeed ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
// we may be able to (partially) reuse collision result
// but only safe if collision was resting
canReusePrevCol = prevCol && prevCol . collided && motion < 0.2 ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
// reuse collision object
collision = prevCol ;
} else {
collision = { collided : false , bodyA : bodyA , bodyB : bodyB } ;
}
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( prevCol && canReusePrevCol ) {
// if we can reuse the collision result
// we only need to test the previously found axis
var axes = [ prevCol . bodyA . axes [ prevCol . axisNumber ] ] ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
minOverlap = _overlapAxes ( prevCol . bodyA . vertices , prevCol . bodyB . vertices , axes ) ;
collision . reused = true ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( minOverlap . overlap <= 0 ) {
collision . collided = false ;
return collision ;
}
2014-02-19 09:15:05 -05:00
} else {
2014-03-22 13:51:49 -04:00
// if we can't reuse a result, perform a full SAT test
overlapAB = _overlapAxes ( bodyA . vertices , bodyB . vertices , bodyA . axes ) ;
if ( overlapAB . overlap <= 0 ) {
collision . collided = false ;
return collision ;
}
overlapBA = _overlapAxes ( bodyB . vertices , bodyA . vertices , bodyB . axes ) ;
if ( overlapBA . overlap <= 0 ) {
collision . collided = false ;
return collision ;
}
if ( overlapAB . overlap < overlapBA . overlap ) {
minOverlap = overlapAB ;
collision . bodyA = bodyA ;
collision . bodyB = bodyB ;
} else {
minOverlap = overlapBA ;
collision . bodyA = bodyB ;
collision . bodyB = bodyA ;
}
// important for reuse later
collision . axisNumber = minOverlap . axisNumber ;
2014-02-19 09:15:05 -05:00
}
collision . collided = true ;
collision . normal = minOverlap . axis ;
collision . depth = minOverlap . overlap ;
bodyA = collision . bodyA ;
bodyB = collision . bodyB ;
// ensure normal is facing away from bodyA
if ( Vector . dot ( collision . normal , Vector . sub ( bodyB . position , bodyA . position ) ) > 0 )
collision . normal = Vector . neg ( collision . normal ) ;
collision . tangent = Vector . perp ( collision . normal ) ;
collision . penetration = {
x : collision . normal . x * collision . depth ,
y : collision . normal . y * collision . depth
} ;
// find support points, there is always either exactly one or two
var verticesB = _findSupports ( bodyA , bodyB , collision . normal ) ,
supports = [ verticesB [ 0 ] ] ;
if ( Vertices . contains ( bodyA . vertices , verticesB [ 1 ] ) ) {
supports . push ( verticesB [ 1 ] ) ;
} else {
var verticesA = _findSupports ( bodyB , bodyA , Vector . neg ( collision . normal ) ) ;
if ( Vertices . contains ( bodyB . vertices , verticesA [ 0 ] ) ) {
supports . push ( verticesA [ 0 ] ) ;
}
if ( supports . length < 2 && Vertices . contains ( bodyB . vertices , verticesA [ 1 ] ) ) {
supports . push ( verticesA [ 1 ] ) ;
}
}
collision . supports = supports ;
collision . supportCorrected = Vector . sub ( verticesB [ 0 ] , collision . penetration ) ;
return collision ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _overlapAxes
* @ private
* @ param { } verticesA
* @ param { } verticesB
* @ param { } axes
* @ return result
* /
2014-02-19 09:15:05 -05:00
var _overlapAxes = function ( verticesA , verticesB , axes ) {
var projectionA = { } ,
projectionB = { } ,
result = { overlap : Number . MAX _VALUE } ,
overlap ,
axis ;
for ( var i = 0 ; i < axes . length ; i ++ ) {
axis = axes [ i ] ;
_projectToAxis ( projectionA , verticesA , axis ) ;
_projectToAxis ( projectionB , verticesB , axis ) ;
overlap = projectionA . min < projectionB . min
? projectionA . max - projectionB . min
: projectionB . max - projectionA . min ;
2014-03-22 13:51:49 -04:00
if ( overlap <= 0 ) {
result . overlap = overlap ;
return result ;
}
2014-02-19 09:15:05 -05:00
if ( overlap < result . overlap ) {
result . overlap = overlap ;
result . axis = axis ;
2014-03-22 13:51:49 -04:00
result . axisNumber = i ;
2014-02-19 09:15:05 -05:00
}
}
return result ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _projectToAxis
* @ private
* @ param { } projection
* @ param { } vertices
* @ param { } axis
* /
2014-02-19 09:15:05 -05:00
var _projectToAxis = function ( projection , vertices , axis ) {
var min = Vector . dot ( vertices [ 0 ] , axis ) ,
max = min ;
for ( var i = 1 ; i < vertices . length ; i += 1 ) {
var dot = Vector . dot ( vertices [ i ] , axis ) ;
if ( dot > max ) {
max = dot ;
} else if ( dot < min ) {
min = dot ;
}
}
projection . min = min ;
projection . max = max ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _findSupports
* @ private
* @ param { } bodyA
* @ param { } bodyB
* @ param { } normal
* @ return ArrayExpression
* /
2014-02-19 09:15:05 -05:00
var _findSupports = function ( bodyA , bodyB , normal ) {
var nearestDistance = Number . MAX _VALUE ,
vertexToBody = { x : 0 , y : 0 } ,
vertices = bodyB . vertices ,
bodyAPosition = bodyA . position ,
distance ,
vertex ,
vertexA = vertices [ 0 ] ,
vertexB = vertices [ 1 ] ;
// find closest vertex on bodyB
for ( var i = 0 ; i < vertices . length ; i ++ ) {
vertex = vertices [ i ] ;
vertexToBody . x = vertex . x - bodyAPosition . x ;
vertexToBody . y = vertex . y - bodyAPosition . y ;
distance = - Vector . dot ( normal , vertexToBody ) ;
if ( distance < nearestDistance ) {
nearestDistance = distance ;
vertexA = vertex ;
}
}
// find next closest vertex using the two connected to it
var prevIndex = vertexA . index - 1 >= 0 ? vertexA . index - 1 : vertices . length - 1 ;
vertex = vertices [ prevIndex ] ;
vertexToBody . x = vertex . x - bodyAPosition . x ;
vertexToBody . y = vertex . y - bodyAPosition . y ;
nearestDistance = - Vector . dot ( normal , vertexToBody ) ;
vertexB = vertex ;
var nextIndex = ( vertexA . index + 1 ) % vertices . length ;
vertex = vertices [ nextIndex ] ;
vertexToBody . x = vertex . x - bodyAPosition . x ;
vertexToBody . y = vertex . y - bodyAPosition . y ;
distance = - Vector . dot ( normal , vertexToBody ) ;
if ( distance < nearestDistance ) {
nearestDistance = distance ;
vertexB = vertex ;
}
return [ vertexA , vertexB ] ;
} ;
} ) ( ) ;
; // End src/collision/SAT.js
// Begin src/constraint/Constraint.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Constraint ` module contains methods for creating and manipulating constraints .
* Constraints are used for specifying that a fixed distance must be maintained between two bodies ( or a body and a fixed world - space position ) .
* The stiffness of constraints can be modified to create springs or elastic .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Constraint
* /
2014-02-19 09:15:05 -05:00
// TODO: fix instabillity issues with torque
// TODO: linked constraints
// TODO: breakable constraints
// TODO: collidable constraints
// TODO: allow constrained bodies to sleep
// TODO: handle 0 length constraints properly
// TODO: impulse caching and warming
var Constraint = { } ;
( function ( ) {
2014-03-22 13:51:49 -04:00
var _minLength = 0.000001 ,
2014-05-01 09:09:06 -04:00
_minDifference = 0.001 ;
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new constraint .
* All properties have default values , and many are pre - calculated automatically based on other properties .
* See the properites section below for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method create
* @ param { } options
* @ return { constraint } constraint
* /
2014-02-19 09:15:05 -05:00
Constraint . create = function ( options ) {
var constraint = options ;
// if bodies defined but no points, use body centre
if ( constraint . bodyA && ! constraint . pointA )
constraint . pointA = { x : 0 , y : 0 } ;
if ( constraint . bodyB && ! constraint . pointB )
constraint . pointB = { x : 0 , y : 0 } ;
// calculate static length using initial world space points
var initialPointA = constraint . bodyA ? Vector . add ( constraint . bodyA . position , constraint . pointA ) : constraint . pointA ,
initialPointB = constraint . bodyB ? Vector . add ( constraint . bodyB . position , constraint . pointB ) : constraint . pointB ,
length = Vector . magnitude ( Vector . sub ( initialPointA , initialPointB ) ) ;
constraint . length = constraint . length || length || _minLength ;
2014-03-22 13:51:49 -04:00
// render
var render = {
visible : true ,
lineWidth : 2 ,
strokeStyle : '#666'
} ;
constraint . render = Common . extend ( render , constraint . render ) ;
2014-02-19 09:15:05 -05:00
// option defaults
2014-05-01 09:09:06 -04:00
constraint . id = constraint . id || Common . nextId ( ) ;
constraint . label = constraint . label || 'Constraint' ;
2014-03-30 14:45:30 -04:00
constraint . type = 'constraint' ;
2014-02-19 09:15:05 -05:00
constraint . stiffness = constraint . stiffness || 1 ;
constraint . angularStiffness = constraint . angularStiffness || 0 ;
constraint . angleA = constraint . bodyA ? constraint . bodyA . angle : constraint . angleA ;
constraint . angleB = constraint . bodyB ? constraint . bodyB . angle : constraint . angleB ;
return constraint ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-03-30 14:45:30 -04:00
* @ method solveAll
2014-02-28 20:10:08 -05:00
* @ param { constraint [ ] } constraints
2014-05-01 09:09:06 -04:00
* @ param { number } timeScale
2014-02-28 20:10:08 -05:00
* /
2014-05-01 09:09:06 -04:00
Constraint . solveAll = function ( constraints , timeScale ) {
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < constraints . length ; i ++ ) {
2014-05-01 09:09:06 -04:00
Constraint . solve ( constraints [ i ] , timeScale ) ;
2014-02-19 09:15:05 -05:00
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-03-30 14:45:30 -04:00
* @ method solve
2014-02-28 20:10:08 -05:00
* @ param { constraint } constraint
2014-05-01 09:09:06 -04:00
* @ param { number } timeScale
2014-02-28 20:10:08 -05:00
* /
2014-05-01 09:09:06 -04:00
Constraint . solve = function ( constraint , timeScale ) {
2014-02-19 09:15:05 -05:00
var bodyA = constraint . bodyA ,
bodyB = constraint . bodyB ,
pointA = constraint . pointA ,
pointB = constraint . pointB ;
// update reference angle
if ( bodyA && ! bodyA . isStatic ) {
constraint . pointA = Vector . rotate ( pointA , bodyA . angle - constraint . angleA ) ;
constraint . angleA = bodyA . angle ;
}
// update reference angle
if ( bodyB && ! bodyB . isStatic ) {
constraint . pointB = Vector . rotate ( pointB , bodyB . angle - constraint . angleB ) ;
constraint . angleB = bodyB . angle ;
}
var pointAWorld = pointA ,
pointBWorld = pointB ;
if ( bodyA ) pointAWorld = Vector . add ( bodyA . position , pointA ) ;
if ( bodyB ) pointBWorld = Vector . add ( bodyB . position , pointB ) ;
if ( ! pointAWorld || ! pointBWorld )
return ;
var delta = Vector . sub ( pointAWorld , pointBWorld ) ,
currentLength = Vector . magnitude ( delta ) ;
// prevent singularity
if ( currentLength === 0 )
currentLength = _minLength ;
// solve distance constraint with Gauss-Siedel method
var difference = ( currentLength - constraint . length ) / currentLength ,
normal = Vector . div ( delta , currentLength ) ,
2014-05-01 09:09:06 -04:00
force = Vector . mult ( delta , difference * 0.5 * constraint . stiffness * timeScale * timeScale ) ;
2014-04-01 08:47:17 -04:00
// if difference is very small, we can skip
2014-05-01 09:09:06 -04:00
if ( Math . abs ( 1 - ( currentLength / constraint . length ) ) < _minDifference * timeScale )
2014-04-01 08:47:17 -04:00
return ;
2014-02-19 09:15:05 -05:00
var velocityPointA ,
velocityPointB ,
offsetA ,
offsetB ,
oAn ,
oBn ,
bodyADenom ,
bodyBDenom ;
if ( bodyA && ! bodyA . isStatic ) {
// point body offset
offsetA = {
x : pointAWorld . x - bodyA . position . x + force . x ,
y : pointAWorld . y - bodyA . position . y + force . y
} ;
// update velocity
bodyA . velocity . x = bodyA . position . x - bodyA . positionPrev . x ;
bodyA . velocity . y = bodyA . position . y - bodyA . positionPrev . y ;
bodyA . angularVelocity = bodyA . angle - bodyA . anglePrev ;
// find point velocity and body mass
velocityPointA = Vector . add ( bodyA . velocity , Vector . mult ( Vector . perp ( offsetA ) , bodyA . angularVelocity ) ) ;
oAn = Vector . dot ( offsetA , normal ) ;
bodyADenom = bodyA . inverseMass + bodyA . inverseInertia * oAn * oAn ;
} else {
velocityPointA = { x : 0 , y : 0 } ;
bodyADenom = bodyA ? bodyA . inverseMass : 0 ;
}
if ( bodyB && ! bodyB . isStatic ) {
// point body offset
offsetB = {
x : pointBWorld . x - bodyB . position . x - force . x ,
y : pointBWorld . y - bodyB . position . y - force . y
} ;
// update velocity
bodyB . velocity . x = bodyB . position . x - bodyB . positionPrev . x ;
bodyB . velocity . y = bodyB . position . y - bodyB . positionPrev . y ;
bodyB . angularVelocity = bodyB . angle - bodyB . anglePrev ;
// find point velocity and body mass
velocityPointB = Vector . add ( bodyB . velocity , Vector . mult ( Vector . perp ( offsetB ) , bodyB . angularVelocity ) ) ;
oBn = Vector . dot ( offsetB , normal ) ;
bodyBDenom = bodyB . inverseMass + bodyB . inverseInertia * oBn * oBn ;
} else {
velocityPointB = { x : 0 , y : 0 } ;
bodyBDenom = bodyB ? bodyB . inverseMass : 0 ;
}
var relativeVelocity = Vector . sub ( velocityPointB , velocityPointA ) ,
normalImpulse = Vector . dot ( normal , relativeVelocity ) / ( bodyADenom + bodyBDenom ) ;
if ( normalImpulse > 0 ) normalImpulse = 0 ;
var normalVelocity = {
x : normal . x * normalImpulse ,
y : normal . y * normalImpulse
} ;
var torque ;
if ( bodyA && ! bodyA . isStatic ) {
torque = Vector . cross ( offsetA , normalVelocity ) * bodyA . inverseInertia * ( 1 - constraint . angularStiffness ) ;
Sleeping . set ( bodyA , false ) ;
// clamp to prevent instabillity
// TODO: solve this properlly
torque = Common . clamp ( torque , - 0.01 , 0.01 ) ;
2014-03-30 14:45:30 -04:00
// keep track of applied impulses for post solving
bodyA . constraintImpulse . x -= force . x ;
bodyA . constraintImpulse . y -= force . y ;
bodyA . constraintImpulse . angle += torque ;
2014-02-19 09:15:05 -05:00
// apply forces
bodyA . position . x -= force . x ;
bodyA . position . y -= force . y ;
bodyA . angle += torque ;
}
if ( bodyB && ! bodyB . isStatic ) {
torque = Vector . cross ( offsetB , normalVelocity ) * bodyB . inverseInertia * ( 1 - constraint . angularStiffness ) ;
Sleeping . set ( bodyB , false ) ;
// clamp to prevent instabillity
// TODO: solve this properlly
torque = Common . clamp ( torque , - 0.01 , 0.01 ) ;
2014-03-30 14:45:30 -04:00
// keep track of applied impulses for post solving
bodyB . constraintImpulse . x += force . x ;
bodyB . constraintImpulse . y += force . y ;
bodyB . constraintImpulse . angle -= torque ;
2014-02-19 09:15:05 -05:00
// apply forces
bodyB . position . x += force . x ;
bodyB . position . y += force . y ;
bodyB . angle -= torque ;
}
} ;
2014-03-30 14:45:30 -04:00
/ * *
* Performs body updates required after solving constraints
2014-06-09 14:40:24 -04:00
* @ private
2014-03-30 14:45:30 -04:00
* @ method postSolveAll
* @ param { body [ ] } bodies
* /
Constraint . postSolveAll = function ( bodies ) {
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ,
impulse = body . constraintImpulse ;
2014-04-01 08:47:17 -04:00
// update geometry and reset
Vertices . translate ( body . vertices , impulse ) ;
if ( impulse . angle !== 0 ) {
2014-03-30 14:45:30 -04:00
Vertices . rotate ( body . vertices , impulse . angle , body . position ) ;
Axes . rotate ( body . axes , impulse . angle ) ;
impulse . angle = 0 ;
}
2014-04-01 08:47:17 -04:00
Bounds . update ( body . bounds , body . vertices ) ;
impulse . x = 0 ;
impulse . y = 0 ;
2014-03-30 14:45:30 -04:00
}
} ;
2014-06-09 14:40:24 -04:00
/ *
*
* Properties Documentation
*
* /
/ * *
* An integer ` Number ` uniquely identifying number generated in ` Composite.create ` by ` Common.nextId ` .
*
* @ property id
* @ type number
* /
/ * *
* A ` String ` denoting the type of object .
*
* @ property type
* @ type string
* @ default "constraint"
* /
/ * *
* An arbitrary ` String ` name to help the user identify and manage bodies .
*
* @ property label
* @ type string
* @ default "Constraint"
* /
/ * *
* An ` Object ` that defines the rendering properties to be consumed by the module ` Matter.Render ` .
*
* @ property render
* @ type object
* /
/ * *
* A flag that indicates if the constraint should be rendered .
*
* @ property render . visible
* @ type boolean
* @ default true
* /
/ * *
* A ` Number ` that defines the line width to use when rendering the constraint outline .
* A value of ` 0 ` means no outline will be rendered .
*
* @ property render . lineWidth
* @ type number
* @ default 2
* /
/ * *
* A ` String ` that defines the stroke style to use when rendering the constraint outline .
* It is the same as when using a canvas , so it accepts CSS style property values .
*
* @ property render . strokeStyle
* @ type string
* @ default a random colour
* /
/ * *
* The first possible ` Body ` that this constraint is attached to .
*
* @ property bodyA
* @ type body
* @ default null
* /
/ * *
* The second possible ` Body ` that this constraint is attached to .
*
* @ property bodyB
* @ type body
* @ default null
* /
/ * *
* A ` Vector ` that specifies the offset of the constraint from center of the ` constraint.bodyA ` if defined , otherwise a world - space position .
*
* @ property pointA
* @ type vector
* @ default { x : 0 , y : 0 }
* /
/ * *
* A ` Vector ` that specifies the offset of the constraint from center of the ` constraint.bodyA ` if defined , otherwise a world - space position .
*
* @ property pointB
* @ type vector
* @ default { x : 0 , y : 0 }
* /
/ * *
* A ` Number ` that specifies the stiffness of the constraint , i . e . the rate at which it returns to its resting ` constraint.length ` .
* A value of ` 1 ` means the constraint should be very stiff .
* A value of ` 0.2 ` means the constraint acts like a soft spring .
*
* @ property stiffness
* @ type number
* @ default 1
* /
/ * *
* A ` Number ` that specifies the target resting length of the constraint .
* It is calculated automatically in ` Constraint.create ` from intial positions of the ` constraint.bodyA ` and ` constraint.bodyB ` .
*
* @ property length
* @ type number
* /
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/constraint/Constraint.js
// Begin src/constraint/MouseConstraint.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.MouseConstraint ` module contains methods for creating mouse constraints .
* Mouse constraints are used for allowing user interaction , providing the ability to move bodies via the mouse or touch .
*
2014-04-01 08:47:17 -04:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
2014-02-28 20:10:08 -05:00
*
* @ class MouseConstraint
* /
2014-02-19 09:15:05 -05:00
var MouseConstraint = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new mouse constraint .
* All properties have default values , and many are pre - calculated automatically based on other properties .
* See the properites section below for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method create
2014-03-30 14:45:30 -04:00
* @ param { engine } engine
* @ param { } options
2014-02-28 20:10:08 -05:00
* @ return { MouseConstraint } A new MouseConstraint
* /
2014-03-30 14:45:30 -04:00
MouseConstraint . create = function ( engine , options ) {
var mouse = engine . input . mouse ;
2014-02-19 09:15:05 -05:00
var constraint = Constraint . create ( {
2014-05-01 09:09:06 -04:00
label : 'Mouse Constraint' ,
2014-02-19 09:15:05 -05:00
pointA : mouse . position ,
pointB : { x : 0 , y : 0 } ,
length : 0.01 ,
stiffness : 0.1 ,
angularStiffness : 1 ,
2014-03-22 13:51:49 -04:00
render : {
strokeStyle : '#90EE90' ,
lineWidth : 3
}
2014-02-19 09:15:05 -05:00
} ) ;
2014-03-30 14:45:30 -04:00
var defaults = {
type : 'mouseConstraint' ,
2014-02-19 09:15:05 -05:00
mouse : mouse ,
dragBody : null ,
dragPoint : null ,
2014-03-22 13:51:49 -04:00
constraint : constraint
2014-02-19 09:15:05 -05:00
} ;
2014-03-30 14:45:30 -04:00
var mouseConstraint = Common . extend ( defaults , options ) ;
Events . on ( engine , 'tick' , function ( event ) {
var allBodies = Composite . allBodies ( engine . world ) ;
MouseConstraint . update ( mouseConstraint , allBodies ) ;
} ) ;
return mouseConstraint ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Updates the given mouse constraint .
* @ private
2014-02-28 20:10:08 -05:00
* @ method update
* @ param { MouseConstraint } mouseConstraint
* @ param { body [ ] } bodies
* /
2014-02-19 09:15:05 -05:00
MouseConstraint . update = function ( mouseConstraint , bodies ) {
var mouse = mouseConstraint . mouse ,
2014-03-22 13:51:49 -04:00
constraint = mouseConstraint . constraint ;
2014-02-19 09:15:05 -05:00
2014-03-24 16:11:42 -04:00
if ( mouse . button === 0 ) {
2014-02-19 09:15:05 -05:00
if ( ! constraint . bodyB ) {
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( Bounds . contains ( body . bounds , mouse . position )
&& Vertices . contains ( body . vertices , mouse . position ) ) {
constraint . pointA = mouse . position ;
constraint . bodyB = body ;
constraint . pointB = { x : mouse . position . x - body . position . x , y : mouse . position . y - body . position . y } ;
constraint . angleB = body . angle ;
Sleeping . set ( body , false ) ;
}
}
}
} else {
constraint . bodyB = null ;
constraint . pointB = null ;
}
if ( constraint . bodyB ) {
Sleeping . set ( constraint . bodyB , false ) ;
constraint . pointA = mouse . position ;
}
} ;
2014-06-09 14:40:24 -04:00
/ *
*
* Properties Documentation
*
* /
/ * *
* A ` String ` denoting the type of object .
*
* @ property type
* @ type string
* @ default "constraint"
* /
/ * *
* The ` Mouse ` instance in use .
*
* @ property mouse
* @ type mouse
* @ default engine . input . mouse
* /
/ * *
* The ` Body ` that is currently being moved by the user , or ` null ` if no body .
*
* @ property dragBody
* @ type body
* @ default null
* /
/ * *
* The ` Vector ` offset at which the drag started relative to the ` dragBody ` , if any .
*
* @ property dragPoint
* @ type body
* @ default null
* /
/ * *
* The ` Constraint ` object that is used to move the body during interaction .
*
* @ property constraint
* @ type constraint
* /
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/constraint/MouseConstraint.js
// Begin src/core/Common.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Common
* /
2014-02-19 09:15:05 -05:00
var Common = { } ;
( function ( ) {
2014-05-01 09:09:06 -04:00
Common . _nextId = 0 ;
2014-06-09 14:40:24 -04:00
Common . _seed = 0 ;
2014-05-01 09:09:06 -04:00
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method extend
2014-03-11 06:29:14 -04:00
* @ param { } obj
* @ param { boolean } deep
2014-02-28 20:10:08 -05:00
* @ return { } obj extended
* /
2014-03-11 06:29:14 -04:00
Common . extend = function ( obj , deep ) {
var argsStart ,
args ,
deepClone ;
if ( typeof deep === 'boolean' ) {
argsStart = 2 ;
deepClone = deep ;
} else {
argsStart = 1 ;
deepClone = true ;
}
args = Array . prototype . slice . call ( arguments , argsStart ) ;
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < args . length ; i ++ ) {
var source = args [ i ] ;
if ( source ) {
for ( var prop in source ) {
2014-03-22 13:51:49 -04:00
if ( deepClone && source [ prop ] && source [ prop ] . constructor === Object ) {
2014-02-19 09:15:05 -05:00
if ( ! obj [ prop ] || obj [ prop ] . constructor === Object ) {
obj [ prop ] = obj [ prop ] || { } ;
2014-03-11 06:29:14 -04:00
Common . extend ( obj [ prop ] , deepClone , source [ prop ] ) ;
2014-02-19 09:15:05 -05:00
} else {
obj [ prop ] = source [ prop ] ;
}
} else {
obj [ prop ] = source [ prop ] ;
}
}
}
}
return obj ;
} ;
2014-03-11 06:29:14 -04:00
/ * *
* Creates a new clone of the object , if deep is true references will also be cloned
* @ method clone
* @ param { } obj
* @ param { bool } deep
* @ return { } obj cloned
* /
Common . clone = function ( obj , deep ) {
return Common . extend ( { } , deep , obj ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method keys
* @ param { } obj
* @ return { string [ ] } keys
* /
2014-02-19 09:15:05 -05:00
Common . keys = function ( obj ) {
if ( Object . keys )
return Object . keys ( obj ) ;
// avoid hasOwnProperty for performance
var keys = [ ] ;
for ( var key in obj )
keys . push ( key ) ;
return keys ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method values
* @ param { } obj
* @ return { array } Array of the objects property values
* /
2014-02-19 09:15:05 -05:00
Common . values = function ( obj ) {
var values = [ ] ;
if ( Object . keys ) {
var keys = Object . keys ( obj ) ;
for ( var i = 0 ; i < keys . length ; i ++ ) {
values . push ( obj [ keys [ i ] ] ) ;
}
return values ;
}
// avoid hasOwnProperty for performance
for ( var key in obj )
values . push ( obj [ key ] ) ;
return values ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method shadeColor
* @ param { string } color
* @ param { number } percent
* @ return { string } A hex colour string made by lightening or darkening color by percent
* /
2014-02-19 09:15:05 -05:00
Common . shadeColor = function ( color , percent ) {
// http://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color
var colorInteger = parseInt ( color . slice ( 1 ) , 16 ) ,
amount = Math . round ( 2.55 * percent ) ,
R = ( colorInteger >> 16 ) + amount ,
B = ( colorInteger >> 8 & 0x00FF ) + amount ,
G = ( colorInteger & 0x0000FF ) + amount ;
return "#" + ( 0x1000000 + ( R < 255 ? R < 1 ? 0 : R : 255 ) * 0x10000
+ ( B < 255 ? B < 1 ? 0 : B : 255 ) * 0x100
+ ( G < 255 ? G < 1 ? 0 : G : 255 ) ) . toString ( 16 ) . slice ( 1 ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method shuffle
* @ param { array } array
* @ return { array } array shuffled randomly
* /
2014-02-19 09:15:05 -05:00
Common . shuffle = function ( array ) {
for ( var i = array . length - 1 ; i > 0 ; i -- ) {
2014-06-09 14:40:24 -04:00
var j = Math . floor ( Common . random ( ) * ( i + 1 ) ) ;
2014-02-19 09:15:05 -05:00
var temp = array [ i ] ;
array [ i ] = array [ j ] ;
array [ j ] = temp ;
}
return array ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method choose
* @ param { array } choices
* @ return { object } A random choice object from the array
* /
2014-02-19 09:15:05 -05:00
Common . choose = function ( choices ) {
2014-06-09 14:40:24 -04:00
return choices [ Math . floor ( Common . random ( ) * choices . length ) ] ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method isElement
* @ param { object } obj
* @ return { boolean } True if the object is a HTMLElement , otherwise false
* /
2014-02-19 09:15:05 -05:00
Common . isElement = function ( obj ) {
// http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
try {
return obj instanceof HTMLElement ;
}
catch ( e ) {
return ( typeof obj === "object" ) &&
( obj . nodeType === 1 ) && ( typeof obj . style === "object" ) &&
( typeof obj . ownerDocument === "object" ) ;
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method clamp
* @ param { number } value
* @ param { number } min
* @ param { number } max
* @ return { number } The value clamped between min and max inclusive
* /
2014-02-19 09:15:05 -05:00
Common . clamp = function ( value , min , max ) {
if ( value < min )
return min ;
if ( value > max )
return max ;
return value ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method sign
* @ param { number } value
* @ return { number } - 1 if negative , + 1 if 0 or positive
* /
2014-02-19 09:15:05 -05:00
Common . sign = function ( value ) {
return value < 0 ? - 1 : 1 ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method now
2014-03-22 13:51:49 -04:00
* @ return { number } the current timestamp ( high - res if avaliable )
2014-02-28 20:10:08 -05:00
* /
2014-03-22 13:51:49 -04:00
Common . now = function ( ) {
2014-02-19 09:15:05 -05:00
// http://stackoverflow.com/questions/221294/how-do-you-get-a-timestamp-in-javascript
2014-03-22 13:51:49 -04:00
// https://gist.github.com/davidwaterston/2982531
var perf = window . performance ;
if ( perf ) {
perf . now = perf . now || perf . webkitNow || perf . msNow || perf . oNow || perf . mozNow ;
return + ( perf . now ( ) ) ;
}
2014-02-19 09:15:05 -05:00
return + ( new Date ( ) ) ;
} ;
2014-03-22 13:51:49 -04:00
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method random
* @ param { number } min
* @ param { number } max
* @ return { number } A random number between min and max inclusive
* /
2014-02-19 09:15:05 -05:00
Common . random = function ( min , max ) {
2014-06-09 14:40:24 -04:00
min = ( typeof min !== "undefined" ) ? min : 0 ;
max = ( typeof max !== "undefined" ) ? max : 1 ;
return min + _seededRandom ( ) * ( max - min ) ;
2014-02-19 09:15:05 -05:00
} ;
2014-03-22 13:51:49 -04:00
/ * *
* Converts a CSS hex colour string into an integer
* @ method colorToNumber
* @ param { string } colorString
* @ return { number } An integer representing the CSS hex string
* /
Common . colorToNumber = function ( colorString ) {
colorString = colorString . replace ( '#' , '' ) ;
if ( colorString . length == 3 ) {
colorString = colorString . charAt ( 0 ) + colorString . charAt ( 0 )
+ colorString . charAt ( 1 ) + colorString . charAt ( 1 )
+ colorString . charAt ( 2 ) + colorString . charAt ( 2 ) ;
}
return parseInt ( colorString , 16 ) ;
} ;
/ * *
* A wrapper for console . log , for providing errors and warnings
* @ method log
* @ param { string } message
* @ param { string } type
* /
Common . log = function ( message , type ) {
if ( ! console || ! console . log )
return ;
var style ;
switch ( type ) {
case 'warn' :
style = 'color: coral' ;
break ;
case 'error' :
style = 'color: red' ;
break ;
}
console . log ( '%c [Matter] ' + type + ': ' + message , style ) ;
} ;
2014-05-01 09:09:06 -04:00
/ * *
* Returns the next unique sequential ID
* @ method nextId
* @ return { Number } Unique sequential ID
* /
Common . nextId = function ( ) {
return Common . _nextId ++ ;
} ;
2014-06-09 14:40:24 -04:00
var _seededRandom = function ( ) {
// https://gist.github.com/ngryman/3830489
Common . _seed = ( Common . _seed * 9301 + 49297 ) % 233280 ;
return Common . _seed / 233280 ;
} ;
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/core/Common.js
// Begin src/core/Engine.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Engine ` module contains methods for creating and manipulating engines .
* An engine is a controller that manages updating and rendering the simulation of the world .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Engine
* /
2014-02-19 09:15:05 -05:00
var Engine = { } ;
( function ( ) {
var _fps = 60 ,
2014-03-30 14:45:30 -04:00
_deltaSampleSize = _fps ,
2014-02-19 09:15:05 -05:00
_delta = 1000 / _fps ;
var _requestAnimationFrame = window . requestAnimationFrame || window . webkitRequestAnimationFrame
|| window . mozRequestAnimationFrame || window . msRequestAnimationFrame
|| function ( callback ) { window . setTimeout ( function ( ) { callback ( Common . now ( ) ) ; } , _delta ) ; } ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new engine . The options parameter is an object that specifies any properties you wish to override the defaults .
* All properties have default values , and many are pre - calculated automatically based on other properties .
* See the properites section below for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method create
* @ param { HTMLElement } element
2014-06-09 14:40:24 -04:00
* @ param { object } [ options ]
2014-02-28 20:10:08 -05:00
* @ return { engine } engine
* /
2014-02-19 09:15:05 -05:00
Engine . create = function ( element , options ) {
2014-03-22 13:51:49 -04:00
// options may be passed as the first (and only) argument
options = Common . isElement ( element ) ? options : element ;
element = Common . isElement ( element ) ? element : null ;
2014-02-19 09:15:05 -05:00
var defaults = {
enabled : true ,
positionIterations : 6 ,
velocityIterations : 4 ,
2014-03-30 14:45:30 -04:00
constraintIterations : 2 ,
2014-02-19 09:15:05 -05:00
enableSleeping : false ,
timeScale : 1 ,
input : { } ,
2014-03-11 06:29:14 -04:00
events : [ ] ,
2014-02-19 09:15:05 -05:00
timing : {
fps : _fps ,
timestamp : 0 ,
delta : _delta ,
correction : 1 ,
deltaMin : 1000 / _fps ,
2014-05-01 09:09:06 -04:00
deltaMax : 1000 / ( _fps * 0.5 ) ,
2014-05-05 14:32:51 -04:00
timeScale : 1 ,
isFixed : false
2014-03-22 13:51:49 -04:00
} ,
render : {
element : element ,
controller : Render
2014-02-19 09:15:05 -05:00
}
} ;
var engine = Common . extend ( defaults , options ) ;
2014-03-22 13:51:49 -04:00
engine . render = engine . render . controller . create ( engine . render ) ;
2014-02-19 09:15:05 -05:00
engine . world = World . create ( engine . world ) ;
2014-03-24 16:11:42 -04:00
engine . pairs = Pairs . create ( ) ;
2014-02-19 09:15:05 -05:00
engine . metrics = engine . metrics || Metrics . create ( ) ;
engine . input . mouse = engine . input . mouse || Mouse . create ( engine . render . canvas ) ;
engine . broadphase = engine . broadphase || {
current : 'grid' ,
grid : {
controller : Grid ,
instance : Grid . create ( ) ,
detector : Detector . collisions
} ,
bruteForce : {
detector : Detector . bruteForce
}
} ;
return engine ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-05-05 14:32:51 -04:00
* An optional utility function that provides a game loop , that handles updating the engine for you .
* Calls ` Engine.update ` and ` Engine.render ` on the ` requestAnimationFrame ` event automatically .
* Handles time correction and non - fixed dynamic timing ( if enabled ) .
* Triggers ` beforeTick ` , ` tick ` and ` afterTick ` events .
2014-02-28 20:10:08 -05:00
* @ method run
* @ param { engine } engine
* /
2014-02-19 09:15:05 -05:00
Engine . run = function ( engine ) {
2014-05-05 14:32:51 -04:00
var counterTimestamp = 0 ,
2014-02-19 09:15:05 -05:00
frameCounter = 0 ,
2014-05-01 09:09:06 -04:00
deltaHistory = [ ] ,
2014-05-05 14:32:51 -04:00
timePrev ,
2014-05-01 09:09:06 -04:00
timeScalePrev = 1 ;
2014-02-19 09:15:05 -05:00
2014-05-05 14:32:51 -04:00
( function render ( time ) {
2014-02-19 09:15:05 -05:00
_requestAnimationFrame ( render ) ;
if ( ! engine . enabled )
return ;
2014-05-05 14:32:51 -04:00
var timing = engine . timing ,
delta ,
correction ;
2014-03-22 13:51:49 -04:00
2014-03-11 06:29:14 -04:00
// create an event object
var event = {
2014-05-05 14:32:51 -04:00
timestamp : time
2014-03-11 06:29:14 -04:00
} ;
Events . trigger ( engine , 'beforeTick' , event ) ;
2014-05-05 14:32:51 -04:00
if ( timing . isFixed ) {
// fixed timestep
delta = timing . delta ;
} else {
// dynamic timestep based on wall clock between calls
delta = ( time - timePrev ) || timing . delta ;
timePrev = time ;
// optimistically filter delta over a few frames, to improve stability
deltaHistory . push ( delta ) ;
deltaHistory = deltaHistory . slice ( - _deltaSampleSize ) ;
delta = Math . min . apply ( null , deltaHistory ) ;
// limit delta
delta = delta < timing . deltaMin ? timing . deltaMin : delta ;
delta = delta > timing . deltaMax ? timing . deltaMax : delta ;
2014-02-19 09:15:05 -05:00
2014-05-05 14:32:51 -04:00
// time correction for delta
correction = delta / timing . delta ;
2014-02-19 09:15:05 -05:00
2014-05-05 14:32:51 -04:00
// update engine timing object
timing . delta = delta ;
}
2014-03-11 06:29:14 -04:00
2014-05-01 09:09:06 -04:00
// time correction for time scaling
if ( timeScalePrev !== 0 )
correction *= timing . timeScale / timeScalePrev ;
if ( timing . timeScale === 0 )
correction = 0 ;
timeScalePrev = timing . timeScale ;
2014-02-19 09:15:05 -05:00
2014-03-11 06:29:14 -04:00
// fps counter
2014-02-19 09:15:05 -05:00
frameCounter += 1 ;
2014-05-05 14:32:51 -04:00
if ( time - counterTimestamp >= 1000 ) {
timing . fps = frameCounter * ( ( time - counterTimestamp ) / 1000 ) ;
counterTimestamp = time ;
2014-02-19 09:15:05 -05:00
frameCounter = 0 ;
}
2014-05-05 14:32:51 -04:00
Events . trigger ( engine , 'tick' , event ) ;
2014-03-11 06:29:14 -04:00
2014-03-24 16:11:42 -04:00
// if world has been modified, clear the render scene graph
if ( engine . world . isModified )
engine . render . controller . clear ( engine . render ) ;
2014-03-11 06:29:14 -04:00
// update
Engine . update ( engine , delta , correction ) ;
2014-03-22 13:51:49 -04:00
// trigger events that may have occured during the step
_triggerCollisionEvents ( engine ) ;
_triggerMouseEvents ( engine ) ;
2014-03-11 06:29:14 -04:00
// render
2014-05-05 14:32:51 -04:00
Engine . render ( engine ) ;
2014-03-11 06:29:14 -04:00
2014-05-05 14:32:51 -04:00
Events . trigger ( engine , 'afterTick' , event ) ;
2014-02-19 09:15:05 -05:00
} ) ( ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-05-05 14:32:51 -04:00
* Moves the simulation forward in time by ` delta ` ms . Triggers ` beforeUpdate ` and ` afterUpdate ` events .
2014-02-28 20:10:08 -05:00
* @ method update
* @ param { engine } engine
* @ param { number } delta
2014-06-09 14:40:24 -04:00
* @ param { number } [ correction ]
2014-02-28 20:10:08 -05:00
* /
2014-02-19 09:15:05 -05:00
Engine . update = function ( engine , delta , correction ) {
2014-05-05 14:32:51 -04:00
correction = ( typeof correction !== 'undefined' ) ? correction : 1 ;
2014-02-19 09:15:05 -05:00
var world = engine . world ,
2014-05-01 09:09:06 -04:00
timing = engine . timing ,
2014-02-19 09:15:05 -05:00
broadphase = engine . broadphase [ engine . broadphase . current ] ,
broadphasePairs = [ ] ,
i ;
2014-05-05 14:32:51 -04:00
// increment timestamp
timing . timestamp += delta * timing . timeScale ;
timing . correction = correction ;
// create an event object
var event = {
timestamp : engine . timing . timestamp
} ;
Events . trigger ( engine , 'beforeUpdate' , event ) ;
2014-03-24 16:11:42 -04:00
// get lists of all bodies and constraints, no matter what composites they are in
var allBodies = Composite . allBodies ( world ) ,
allConstraints = Composite . allConstraints ( world ) ;
// reset metrics logging
2014-02-19 09:15:05 -05:00
Metrics . reset ( engine . metrics ) ;
2014-03-24 16:11:42 -04:00
// if sleeping enabled, call the sleeping controller
2014-03-11 06:29:14 -04:00
if ( engine . enableSleeping )
2014-03-24 16:11:42 -04:00
Sleeping . update ( allBodies ) ;
2014-03-11 06:29:14 -04:00
2014-03-24 16:11:42 -04:00
// applies gravity to all bodies
Body . applyGravityAll ( allBodies , world . gravity ) ;
2014-03-11 06:29:14 -04:00
2014-03-24 16:11:42 -04:00
// update all body position and rotation by integration
2014-05-05 14:32:51 -04:00
Body . updateAll ( allBodies , delta , timing . timeScale , correction , world . bounds ) ;
2014-02-19 09:15:05 -05:00
2014-03-11 06:29:14 -04:00
// update all constraints
2014-02-19 09:15:05 -05:00
for ( i = 0 ; i < engine . constraintIterations ; i ++ ) {
2014-05-01 09:09:06 -04:00
Constraint . solveAll ( allConstraints , timing . timeScale ) ;
2014-02-19 09:15:05 -05:00
}
2014-03-30 14:45:30 -04:00
Constraint . postSolveAll ( allBodies ) ;
2014-02-19 09:15:05 -05:00
2014-03-11 06:29:14 -04:00
// broadphase pass: find potential collision pairs
2014-02-19 09:15:05 -05:00
if ( broadphase . controller ) {
2014-03-24 16:11:42 -04:00
// if world is dirty, we must flush the whole grid
if ( world . isModified )
broadphase . controller . clear ( broadphase . instance ) ;
// update the grid buckets based on current bodies
broadphase . controller . update ( broadphase . instance , allBodies , engine , world . isModified ) ;
2014-02-19 09:15:05 -05:00
broadphasePairs = broadphase . instance . pairsList ;
} else {
2014-03-24 16:11:42 -04:00
// if no broadphase set, we just pass all bodies
broadphasePairs = allBodies ;
2014-02-19 09:15:05 -05:00
}
2014-03-22 13:51:49 -04:00
2014-03-11 06:29:14 -04:00
// narrowphase pass: find actual collisions, then create or update collision pairs
2014-03-22 13:51:49 -04:00
var collisions = broadphase . detector ( broadphasePairs , engine ) ;
2014-03-11 06:29:14 -04:00
2014-03-24 16:11:42 -04:00
// update collision pairs
2014-03-22 13:51:49 -04:00
var pairs = engine . pairs ,
2014-05-01 09:09:06 -04:00
timestamp = timing . timestamp ;
2014-03-24 16:11:42 -04:00
Pairs . update ( pairs , collisions , timestamp ) ;
Pairs . removeOld ( pairs , timestamp ) ;
2014-02-19 09:15:05 -05:00
// wake up bodies involved in collisions
if ( engine . enableSleeping )
2014-03-11 06:29:14 -04:00
Sleeping . afterCollisions ( pairs . list ) ;
2014-02-19 09:15:05 -05:00
// iteratively resolve velocity between collisions
2014-03-11 06:29:14 -04:00
Resolver . preSolveVelocity ( pairs . list ) ;
2014-02-19 09:15:05 -05:00
for ( i = 0 ; i < engine . velocityIterations ; i ++ ) {
2014-05-05 14:32:51 -04:00
Resolver . solveVelocity ( pairs . list , timing . timeScale ) ;
2014-02-19 09:15:05 -05:00
}
// iteratively resolve position between collisions
for ( i = 0 ; i < engine . positionIterations ; i ++ ) {
2014-05-01 09:09:06 -04:00
Resolver . solvePosition ( pairs . list , timing . timeScale ) ;
2014-02-19 09:15:05 -05:00
}
2014-03-24 16:11:42 -04:00
Resolver . postSolvePosition ( allBodies ) ;
2014-02-19 09:15:05 -05:00
2014-03-24 16:11:42 -04:00
// update metrics log
2014-02-19 09:15:05 -05:00
Metrics . update ( engine . metrics , engine ) ;
2014-03-11 06:29:14 -04:00
// clear force buffers
2014-03-24 16:11:42 -04:00
Body . resetForcesAll ( allBodies ) ;
// clear all composite modified flags
if ( world . isModified )
Composite . setModified ( world , false , false , true ) ;
2014-03-11 06:29:14 -04:00
2014-05-05 14:32:51 -04:00
Events . trigger ( engine , 'afterUpdate' , event ) ;
2014-02-19 09:15:05 -05:00
return engine ;
} ;
2014-05-05 14:32:51 -04:00
/ * *
* Renders the world by calling its defined renderer ` engine.render.controller ` . Triggers ` beforeRender ` and ` afterRender ` events .
* @ method render
* @ param { engine } engineA
* @ param { engine } engineB
* /
Engine . render = function ( engine ) {
// create an event object
var event = {
timestamp : engine . timing . timestamp
} ;
Events . trigger ( engine , 'beforeRender' , event ) ;
engine . render . controller . world ( engine ) ;
Events . trigger ( engine , 'afterRender' , event ) ;
} ;
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Merges two engines by keeping the configuration of ` engineA ` but replacing the world with the one from ` engineB ` .
2014-02-28 20:10:08 -05:00
* @ method merge
* @ param { engine } engineA
* @ param { engine } engineB
* /
2014-02-19 09:15:05 -05:00
Engine . merge = function ( engineA , engineB ) {
Common . extend ( engineA , engineB ) ;
if ( engineB . world ) {
engineA . world = engineB . world ;
Engine . clear ( engineA ) ;
2014-03-24 16:11:42 -04:00
var bodies = Composite . allBodies ( engineA . world ) ;
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
Sleeping . set ( body , false ) ;
2014-05-01 09:09:06 -04:00
body . id = Common . nextId ( ) ;
2014-02-19 09:15:05 -05:00
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Clears the engine including the world , pairs and broadphase .
2014-02-28 20:10:08 -05:00
* @ method clear
* @ param { engine } engine
* /
2014-02-19 09:15:05 -05:00
Engine . clear = function ( engine ) {
var world = engine . world ;
2014-03-24 16:11:42 -04:00
Pairs . clear ( engine . pairs ) ;
2014-02-19 09:15:05 -05:00
var broadphase = engine . broadphase [ engine . broadphase . current ] ;
if ( broadphase . controller ) {
2014-03-24 16:11:42 -04:00
var bodies = Composite . allBodies ( world ) ;
broadphase . controller . clear ( broadphase . instance ) ;
broadphase . controller . update ( broadphase . instance , bodies , engine , true ) ;
2014-02-19 09:15:05 -05:00
}
} ;
2014-03-22 13:51:49 -04:00
/ * *
* Triggers mouse events
* @ method _triggerMouseEvents
* @ private
* @ param { engine } engine
* /
var _triggerMouseEvents = function ( engine ) {
var mouse = engine . input . mouse ,
mouseEvents = mouse . sourceEvents ;
if ( mouseEvents . mousemove ) {
Events . trigger ( engine , 'mousemove' , {
mouse : mouse
} ) ;
}
if ( mouseEvents . mousedown ) {
Events . trigger ( engine , 'mousedown' , {
mouse : mouse
} ) ;
}
if ( mouseEvents . mouseup ) {
Events . trigger ( engine , 'mouseup' , {
mouse : mouse
} ) ;
}
// reset the mouse state ready for the next step
Mouse . clearSourceEvents ( mouse ) ;
} ;
/ * *
* Triggers collision events
* @ method _triggerMouseEvents
* @ private
* @ param { engine } engine
* /
var _triggerCollisionEvents = function ( engine ) {
var pairs = engine . pairs ;
if ( pairs . collisionStart . length > 0 ) {
Events . trigger ( engine , 'collisionStart' , {
pairs : pairs . collisionStart
} ) ;
}
if ( pairs . collisionActive . length > 0 ) {
Events . trigger ( engine , 'collisionActive' , {
pairs : pairs . collisionActive
} ) ;
}
if ( pairs . collisionEnd . length > 0 ) {
Events . trigger ( engine , 'collisionEnd' , {
pairs : pairs . collisionEnd
} ) ;
}
} ;
2014-05-01 09:09:06 -04:00
/ *
*
* Events Documentation
*
* /
/ * *
* Fired at the start of a tick , before any updates to the engine or timing
*
* @ event beforeTick
* @ param { } event An event object
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired after engine timing updated , but just before engine state updated
*
* @ event tick
* @ param { } event An event object
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired just before an update
*
* @ event beforeUpdate
* @ param { } event An event object
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired after engine update and all collision events
*
* @ event afterUpdate
* @ param { } event An event object
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired just before rendering
*
* @ event beforeRender
* @ param { } event An event object
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired after rendering
*
* @ event afterRender
* @ param { } event An event object
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired after engine update and after rendering
*
* @ event afterTick
* @ param { } event An event object
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired when the mouse has moved ( or a touch moves ) during the last step
*
* @ event mousemove
* @ param { } event An event object
* @ param { mouse } event . mouse The engine ' s mouse instance
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired when the mouse is down ( or a touch has started ) during the last step
*
* @ event mousedown
* @ param { } event An event object
* @ param { mouse } event . mouse The engine ' s mouse instance
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired when the mouse is up ( or a touch has ended ) during the last step
*
* @ event mouseup
* @ param { } event An event object
* @ param { mouse } event . mouse The engine ' s mouse instance
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired after engine update , provides a list of all pairs that have started to collide in the current tick ( if any )
*
* @ event collisionStart
* @ param { } event An event object
* @ param { } event . pairs List of affected pairs
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired after engine update , provides a list of all pairs that are colliding in the current tick ( if any )
*
* @ event collisionActive
* @ param { } event An event object
* @ param { } event . pairs List of affected pairs
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
/ * *
* Fired after engine update , provides a list of all pairs that have ended collision in the current tick ( if any )
*
* @ event collisionEnd
* @ param { } event An event object
* @ param { } event . pairs List of affected pairs
* @ param { DOMHighResTimeStamp } event . timestamp The timestamp of the current tick
* @ param { } event . source The source object of the event
* @ param { } event . name The name of the event
* /
2014-06-09 14:40:24 -04:00
/ *
*
* Properties Documentation
*
* /
/ * *
* A flag that specifies whether the engine is running or not .
*
* @ property enabled
* @ type boolean
* @ default true
* /
/ * *
* An integer ` Number ` that specifies the number of position iterations to perform each update .
* The higher the value , the higher quality the simulation will be at the expense of performance .
*
* @ property positionIterations
* @ type number
* @ default 6
* /
/ * *
* An integer ` Number ` that specifies the number of velocity iterations to perform each update .
* The higher the value , the higher quality the simulation will be at the expense of performance .
*
* @ property velocityIterations
* @ type number
* @ default 4
* /
/ * *
* An integer ` Number ` that specifies the number of constraint iterations to perform each update .
* The higher the value , the higher quality the simulation will be at the expense of performance .
* The default value of ` 2 ` is usually very adequate .
*
* @ property constraintIterations
* @ type number
* @ default 2
* /
/ * *
* A flag that specifies whether the engine should allow sleeping via the ` Matter.Sleeping ` module .
* Sleeping can improve stability and performance , but often at the expense of accuracy .
*
* @ property enableSleeping
* @ type boolean
* @ default false
* /
/ * *
* An ` Object ` containing properties regarding the timing systems of the engine .
*
* @ property timing
* @ type object
* /
/ * *
* A ` Number ` that specifies the global scaling factor of time for all bodies .
* A value of ` 0 ` freezes the simulation .
* A value of ` 0.1 ` gives a slow - motion effect .
* A value of ` 1.2 ` gives a speed - up effect .
*
* @ property timing . timeScale
* @ type number
* @ default 1
* /
/ * *
* A ` Number ` that specifies the current simulation - time in milliseconds starting from ` 0 ` .
* It is incremented on every ` Engine.update ` by the ` timing.delta ` .
*
* @ property timing . timestamp
* @ type number
* @ default 0
* /
/ * *
* A ` Number ` that specifies the time step between updates in milliseconds .
* If ` engine.timing.isFixed ` is set to ` true ` , then ` delta ` is fixed .
* If it is ` false ` , then ` delta ` can dynamically change to maintain the correct apparant simulation speed .
*
* @ property timing . delta
* @ type number
* @ default 1000 / 60
* /
/ * *
* A ` Number ` that specifies the time correction factor to apply to the current timestep .
* It is automatically handled when using ` Engine.run ` , but is also only optional even if you use your own game loop .
* The value is defined as ` delta / lastDelta ` , i . e . the percentage change of ` delta ` between steps .
* This value is always ` 1 ` ( no correction ) when frame rate is constant or ` engine.timing.isFixed ` is ` true ` .
* If the framerate and hence ` delta ` are changing , then correction should be applied to the current update to account for the change .
* See the paper on < a href = "http://lonesock.net/article/verlet.html" > Time Corrected Verlet < / a > f o r m o r e i n f o r m a t i o n .
*
* @ property timing . correction
* @ type number
* @ default 1
* /
/ * *
* An instance of a ` Render ` controller . The default value is a ` Matter.Render ` instance created by ` Engine.create ` .
* One may also develop a custom renderer module based on ` Matter.Render ` and pass an instance of it to ` Engine.create ` via ` options.render ` .
*
* A minimal custom renderer object must define at least three functions : ` create ` , ` clear ` and ` world ` ( see ` Matter.Render ` ) .
* It is also possible to instead pass the _module _ reference via ` options.render.controller ` and ` Engine.create ` will instantiate one for you .
*
* @ property render
* @ type render
* @ default a Matter . Render instance
* /
/ * *
* A ` World ` composite object that will contain all simulated bodies and constraints .
*
* @ property world
* @ type world
* @ default a Matter . World instance
* /
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/core/Engine.js
2014-03-11 06:29:14 -04:00
// Begin src/core/Events.js
/ * *
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Events
* /
var Events = { } ;
( function ( ) {
/ * *
2014-06-09 14:40:24 -04:00
* Subscribes a callback function to the given object ' s ` eventName ` .
2014-03-11 06:29:14 -04:00
* @ method on
* @ param { } object
* @ param { string } eventNames
* @ param { function } callback
* /
Events . on = function ( object , eventNames , callback ) {
var names = eventNames . split ( ' ' ) ,
name ;
for ( var i = 0 ; i < names . length ; i ++ ) {
name = names [ i ] ;
object . events = object . events || { } ;
object . events [ name ] = object . events [ name ] || [ ] ;
object . events [ name ] . push ( callback ) ;
}
2014-05-01 09:09:06 -04:00
return callback ;
2014-03-11 06:29:14 -04:00
} ;
2014-03-24 16:11:42 -04:00
/ * *
2014-06-09 14:40:24 -04:00
* Removes the given event callback . If no callback , clears all callbacks in ` eventNames ` . If no ` eventNames ` , clears all events .
2014-03-24 16:11:42 -04:00
* @ method off
* @ param { } object
* @ param { string } eventNames
2014-05-01 09:09:06 -04:00
* @ param { function } callback
2014-03-24 16:11:42 -04:00
* /
2014-05-01 09:09:06 -04:00
Events . off = function ( object , eventNames , callback ) {
2014-03-24 16:11:42 -04:00
if ( ! eventNames ) {
object . events = { } ;
return ;
}
2014-05-01 09:09:06 -04:00
// handle Events.off(object, callback)
if ( typeof eventNames === 'function' ) {
callback = eventNames ;
eventNames = Common . keys ( object . events ) . join ( ' ' ) ;
}
2014-03-24 16:11:42 -04:00
var names = eventNames . split ( ' ' ) ;
2014-05-01 09:09:06 -04:00
2014-03-24 16:11:42 -04:00
for ( var i = 0 ; i < names . length ; i ++ ) {
2014-05-01 09:09:06 -04:00
var callbacks = object . events [ names [ i ] ] ,
newCallbacks = [ ] ;
if ( callback ) {
for ( var j = 0 ; j < callbacks . length ; j ++ ) {
if ( callbacks [ j ] !== callback )
newCallbacks . push ( callbacks [ j ] ) ;
}
}
object . events [ names [ i ] ] = newCallbacks ;
2014-03-24 16:11:42 -04:00
}
} ;
2014-03-11 06:29:14 -04:00
/ * *
2014-06-09 14:40:24 -04:00
* Fires all the callbacks subscribed to the given object ' s ` eventName ` , in the order they subscribed , if any .
2014-03-24 16:11:42 -04:00
* @ method trigger
2014-03-11 06:29:14 -04:00
* @ param { } object
* @ param { string } eventNames
* @ param { } event
* /
Events . trigger = function ( object , eventNames , event ) {
var names ,
name ,
callbacks ,
eventClone ;
if ( object . events ) {
2014-03-22 13:51:49 -04:00
if ( ! event )
event = { } ;
2014-03-11 06:29:14 -04:00
names = eventNames . split ( ' ' ) ;
for ( var i = 0 ; i < names . length ; i ++ ) {
name = names [ i ] ;
2014-03-30 14:45:30 -04:00
callbacks = object . events [ name ] ;
2014-03-11 06:29:14 -04:00
2014-03-30 14:45:30 -04:00
if ( callbacks ) {
2014-03-11 06:29:14 -04:00
eventClone = Common . clone ( event , false ) ;
eventClone . name = name ;
eventClone . source = object ;
for ( var j = 0 ; j < callbacks . length ; j ++ ) {
callbacks [ j ] . apply ( object , [ eventClone ] ) ;
}
}
}
}
} ;
} ) ( ) ;
; // End src/core/Events.js
2014-02-19 09:15:05 -05:00
// Begin src/core/Metrics.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Metrics
* /
2014-02-19 09:15:05 -05:00
var Metrics = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method create
* @ return { metrics } A new metrics
* /
2014-02-19 09:15:05 -05:00
Metrics . create = function ( ) {
return {
2014-03-22 13:51:49 -04:00
extended : false ,
2014-02-19 09:15:05 -05:00
narrowDetections : 0 ,
narrowphaseTests : 0 ,
2014-03-22 13:51:49 -04:00
narrowReuse : 0 ,
narrowReuseCount : 0 ,
2014-02-19 09:15:05 -05:00
midphaseTests : 0 ,
broadphaseTests : 0 ,
narrowEff : 0.0001 ,
midEff : 0.0001 ,
broadEff : 0.0001 ,
collisions : 0 ,
buckets : 0 ,
bodies : 0 ,
pairs : 0
} ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method reset
* @ param { metrics } metrics
* /
2014-02-19 09:15:05 -05:00
Metrics . reset = function ( metrics ) {
2014-03-22 13:51:49 -04:00
if ( metrics . extended ) {
metrics . narrowDetections = 0 ;
metrics . narrowphaseTests = 0 ;
metrics . narrowReuse = 0 ;
metrics . narrowReuseCount = 0 ;
metrics . midphaseTests = 0 ;
metrics . broadphaseTests = 0 ;
metrics . narrowEff = 0 ;
metrics . midEff = 0 ;
metrics . broadEff = 0 ;
metrics . collisions = 0 ;
metrics . buckets = 0 ;
metrics . pairs = 0 ;
metrics . bodies = 0 ;
}
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method update
* @ param { metrics } metrics
* @ param { engine } engine
* /
2014-02-19 09:15:05 -05:00
Metrics . update = function ( metrics , engine ) {
2014-03-22 13:51:49 -04:00
if ( metrics . extended ) {
var world = engine . world ,
2014-03-24 16:11:42 -04:00
broadphase = engine . broadphase [ engine . broadphase . current ] ,
bodies = Composite . allBodies ( world ) ;
2014-03-22 13:51:49 -04:00
metrics . collisions = metrics . narrowDetections ;
metrics . pairs = engine . pairs . list . length ;
2014-03-24 16:11:42 -04:00
metrics . bodies = bodies . length ;
2014-03-22 13:51:49 -04:00
metrics . midEff = ( metrics . narrowDetections / ( metrics . midphaseTests || 1 ) ) . toFixed ( 2 ) ;
metrics . narrowEff = ( metrics . narrowDetections / ( metrics . narrowphaseTests || 1 ) ) . toFixed ( 2 ) ;
2014-03-24 16:11:42 -04:00
metrics . broadEff = ( 1 - ( metrics . broadphaseTests / ( bodies . length || 1 ) ) ) . toFixed ( 2 ) ;
2014-03-22 13:51:49 -04:00
metrics . narrowReuse = ( metrics . narrowReuseCount / ( metrics . narrowphaseTests || 1 ) ) . toFixed ( 2 ) ;
//if (broadphase.instance)
// metrics.buckets = Common.keys(broadphase.instance.buckets).length;
}
2014-02-19 09:15:05 -05:00
} ;
} ) ( ) ;
; // End src/core/Metrics.js
// Begin src/core/Mouse.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Mouse
* /
2014-02-19 09:15:05 -05:00
var Mouse ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ param { HTMLElement } element
* /
2014-02-19 09:15:05 -05:00
Mouse = function ( element ) {
var mouse = this ;
2014-03-30 14:45:30 -04:00
this . element = element || document . body ;
2014-05-05 14:32:51 -04:00
this . absolute = { x : 0 , y : 0 } ;
2014-02-19 09:15:05 -05:00
this . position = { x : 0 , y : 0 } ;
this . mousedownPosition = { x : 0 , y : 0 } ;
this . mouseupPosition = { x : 0 , y : 0 } ;
2014-05-01 09:09:06 -04:00
this . offset = { x : 0 , y : 0 } ;
2014-05-05 14:32:51 -04:00
this . scale = { x : 1 , y : 1 } ;
this . wheelDelta = 0 ;
2014-02-19 09:15:05 -05:00
this . button = - 1 ;
2014-03-22 13:51:49 -04:00
this . sourceEvents = {
mousemove : null ,
mousedown : null ,
2014-05-05 14:32:51 -04:00
mouseup : null ,
mousewheel : null
2014-03-22 13:51:49 -04:00
} ;
2014-02-19 09:15:05 -05:00
2014-03-30 14:45:30 -04:00
this . mousemove = function ( event ) {
var position = _getRelativeMousePosition ( event , mouse . element ) ,
2014-02-19 09:15:05 -05:00
touches = event . changedTouches ;
if ( touches ) {
mouse . button = 0 ;
event . preventDefault ( ) ;
}
2014-05-05 14:32:51 -04:00
mouse . absolute . x = position . x ;
mouse . absolute . y = position . y ;
mouse . position . x = mouse . absolute . x * mouse . scale . x + mouse . offset . x ;
mouse . position . y = mouse . absolute . y * mouse . scale . y + mouse . offset . y ;
2014-03-22 13:51:49 -04:00
mouse . sourceEvents . mousemove = event ;
2014-02-19 09:15:05 -05:00
} ;
2014-03-30 14:45:30 -04:00
this . mousedown = function ( event ) {
var position = _getRelativeMousePosition ( event , mouse . element ) ,
2014-02-19 09:15:05 -05:00
touches = event . changedTouches ;
if ( touches ) {
mouse . button = 0 ;
event . preventDefault ( ) ;
} else {
mouse . button = event . button ;
}
2014-05-05 14:32:51 -04:00
mouse . absolute . x = position . x ;
mouse . absolute . y = position . y ;
mouse . position . x = mouse . absolute . x * mouse . scale . x + mouse . offset . x ;
mouse . position . y = mouse . absolute . y * mouse . scale . y + mouse . offset . y ;
mouse . mousedownPosition . x = mouse . position . x ;
mouse . mousedownPosition . y = mouse . position . y ;
2014-03-22 13:51:49 -04:00
mouse . sourceEvents . mousedown = event ;
2014-02-19 09:15:05 -05:00
} ;
2014-03-30 14:45:30 -04:00
this . mouseup = function ( event ) {
var position = _getRelativeMousePosition ( event , mouse . element ) ,
2014-02-19 09:15:05 -05:00
touches = event . changedTouches ;
if ( touches ) {
event . preventDefault ( ) ;
}
mouse . button = - 1 ;
2014-05-05 14:32:51 -04:00
mouse . absolute . x = position . x ;
mouse . absolute . y = position . y ;
mouse . position . x = mouse . absolute . x * mouse . scale . x + mouse . offset . x ;
mouse . position . y = mouse . absolute . y * mouse . scale . y + mouse . offset . y ;
mouse . mouseupPosition . x = mouse . position . x ;
mouse . mouseupPosition . y = mouse . position . y ;
2014-03-22 13:51:49 -04:00
mouse . sourceEvents . mouseup = event ;
2014-02-19 09:15:05 -05:00
} ;
2014-03-30 14:45:30 -04:00
2014-05-05 14:32:51 -04:00
this . mousewheel = function ( event ) {
mouse . wheelDelta = Math . max ( - 1 , Math . min ( 1 , event . wheelDelta || - event . detail ) ) ;
event . preventDefault ( ) ;
} ;
2014-03-30 14:45:30 -04:00
Mouse . setElement ( mouse , mouse . element ) ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method create
* @ param { HTMLElement } element
* @ return { mouse } A new mouse
* /
2014-02-19 09:15:05 -05:00
Mouse . create = function ( element ) {
return new Mouse ( element ) ;
} ;
2014-03-22 13:51:49 -04:00
2014-03-30 14:45:30 -04:00
/ * *
* Sets the element the mouse is bound to ( and relative to )
* @ method setElement
* @ param { mouse } mouse
* @ param { HTMLElement } element
* /
Mouse . setElement = function ( mouse , element ) {
mouse . element = element ;
element . addEventListener ( 'mousemove' , mouse . mousemove ) ;
element . addEventListener ( 'mousedown' , mouse . mousedown ) ;
element . addEventListener ( 'mouseup' , mouse . mouseup ) ;
2014-05-05 14:32:51 -04:00
element . addEventListener ( "mousewheel" , mouse . mousewheel ) ;
element . addEventListener ( "DOMMouseScroll" , mouse . mousewheel ) ;
2014-03-30 14:45:30 -04:00
element . addEventListener ( 'touchmove' , mouse . mousemove ) ;
element . addEventListener ( 'touchstart' , mouse . mousedown ) ;
element . addEventListener ( 'touchend' , mouse . mouseup ) ;
} ;
2014-03-22 13:51:49 -04:00
/ * *
* Clears all captured source events
2014-04-01 08:47:17 -04:00
* @ method clearSourceEvents
2014-03-22 13:51:49 -04:00
* @ param { mouse } mouse
* /
Mouse . clearSourceEvents = function ( mouse ) {
mouse . sourceEvents . mousemove = null ;
mouse . sourceEvents . mousedown = null ;
mouse . sourceEvents . mouseup = null ;
2014-05-05 14:32:51 -04:00
mouse . sourceEvents . mousewheel = null ;
mouse . wheelDelta = 0 ;
} ;
/ * *
* Sets the offset
* @ method setOffset
* @ param { mouse } mouse
* /
Mouse . setOffset = function ( mouse , offset ) {
mouse . offset . x = offset . x ;
mouse . offset . y = offset . y ;
mouse . position . x = mouse . absolute . x * mouse . scale . x + mouse . offset . x ;
mouse . position . y = mouse . absolute . y * mouse . scale . y + mouse . offset . y ;
} ;
/ * *
* Sets the scale
* @ method setScale
* @ param { mouse } mouse
* /
Mouse . setScale = function ( mouse , scale ) {
mouse . scale . x = scale . x ;
mouse . scale . y = scale . y ;
mouse . position . x = mouse . absolute . x * mouse . scale . x + mouse . offset . x ;
mouse . position . y = mouse . absolute . y * mouse . scale . y + mouse . offset . y ;
2014-03-22 13:51:49 -04:00
} ;
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _getRelativeMousePosition
* @ private
* @ param { } event
* @ param { } element
* @ return ObjectExpression
* /
2014-02-19 09:15:05 -05:00
var _getRelativeMousePosition = function ( event , element ) {
var elementBounds = element . getBoundingClientRect ( ) ,
2014-05-05 14:32:51 -04:00
rootNode = ( document . documentElement || document . body . parentNode || document . body ) ,
scrollX = ( window . pageXOffset !== undefined ) ? window . pageXOffset : rootNode . scrollLeft ,
scrollY = ( window . pageYOffset !== undefined ) ? window . pageYOffset : rootNode . scrollTop ,
2014-02-19 09:15:05 -05:00
touches = event . changedTouches ,
x , y ;
if ( touches ) {
x = touches [ 0 ] . pageX - elementBounds . left - scrollX ;
y = touches [ 0 ] . pageY - elementBounds . top - scrollY ;
} else {
x = event . pageX - elementBounds . left - scrollX ;
y = event . pageY - elementBounds . top - scrollY ;
}
return {
x : x / ( element . clientWidth / element . width ) ,
y : y / ( element . clientHeight / element . height )
} ;
} ;
} ) ( ) ;
; // End src/core/Mouse.js
// Begin src/core/Sleeping.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Sleeping
* /
2014-02-19 09:15:05 -05:00
var Sleeping = { } ;
( function ( ) {
var _motionWakeThreshold = 0.18 ,
_motionSleepThreshold = 0.08 ,
_minBias = 0.9 ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method update
* @ param { body [ ] } bodies
* /
2014-02-19 09:15:05 -05:00
Sleeping . update = function ( bodies ) {
// update bodies sleeping status
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ,
motion = body . speed * body . speed + body . angularSpeed * body . angularSpeed ;
2014-03-11 06:29:14 -04:00
// wake up bodies if they have a force applied
if ( body . force . x > 0 || body . force . y > 0 ) {
Sleeping . set ( body , false ) ;
continue ;
}
2014-02-19 09:15:05 -05:00
var minMotion = Math . min ( body . motion , motion ) ,
maxMotion = Math . max ( body . motion , motion ) ;
// biased average motion estimation between frames
body . motion = _minBias * minMotion + ( 1 - _minBias ) * maxMotion ;
if ( body . sleepThreshold > 0 && body . motion < _motionSleepThreshold ) {
body . sleepCounter += 1 ;
if ( body . sleepCounter >= body . sleepThreshold )
Sleeping . set ( body , true ) ;
} else if ( body . sleepCounter > 0 ) {
body . sleepCounter -= 1 ;
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method afterCollisions
* @ param { pair [ ] } pairs
* /
2014-02-19 09:15:05 -05:00
Sleeping . afterCollisions = function ( pairs ) {
// wake up bodies involved in collisions
for ( var i = 0 ; i < pairs . length ; i ++ ) {
2014-03-11 06:29:14 -04:00
var pair = pairs [ i ] ;
// don't wake inactive pairs
if ( ! pair . isActive )
continue ;
var collision = pair . collision ,
2014-02-19 09:15:05 -05:00
bodyA = collision . bodyA ,
bodyB = collision . bodyB ;
2014-03-11 06:29:14 -04:00
// don't wake if at least one body is static
2014-02-19 09:15:05 -05:00
if ( ( bodyA . isSleeping && bodyB . isSleeping ) || bodyA . isStatic || bodyB . isStatic )
continue ;
if ( bodyA . isSleeping || bodyB . isSleeping ) {
var sleepingBody = ( bodyA . isSleeping && ! bodyA . isStatic ) ? bodyA : bodyB ,
movingBody = sleepingBody === bodyA ? bodyB : bodyA ;
if ( ! sleepingBody . isStatic && movingBody . motion > _motionWakeThreshold ) {
Sleeping . set ( sleepingBody , false ) ;
}
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method set
* @ param { body } body
* @ param { boolean } isSleeping
* /
2014-02-19 09:15:05 -05:00
Sleeping . set = function ( body , isSleeping ) {
if ( isSleeping ) {
body . isSleeping = true ;
body . sleepCounter = body . sleepThreshold ;
body . positionImpulse . x = 0 ;
body . positionImpulse . y = 0 ;
body . positionPrev . x = body . position . x ;
body . positionPrev . y = body . position . y ;
body . anglePrev = body . angle ;
body . speed = 0 ;
body . angularSpeed = 0 ;
body . motion = 0 ;
} else {
body . isSleeping = false ;
body . sleepCounter = 0 ;
}
} ;
} ) ( ) ;
; // End src/core/Sleeping.js
// Begin src/factory/Bodies.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Bodies ` module contains factory methods for creating rigid body models
* with commonly used body configurations ( such as rectangles , circles and other polygons ) .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Bodies
* /
2014-02-19 09:15:05 -05:00
// TODO: true circle bodies
var Bodies = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new rigid body model with a rectangle hull .
* The options parameter is an object that specifies any properties you wish to override the defaults .
* See the properites section of the ` Matter.Body ` module for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method rectangle
* @ param { number } x
* @ param { number } y
* @ param { number } width
* @ param { number } height
2014-06-09 14:40:24 -04:00
* @ param { object } [ options ]
2014-02-28 20:10:08 -05:00
* @ return { body } A new rectangle body
* /
2014-02-19 09:15:05 -05:00
Bodies . rectangle = function ( x , y , width , height , options ) {
options = options || { } ;
var rectangle = {
2014-05-01 09:09:06 -04:00
label : 'Rectangle Body' ,
2014-03-22 13:51:49 -04:00
position : { x : x , y : y } ,
2014-05-05 14:32:51 -04:00
vertices : Vertices . fromPath ( 'L 0 0 L ' + width + ' 0 L ' + width + ' ' + height + ' L 0 ' + height )
2014-03-22 13:51:49 -04:00
} ;
2014-02-19 09:15:05 -05:00
2014-05-05 14:32:51 -04:00
if ( options . chamfer ) {
var chamfer = options . chamfer ;
rectangle . vertices = Vertices . chamfer ( rectangle . vertices , chamfer . radius ,
chamfer . quality , chamfer . qualityMin , chamfer . qualityMax ) ;
delete options . chamfer ;
}
2014-02-19 09:15:05 -05:00
return Body . create ( Common . extend ( { } , rectangle , options ) ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new rigid body model with a trapezoid hull .
* The options parameter is an object that specifies any properties you wish to override the defaults .
* See the properites section of the ` Matter.Body ` module for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method trapezoid
* @ param { number } x
* @ param { number } y
* @ param { number } width
* @ param { number } height
* @ param { number } slope
2014-06-09 14:40:24 -04:00
* @ param { object } [ options ]
2014-02-28 20:10:08 -05:00
* @ return { body } A new trapezoid body
* /
2014-02-19 09:15:05 -05:00
Bodies . trapezoid = function ( x , y , width , height , slope , options ) {
options = options || { } ;
slope *= 0.5 ;
var roof = ( 1 - ( slope * 2 ) ) * width ;
var x1 = width * slope ,
x2 = x1 + roof ,
x3 = x2 + x1 ;
var trapezoid = {
2014-05-01 09:09:06 -04:00
label : 'Trapezoid Body' ,
2014-03-22 13:51:49 -04:00
position : { x : x , y : y } ,
2014-05-05 14:32:51 -04:00
vertices : Vertices . fromPath ( 'L 0 0 L ' + x1 + ' ' + ( - height ) + ' L ' + x2 + ' ' + ( - height ) + ' L ' + x3 + ' 0' )
2014-03-22 13:51:49 -04:00
} ;
2014-02-19 09:15:05 -05:00
2014-05-05 14:32:51 -04:00
if ( options . chamfer ) {
var chamfer = options . chamfer ;
trapezoid . vertices = Vertices . chamfer ( trapezoid . vertices , chamfer . radius ,
chamfer . quality , chamfer . qualityMin , chamfer . qualityMax ) ;
delete options . chamfer ;
}
2014-02-19 09:15:05 -05:00
return Body . create ( Common . extend ( { } , trapezoid , options ) ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new rigid body model with a circle hull .
* The options parameter is an object that specifies any properties you wish to override the defaults .
* See the properites section of the ` Matter.Body ` module for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method circle
* @ param { number } x
* @ param { number } y
* @ param { number } radius
2014-06-09 14:40:24 -04:00
* @ param { object } [ options ]
2014-02-28 20:10:08 -05:00
* @ param { number } maxSides
* @ return { body } A new circle body
* /
2014-02-19 09:15:05 -05:00
Bodies . circle = function ( x , y , radius , options , maxSides ) {
options = options || { } ;
2014-05-01 09:09:06 -04:00
options . label = 'Circle Body' ;
2014-02-19 09:15:05 -05:00
// approximate circles with polygons until true circles implemented in SAT
maxSides = maxSides || 25 ;
var sides = Math . ceil ( Math . max ( 10 , Math . min ( maxSides , radius ) ) ) ;
// optimisation: always use even number of sides (half the number of unique axes)
if ( sides % 2 === 1 )
sides += 1 ;
// flag for better rendering
options . circleRadius = radius ;
return Bodies . polygon ( x , y , sides , radius , options ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new rigid body model with a regular polygon hull with the given number of sides .
* The options parameter is an object that specifies any properties you wish to override the defaults .
* See the properites section of the ` Matter.Body ` module for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method polygon
* @ param { number } x
* @ param { number } y
* @ param { number } sides
* @ param { number } radius
2014-06-09 14:40:24 -04:00
* @ param { object } [ options ]
2014-02-28 20:10:08 -05:00
* @ return { body } A new regular polygon body
* /
2014-02-19 09:15:05 -05:00
Bodies . polygon = function ( x , y , sides , radius , options ) {
options = options || { } ;
if ( sides < 3 )
return Bodies . circle ( x , y , radius , options ) ;
var theta = 2 * Math . PI / sides ,
path = '' ,
offset = theta * 0.5 ;
for ( var i = 0 ; i < sides ; i += 1 ) {
var angle = offset + ( i * theta ) ,
xx = Math . cos ( angle ) * radius ,
yy = Math . sin ( angle ) * radius ;
path += 'L ' + xx . toFixed ( 3 ) + ' ' + yy . toFixed ( 3 ) + ' ' ;
}
var polygon = {
2014-05-01 09:09:06 -04:00
label : 'Polygon Body' ,
2014-03-22 13:51:49 -04:00
position : { x : x , y : y } ,
2014-05-05 14:32:51 -04:00
vertices : Vertices . fromPath ( path )
2014-03-22 13:51:49 -04:00
} ;
2014-02-19 09:15:05 -05:00
2014-05-05 14:32:51 -04:00
if ( options . chamfer ) {
var chamfer = options . chamfer ;
polygon . vertices = Vertices . chamfer ( polygon . vertices , chamfer . radius ,
chamfer . quality , chamfer . qualityMin , chamfer . qualityMax ) ;
delete options . chamfer ;
}
2014-02-19 09:15:05 -05:00
return Body . create ( Common . extend ( { } , polygon , options ) ) ;
} ;
} ) ( ) ;
; // End src/factory/Bodies.js
// Begin src/factory/Composites.js
2014-02-28 20:10:08 -05:00
/ * *
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Composites
* /
2014-02-19 09:15:05 -05:00
var Composites = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method stack
* @ param { number } xx
* @ param { number } yy
* @ param { number } columns
* @ param { number } rows
* @ param { number } columnGap
* @ param { number } rowGap
* @ param { function } callback
* @ return { composite } A new composite containing objects created in the callback
* /
2014-02-19 09:15:05 -05:00
Composites . stack = function ( xx , yy , columns , rows , columnGap , rowGap , callback ) {
2014-05-01 09:09:06 -04:00
var stack = Composite . create ( { label : 'Stack' } ) ,
2014-02-19 09:15:05 -05:00
x = xx ,
y = yy ,
lastBody ,
i = 0 ;
for ( var row = 0 ; row < rows ; row ++ ) {
var maxHeight = 0 ;
for ( var column = 0 ; column < columns ; column ++ ) {
var body = callback ( x , y , column , row , lastBody , i ) ;
if ( body ) {
var bodyHeight = body . bounds . max . y - body . bounds . min . y ,
bodyWidth = body . bounds . max . x - body . bounds . min . x ;
if ( bodyHeight > maxHeight )
maxHeight = bodyHeight ;
Body . translate ( body , { x : bodyWidth * 0.5 , y : bodyHeight * 0.5 } ) ;
x = body . bounds . max . x + columnGap ;
Composite . addBody ( stack , body ) ;
lastBody = body ;
i += 1 ;
}
}
y += maxHeight + rowGap ;
x = xx ;
}
return stack ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method chain
* @ param { composite } composite
* @ param { number } xOffsetA
* @ param { number } yOffsetA
* @ param { number } xOffsetB
* @ param { number } yOffsetB
* @ param { object } options
* @ return { composite } A new composite containing objects chained together with constraints
* /
2014-02-19 09:15:05 -05:00
Composites . chain = function ( composite , xOffsetA , yOffsetA , xOffsetB , yOffsetB , options ) {
var bodies = composite . bodies ;
for ( var i = 1 ; i < bodies . length ; i ++ ) {
var bodyA = bodies [ i - 1 ] ,
bodyB = bodies [ i ] ,
bodyAHeight = bodyA . bounds . max . y - bodyA . bounds . min . y ,
bodyAWidth = bodyA . bounds . max . x - bodyA . bounds . min . x ,
bodyBHeight = bodyB . bounds . max . y - bodyB . bounds . min . y ,
bodyBWidth = bodyB . bounds . max . x - bodyB . bounds . min . x ;
var defaults = {
bodyA : bodyA ,
pointA : { x : bodyAWidth * xOffsetA , y : bodyAHeight * yOffsetA } ,
bodyB : bodyB ,
pointB : { x : bodyBWidth * xOffsetB , y : bodyBHeight * yOffsetB }
} ;
var constraint = Common . extend ( defaults , options ) ;
Composite . addConstraint ( composite , Constraint . create ( constraint ) ) ;
}
2014-05-01 09:09:06 -04:00
composite . label += ' Chain' ;
2014-02-19 09:15:05 -05:00
return composite ;
} ;
2014-03-30 14:45:30 -04:00
/ * *
* Connects bodies in the composite with constraints in a grid pattern , with optional cross braces
* @ method mesh
* @ param { composite } composite
* @ param { number } columns
* @ param { number } rows
* @ param { boolean } crossBrace
* @ param { object } options
* @ return { composite } The composite containing objects meshed together with constraints
* /
Composites . mesh = function ( composite , columns , rows , crossBrace , options ) {
var bodies = composite . bodies ,
row ,
col ,
bodyA ,
bodyB ,
bodyC ;
for ( row = 0 ; row < rows ; row ++ ) {
for ( col = 0 ; col < columns ; col ++ ) {
if ( col > 0 ) {
bodyA = bodies [ ( col - 1 ) + ( row * columns ) ] ;
bodyB = bodies [ col + ( row * columns ) ] ;
Composite . addConstraint ( composite , Constraint . create ( Common . extend ( { bodyA : bodyA , bodyB : bodyB } , options ) ) ) ;
}
}
for ( col = 0 ; col < columns ; col ++ ) {
if ( row > 0 ) {
bodyA = bodies [ col + ( ( row - 1 ) * columns ) ] ;
bodyB = bodies [ col + ( row * columns ) ] ;
Composite . addConstraint ( composite , Constraint . create ( Common . extend ( { bodyA : bodyA , bodyB : bodyB } , options ) ) ) ;
if ( crossBrace && col > 0 ) {
bodyC = bodies [ ( col - 1 ) + ( ( row - 1 ) * columns ) ] ;
Composite . addConstraint ( composite , Constraint . create ( Common . extend ( { bodyA : bodyC , bodyB : bodyB } , options ) ) ) ;
}
if ( crossBrace && col < columns - 1 ) {
bodyC = bodies [ ( col + 1 ) + ( ( row - 1 ) * columns ) ] ;
Composite . addConstraint ( composite , Constraint . create ( Common . extend ( { bodyA : bodyC , bodyB : bodyB } , options ) ) ) ;
}
}
}
}
2014-05-01 09:09:06 -04:00
composite . label += ' Mesh' ;
2014-03-30 14:45:30 -04:00
return composite ;
} ;
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method pyramid
* @ param { number } xx
* @ param { number } yy
* @ param { number } columns
* @ param { number } rows
* @ param { number } columnGap
* @ param { number } rowGap
* @ param { function } callback
* @ return { composite } A new composite containing objects created in the callback
* /
2014-02-19 09:15:05 -05:00
Composites . pyramid = function ( xx , yy , columns , rows , columnGap , rowGap , callback ) {
return Composites . stack ( xx , yy , columns , rows , columnGap , rowGap , function ( x , y , column , row , lastBody , i ) {
var actualRows = Math . min ( rows , Math . ceil ( columns / 2 ) ) ,
lastBodyWidth = lastBody ? lastBody . bounds . max . x - lastBody . bounds . min . x : 0 ;
if ( row > actualRows )
return ;
// reverse row order
row = actualRows - row ;
var start = row ,
end = columns - 1 - row ;
if ( column < start || column > end )
return ;
// retroactively fix the first body's position, since width was unknown
if ( i === 1 ) {
Body . translate ( lastBody , { x : ( column + ( columns % 2 === 1 ? 1 : - 1 ) ) * lastBodyWidth , y : 0 } ) ;
}
var xOffset = lastBody ? column * lastBodyWidth : 0 ;
return callback ( xx + xOffset + column * columnGap , y , column , row , lastBody , i ) ;
} ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method newtonsCradle
* @ param { number } xx
* @ param { number } yy
* @ param { number } number
* @ param { number } size
* @ param { number } length
* @ return { composite } A new composite newtonsCradle body
* /
2014-02-19 09:15:05 -05:00
Composites . newtonsCradle = function ( xx , yy , number , size , length ) {
2014-05-01 09:09:06 -04:00
var newtonsCradle = Composite . create ( { label : 'Newtons Cradle' } ) ;
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < number ; i ++ ) {
var separation = 1.9 ,
circle = Bodies . circle ( xx + i * ( size * separation ) , yy + length , size ,
2014-03-30 14:45:30 -04:00
{ inertia : 99999 , restitution : 1 , friction : 0 , frictionAir : 0.0001 , slop : 0.01 } ) ,
2014-02-19 09:15:05 -05:00
constraint = Constraint . create ( { pointA : { x : xx + i * ( size * separation ) , y : yy } , bodyB : circle } ) ;
Composite . addBody ( newtonsCradle , circle ) ;
Composite . addConstraint ( newtonsCradle , constraint ) ;
}
return newtonsCradle ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method car
* @ param { number } xx
* @ param { number } yy
* @ param { number } width
* @ param { number } height
* @ param { number } wheelSize
* @ return { composite } A new composite car body
* /
2014-02-19 09:15:05 -05:00
Composites . car = function ( xx , yy , width , height , wheelSize ) {
var groupId = Body . nextGroupId ( ) ,
wheelBase = - 20 ,
wheelAOffset = - width * 0.5 + wheelBase ,
wheelBOffset = width * 0.5 - wheelBase ,
wheelYOffset = 0 ;
2014-05-01 09:09:06 -04:00
var car = Composite . create ( { label : 'Car' } ) ,
2014-05-05 14:32:51 -04:00
body = Bodies . trapezoid ( xx , yy , width , height , 0.3 , {
groupId : groupId ,
friction : 0.01 ,
chamfer : {
radius : 10
}
} ) ;
2014-02-19 09:15:05 -05:00
var wheelA = Bodies . circle ( xx + wheelAOffset , yy + wheelYOffset , wheelSize , {
groupId : groupId ,
restitution : 0.5 ,
friction : 0.9 ,
density : 0.01
} ) ;
var wheelB = Bodies . circle ( xx + wheelBOffset , yy + wheelYOffset , wheelSize , {
groupId : groupId ,
restitution : 0.5 ,
friction : 0.9 ,
density : 0.01
} ) ;
var axelA = Constraint . create ( {
bodyA : body ,
pointA : { x : wheelAOffset , y : wheelYOffset } ,
bodyB : wheelA ,
stiffness : 0.5
} ) ;
var axelB = Constraint . create ( {
bodyA : body ,
pointA : { x : wheelBOffset , y : wheelYOffset } ,
bodyB : wheelB ,
stiffness : 0.5
} ) ;
Composite . addBody ( car , body ) ;
Composite . addBody ( car , wheelA ) ;
Composite . addBody ( car , wheelB ) ;
Composite . addConstraint ( car , axelA ) ;
Composite . addConstraint ( car , axelB ) ;
return car ;
} ;
2014-03-30 14:45:30 -04:00
/ * *
* Creates a simple soft body like object
* @ method softBody
* @ param { number } xx
* @ param { number } yy
* @ param { number } columns
* @ param { number } rows
* @ param { number } columnGap
* @ param { number } rowGap
* @ param { boolean } crossBrace
* @ param { number } particleRadius
* @ param { } particleOptions
* @ param { } constraintOptions
* @ return { composite } A new composite softBody
* /
Composites . softBody = function ( xx , yy , columns , rows , columnGap , rowGap , crossBrace , particleRadius , particleOptions , constraintOptions ) {
particleOptions = Common . extend ( { inertia : Infinity } , particleOptions ) ;
constraintOptions = Common . extend ( { stiffness : 0.4 } , constraintOptions ) ;
var softBody = Composites . stack ( xx , yy , columns , rows , columnGap , rowGap , function ( x , y , column , row ) {
return Bodies . circle ( x , y , particleRadius , particleOptions ) ;
} ) ;
Composites . mesh ( softBody , columns , rows , crossBrace , constraintOptions ) ;
2014-05-01 09:09:06 -04:00
softBody . label = 'Soft Body' ;
2014-03-30 14:45:30 -04:00
return softBody ;
} ;
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/factory/Composites.js
// Begin src/geometry/Axes.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Axes
* /
2014-02-19 09:15:05 -05:00
var Axes = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method fromVertices
* @ param { vertices } vertices
* @ return { axes } A new axes from the given vertices
* /
2014-02-19 09:15:05 -05:00
Axes . fromVertices = function ( vertices ) {
var axes = { } ;
// find the unique axes, using edge normal gradients
for ( var i = 0 ; i < vertices . length ; i ++ ) {
var j = ( i + 1 ) % vertices . length ,
normal = Vector . normalise ( {
x : vertices [ j ] . y - vertices [ i ] . y ,
y : vertices [ i ] . x - vertices [ j ] . x
} ) ,
gradient = ( normal . y === 0 ) ? Infinity : ( normal . x / normal . y ) ;
// limit precision
gradient = gradient . toFixed ( 3 ) . toString ( ) ;
axes [ gradient ] = normal ;
}
return Common . values ( axes ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method rotate
* @ param { axes } axes
* @ param { number } angle
* /
2014-02-19 09:15:05 -05:00
Axes . rotate = function ( axes , angle ) {
if ( angle === 0 )
return ;
var cos = Math . cos ( angle ) ,
sin = Math . sin ( angle ) ;
for ( var i = 0 ; i < axes . length ; i ++ ) {
var axis = axes [ i ] ,
xx ;
xx = axis . x * cos - axis . y * sin ;
axis . y = axis . x * sin + axis . y * cos ;
axis . x = xx ;
}
} ;
} ) ( ) ;
; // End src/geometry/Axes.js
// Begin src/geometry/Bounds.js
2014-02-28 20:10:08 -05:00
/ * *
* _Internal Class _ , not generally used outside of the engine ' s internals .
*
* @ class Bounds
* /
2014-02-19 09:15:05 -05:00
var Bounds = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method create
* @ param { vertices } vertices
* @ return { bounds } A new bounds object
* /
2014-02-19 09:15:05 -05:00
Bounds . create = function ( vertices ) {
var bounds = {
min : { x : 0 , y : 0 } ,
max : { x : 0 , y : 0 }
} ;
2014-05-01 09:09:06 -04:00
if ( vertices )
Bounds . update ( bounds , vertices ) ;
2014-02-19 09:15:05 -05:00
return bounds ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method update
* @ param { bounds } bounds
* @ param { vertices } vertices
* @ param { vector } velocity
* /
2014-02-19 09:15:05 -05:00
Bounds . update = function ( bounds , vertices , velocity ) {
bounds . min . x = Number . MAX _VALUE ;
bounds . max . x = Number . MIN _VALUE ;
bounds . min . y = Number . MAX _VALUE ;
bounds . max . y = Number . MIN _VALUE ;
for ( var i = 0 ; i < vertices . length ; i ++ ) {
var vertex = vertices [ i ] ;
if ( vertex . x > bounds . max . x ) bounds . max . x = vertex . x ;
if ( vertex . x < bounds . min . x ) bounds . min . x = vertex . x ;
if ( vertex . y > bounds . max . y ) bounds . max . y = vertex . y ;
if ( vertex . y < bounds . min . y ) bounds . min . y = vertex . y ;
}
if ( velocity ) {
if ( velocity . x > 0 ) {
bounds . max . x += velocity . x ;
} else {
bounds . min . x += velocity . x ;
}
if ( velocity . y > 0 ) {
bounds . max . y += velocity . y ;
} else {
bounds . min . y += velocity . y ;
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method contains
* @ param { bounds } bounds
* @ param { vector } point
* @ return { boolean } True if the bounds contain the point , otherwise false
* /
2014-02-19 09:15:05 -05:00
Bounds . contains = function ( bounds , point ) {
return point . x >= bounds . min . x && point . x <= bounds . max . x
&& point . y >= bounds . min . y && point . y <= bounds . max . y ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method overlaps
* @ param { bounds } boundsA
* @ param { bounds } boundsB
* @ return { boolean } True if the bounds overlap , otherwise false
* /
2014-02-19 09:15:05 -05:00
Bounds . overlaps = function ( boundsA , boundsB ) {
return ( boundsA . min . x <= boundsB . max . x && boundsA . max . x >= boundsB . min . x
&& boundsA . max . y >= boundsB . min . y && boundsA . min . y <= boundsB . max . y ) ;
} ;
2014-05-01 09:09:06 -04:00
/ * *
* Translates the bounds by the given vector
* @ method translate
* @ param { bounds } bounds
* @ param { vector } vector
* /
Bounds . translate = function ( bounds , vector ) {
bounds . min . x += vector . x ;
bounds . max . x += vector . x ;
bounds . min . y += vector . y ;
bounds . max . y += vector . y ;
} ;
/ * *
* Shifts the bounds to the given position
* @ method shift
* @ param { bounds } bounds
* @ param { vector } position
* /
Bounds . shift = function ( bounds , position ) {
var deltaX = bounds . max . x - bounds . min . x ,
deltaY = bounds . max . y - bounds . min . y ;
bounds . min . x = position . x ;
bounds . max . x = position . x + deltaX ;
bounds . min . y = position . y ;
bounds . max . y = position . y + deltaY ;
} ;
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/geometry/Bounds.js
// Begin src/geometry/Vector.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Vector ` module contains methods for creating and manipulating vectors .
* Vectors are the basis of all the geometry related operations in the engine .
* A ` Matter.Vector ` object is of the form ` { x: 0, y: 0 } ` .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Vector
* /
2014-02-19 09:15:05 -05:00
// TODO: consider params for reusing vector objects
var Vector = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns a new vector with ` x ` and ` y ` copied from the given ` vector ` .
* @ method clone
* @ param { vector } vector
* @ return { vector } A new cloned vector
* /
Vector . clone = function ( vector ) {
return { x : vector . x , y : vector . y } ;
} ;
/ * *
* Returns the magnitude ( length ) of a vector .
2014-02-28 20:10:08 -05:00
* @ method magnitude
* @ param { vector } vector
* @ return { number } The magnitude of the vector
* /
2014-02-19 09:15:05 -05:00
Vector . magnitude = function ( vector ) {
return Math . sqrt ( ( vector . x * vector . x ) + ( vector . y * vector . y ) ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the magnitude ( length ) of a vector ( therefore saving a ` sqrt ` operation ) .
2014-02-28 20:10:08 -05:00
* @ method magnitudeSquared
* @ param { vector } vector
* @ return { number } The squared magnitude of the vector
* /
2014-02-19 09:15:05 -05:00
Vector . magnitudeSquared = function ( vector ) {
return ( vector . x * vector . x ) + ( vector . y * vector . y ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Rotates the vector about ( 0 , 0 ) by specified angle .
2014-02-28 20:10:08 -05:00
* @ method rotate
* @ param { vector } vector
* @ param { number } angle
2014-06-09 14:40:24 -04:00
* @ return { vector } A new vector rotated about ( 0 , 0 )
2014-02-28 20:10:08 -05:00
* /
2014-02-19 09:15:05 -05:00
Vector . rotate = function ( vector , angle ) {
var cos = Math . cos ( angle ) , sin = Math . sin ( angle ) ;
return {
x : vector . x * cos - vector . y * sin ,
y : vector . x * sin + vector . y * cos
} ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Rotates the vector about a specified point by specified angle .
2014-02-28 20:10:08 -05:00
* @ method rotateAbout
* @ param { vector } vector
* @ param { number } angle
* @ param { vector } point
* @ return { vector } A new vector rotated about the point
* /
2014-02-19 09:15:05 -05:00
Vector . rotateAbout = function ( vector , angle , point ) {
var cos = Math . cos ( angle ) , sin = Math . sin ( angle ) ;
return {
x : point . x + ( ( vector . x - point . x ) * cos - ( vector . y - point . y ) * sin ) ,
y : point . y + ( ( vector . x - point . x ) * sin + ( vector . y - point . y ) * cos )
} ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Normalises a vector ( such that its magnitude is ` 1 ` ) .
2014-02-28 20:10:08 -05:00
* @ method normalise
* @ param { vector } vector
* @ return { vector } A new vector normalised
* /
2014-02-19 09:15:05 -05:00
Vector . normalise = function ( vector ) {
var magnitude = Vector . magnitude ( vector ) ;
if ( magnitude === 0 )
return { x : 0 , y : 0 } ;
return { x : vector . x / magnitude , y : vector . y / magnitude } ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the dot - product of two vectors .
2014-02-28 20:10:08 -05:00
* @ method dot
* @ param { vector } vectorA
* @ param { vector } vectorB
* @ return { number } The dot product of the two vectors
* /
2014-02-19 09:15:05 -05:00
Vector . dot = function ( vectorA , vectorB ) {
return ( vectorA . x * vectorB . x ) + ( vectorA . y * vectorB . y ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the cross - product of two vectors .
2014-02-28 20:10:08 -05:00
* @ method cross
* @ param { vector } vectorA
* @ param { vector } vectorB
* @ return { number } The cross product of the two vectors
* /
2014-02-19 09:15:05 -05:00
Vector . cross = function ( vectorA , vectorB ) {
return ( vectorA . x * vectorB . y ) - ( vectorA . y * vectorB . x ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Adds the two vectors .
2014-02-28 20:10:08 -05:00
* @ method add
* @ param { vector } vectorA
* @ param { vector } vectorB
2014-06-09 14:40:24 -04:00
* @ return { vector } A new vector of vectorA and vectorB added
2014-02-28 20:10:08 -05:00
* /
2014-02-19 09:15:05 -05:00
Vector . add = function ( vectorA , vectorB ) {
return { x : vectorA . x + vectorB . x , y : vectorA . y + vectorB . y } ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Subtracts the two vectors .
2014-02-28 20:10:08 -05:00
* @ method sub
* @ param { vector } vectorA
* @ param { vector } vectorB
2014-06-09 14:40:24 -04:00
* @ return { vector } A new vector of vectorA and vectorB subtracted
2014-02-28 20:10:08 -05:00
* /
2014-02-19 09:15:05 -05:00
Vector . sub = function ( vectorA , vectorB ) {
return { x : vectorA . x - vectorB . x , y : vectorA . y - vectorB . y } ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Multiplies a vector and a scalar .
2014-02-28 20:10:08 -05:00
* @ method mult
* @ param { vector } vector
* @ param { number } scalar
* @ return { vector } A new vector multiplied by scalar
* /
2014-02-19 09:15:05 -05:00
Vector . mult = function ( vector , scalar ) {
return { x : vector . x * scalar , y : vector . y * scalar } ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Divides a vector and a scalar .
2014-02-28 20:10:08 -05:00
* @ method div
* @ param { vector } vector
* @ param { number } scalar
* @ return { vector } A new vector divided by scalar
* /
2014-02-19 09:15:05 -05:00
Vector . div = function ( vector , scalar ) {
return { x : vector . x / scalar , y : vector . y / scalar } ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the perpendicular vector . Set ` negate ` to true for the perpendicular in the opposite direction .
2014-02-28 20:10:08 -05:00
* @ method perp
* @ param { vector } vector
2014-06-09 14:40:24 -04:00
* @ param { bool } [ negate = false ]
2014-02-28 20:10:08 -05:00
* @ return { vector } The perpendicular vector
* /
2014-02-19 09:15:05 -05:00
Vector . perp = function ( vector , negate ) {
negate = negate === true ? - 1 : 1 ;
return { x : negate * - vector . y , y : negate * vector . x } ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Negates both components of a vector such that it points in the opposite direction .
2014-02-28 20:10:08 -05:00
* @ method neg
* @ param { vector } vector
* @ return { vector } The negated vector
* /
2014-02-19 09:15:05 -05:00
Vector . neg = function ( vector ) {
return { x : - vector . x , y : - vector . y } ;
} ;
2014-05-01 09:09:06 -04:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the angle in radians between the two vectors relative to the x - axis .
2014-05-01 09:09:06 -04:00
* @ method angle
* @ param { vector } vectorA
* @ param { vector } vectorB
* @ return { number } The angle in radians
* /
Vector . angle = function ( vectorA , vectorB ) {
return Math . atan2 ( vectorB . y - vectorA . y , vectorB . x - vectorA . x ) ;
} ;
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/geometry/Vector.js
// Begin src/geometry/Vertices.js
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Vertices ` module contains methods for creating and manipulating sets of vertices .
* A set of vertices is an array of ` Matter.Vector ` with additional indexing properties inserted by ` Vertices.create ` .
* A ` Matter.Body ` maintains a set of vertices to represent the shape of the object ( its convex hull ) .
*
2014-02-28 20:10:08 -05:00
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class Vertices
* /
2014-02-19 09:15:05 -05:00
// TODO: convex decomposition - http://mnbayazit.com/406/bayazit
var Vertices = { } ;
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new set of ` Matter.Body ` compatible vertices .
* The ` points ` argument accepts an array of ` Matter.Vector ` points orientated around the origin ` (0, 0) ` , for example :
*
* [ { x : 0 , y : 0 } , { x : 25 , y : 50 } , { x : 50 , y : 0 } ]
*
* The ` Vertices.create ` method returns a new array of vertices , which are similar to Matter . Vector objects ,
* but with some additional references required for efficient collision detection routines .
*
* Note that the ` body ` argument is not optional , a ` Matter.Body ` reference must be provided .
*
2014-02-28 20:10:08 -05:00
* @ method create
2014-06-09 14:40:24 -04:00
* @ param { vector [ ] } points
2014-02-28 20:10:08 -05:00
* @ param { body } body
* /
2014-06-09 14:40:24 -04:00
Vertices . create = function ( points , body ) {
var vertices = [ ] ;
for ( var i = 0 ; i < points . length ; i ++ ) {
var point = points [ i ] ,
vertex = { } ;
vertex . x = point . x ;
vertex . y = point . y ;
vertex . index = i ;
vertex . body = body ;
vertices . push ( vertex ) ;
2014-02-19 09:15:05 -05:00
}
2014-06-09 14:40:24 -04:00
return vertices ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Parses a _simple _ SVG - style path into a ` Matter.Vertices ` object for the given ` Matter.Body ` .
2014-02-28 20:10:08 -05:00
* @ method fromPath
* @ param { string } path
2014-06-09 14:40:24 -04:00
* @ param { body } body
2014-02-28 20:10:08 -05:00
* @ return { vertices } vertices
* /
2014-06-09 14:40:24 -04:00
Vertices . fromPath = function ( path , body ) {
2014-02-19 09:15:05 -05:00
var pathPattern = /L\s*([\-\d\.]*)\s*([\-\d\.]*)/ig ,
2014-06-09 14:40:24 -04:00
points = [ ] ;
2014-02-19 09:15:05 -05:00
path . replace ( pathPattern , function ( match , x , y ) {
2014-06-09 14:40:24 -04:00
points . push ( { x : parseFloat ( x , 10 ) , y : parseFloat ( y , 10 ) } ) ;
2014-02-19 09:15:05 -05:00
} ) ;
2014-06-09 14:40:24 -04:00
return Vertices . create ( points , body ) ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the centre ( centroid ) of the set of vertices .
2014-02-28 20:10:08 -05:00
* @ method centre
* @ param { vertices } vertices
* @ return { vector } The centre point
* /
2014-02-19 09:15:05 -05:00
Vertices . centre = function ( vertices ) {
2014-05-05 14:32:51 -04:00
var area = Vertices . area ( vertices , true ) ,
centre = { x : 0 , y : 0 } ,
cross ,
temp ,
j ;
2014-02-19 09:15:05 -05:00
for ( var i = 0 ; i < vertices . length ; i ++ ) {
2014-05-05 14:32:51 -04:00
j = ( i + 1 ) % vertices . length ;
cross = Vector . cross ( vertices [ i ] , vertices [ j ] ) ;
temp = Vector . mult ( Vector . add ( vertices [ i ] , vertices [ j ] ) , cross ) ;
centre = Vector . add ( centre , temp ) ;
2014-02-19 09:15:05 -05:00
}
2014-05-05 14:32:51 -04:00
return Vector . div ( centre , 6 * area ) ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the area of the set of vertices .
2014-02-28 20:10:08 -05:00
* @ method area
* @ param { vertices } vertices
2014-05-05 14:32:51 -04:00
* @ param { bool } signed
2014-02-28 20:10:08 -05:00
* @ return { number } The area
* /
2014-05-05 14:32:51 -04:00
Vertices . area = function ( vertices , signed ) {
2014-02-19 09:15:05 -05:00
var area = 0 ,
j = vertices . length - 1 ;
for ( var i = 0 ; i < vertices . length ; i ++ ) {
area += ( vertices [ j ] . x - vertices [ i ] . x ) * ( vertices [ j ] . y + vertices [ i ] . y ) ;
j = i ;
}
2014-05-05 14:32:51 -04:00
if ( signed )
return area / 2 ;
2014-02-19 09:15:05 -05:00
return Math . abs ( area ) / 2 ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns the moment of inertia ( second moment of area ) of the set of vertices given the total mass .
2014-02-28 20:10:08 -05:00
* @ method inertia
* @ param { vertices } vertices
* @ param { number } mass
2014-06-09 14:40:24 -04:00
* @ return { number } The polygon ' s moment of inertia
2014-02-28 20:10:08 -05:00
* /
2014-02-19 09:15:05 -05:00
Vertices . inertia = function ( vertices , mass ) {
var numerator = 0 ,
denominator = 0 ,
v = vertices ,
cross ,
j ;
// find the polygon's moment of inertia, using second moment of area
// http://www.physicsforums.com/showthread.php?t=25293
for ( var n = 0 ; n < v . length ; n ++ ) {
j = ( n + 1 ) % v . length ;
cross = Math . abs ( Vector . cross ( v [ j ] , v [ n ] ) ) ;
numerator += cross * ( Vector . dot ( v [ j ] , v [ j ] ) + Vector . dot ( v [ j ] , v [ n ] ) + Vector . dot ( v [ n ] , v [ n ] ) ) ;
denominator += cross ;
}
return ( mass / 6 ) * ( numerator / denominator ) ;
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Translates the set of vertices in - place .
2014-02-28 20:10:08 -05:00
* @ method translate
* @ param { vertices } vertices
* @ param { vector } vector
* @ param { number } scalar
* /
2014-02-19 09:15:05 -05:00
Vertices . translate = function ( vertices , vector , scalar ) {
var i ;
if ( scalar ) {
for ( i = 0 ; i < vertices . length ; i ++ ) {
vertices [ i ] . x += vector . x * scalar ;
vertices [ i ] . y += vector . y * scalar ;
}
} else {
for ( i = 0 ; i < vertices . length ; i ++ ) {
vertices [ i ] . x += vector . x ;
vertices [ i ] . y += vector . y ;
}
2014-06-09 14:40:24 -04:00
}
return vertices ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Rotates the set of vertices in - place .
2014-02-28 20:10:08 -05:00
* @ method rotate
* @ param { vertices } vertices
* @ param { number } angle
* @ param { vector } point
* /
2014-02-19 09:15:05 -05:00
Vertices . rotate = function ( vertices , angle , point ) {
if ( angle === 0 )
return ;
var cos = Math . cos ( angle ) ,
sin = Math . sin ( angle ) ;
for ( var i = 0 ; i < vertices . length ; i ++ ) {
var vertice = vertices [ i ] ,
dx = vertice . x - point . x ,
dy = vertice . y - point . y ;
vertice . x = point . x + ( dx * cos - dy * sin ) ;
vertice . y = point . y + ( dx * sin + dy * cos ) ;
}
2014-06-09 14:40:24 -04:00
return vertices ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Returns ` true ` if the ` point ` is inside the set of ` vertices ` .
2014-02-28 20:10:08 -05:00
* @ method contains
* @ param { vertices } vertices
* @ param { vector } point
* @ return { boolean } True if the vertices contains point , otherwise false
* /
2014-02-19 09:15:05 -05:00
Vertices . contains = function ( vertices , point ) {
for ( var i = 0 ; i < vertices . length ; i ++ ) {
var vertice = vertices [ i ] ,
nextVertice = vertices [ ( i + 1 ) % vertices . length ] ;
if ( ( point . x - vertice . x ) * ( nextVertice . y - vertice . y ) + ( point . y - vertice . y ) * ( vertice . x - nextVertice . x ) > 0 ) {
return false ;
}
}
return true ;
} ;
2014-05-01 09:09:06 -04:00
/ * *
2014-06-09 14:40:24 -04:00
* Scales the vertices from a point ( default is centre ) in - place .
2014-05-01 09:09:06 -04:00
* @ method scale
* @ param { vertices } vertices
* @ param { number } scaleX
* @ param { number } scaleY
* @ param { vector } point
* /
Vertices . scale = function ( vertices , scaleX , scaleY , point ) {
if ( scaleX === 1 && scaleY === 1 )
return vertices ;
point = point || Vertices . centre ( vertices ) ;
var vertex ,
delta ;
for ( var i = 0 ; i < vertices . length ; i ++ ) {
vertex = vertices [ i ] ;
delta = Vector . sub ( vertex , point ) ;
vertices [ i ] . x = point . x + delta . x * scaleX ;
vertices [ i ] . y = point . y + delta . y * scaleY ;
}
return vertices ;
} ;
2014-05-05 14:32:51 -04:00
/ * *
* Chamfers a set of vertices by giving them rounded corners , returns a new set of vertices .
* The radius parameter is a single number or an array to specify the radius for each vertex .
* @ method chamfer
* @ param { vertices } vertices
* @ param { number [ ] } radius
* @ param { number } quality
* @ param { number } qualityMin
* @ param { number } qualityMax
* /
Vertices . chamfer = function ( vertices , radius , quality , qualityMin , qualityMax ) {
radius = radius || [ 8 ] ;
if ( ! radius . length )
radius = [ radius ] ;
// quality defaults to -1, which is auto
quality = ( typeof quality !== 'undefined' ) ? quality : - 1 ;
qualityMin = qualityMin || 2 ;
qualityMax = qualityMax || 14 ;
var centre = Vertices . centre ( vertices ) ,
newVertices = [ ] ;
for ( var i = 0 ; i < vertices . length ; i ++ ) {
var prevVertex = vertices [ i - 1 >= 0 ? i - 1 : vertices . length - 1 ] ,
vertex = vertices [ i ] ,
nextVertex = vertices [ ( i + 1 ) % vertices . length ] ,
currentRadius = radius [ i < radius . length ? i : radius . length - 1 ] ;
if ( currentRadius === 0 ) {
newVertices . push ( vertex ) ;
continue ;
}
var prevNormal = Vector . normalise ( {
x : vertex . y - prevVertex . y ,
y : prevVertex . x - vertex . x
} ) ;
var nextNormal = Vector . normalise ( {
x : nextVertex . y - vertex . y ,
y : vertex . x - nextVertex . x
} ) ;
var diagonalRadius = Math . sqrt ( 2 * Math . pow ( currentRadius , 2 ) ) ,
radiusVector = Vector . mult ( Common . clone ( prevNormal ) , currentRadius ) ,
midNormal = Vector . normalise ( Vector . mult ( Vector . add ( prevNormal , nextNormal ) , 0.5 ) ) ,
scaledVertex = Vector . sub ( vertex , Vector . mult ( midNormal , diagonalRadius ) ) ;
var precision = quality ;
if ( quality === - 1 ) {
// automatically decide precision
precision = Math . pow ( currentRadius , 0.32 ) * 1.75 ;
}
precision = Common . clamp ( precision , qualityMin , qualityMax ) ;
// use an even value for precision, more likely to reduce axes by using symmetry
if ( precision % 2 === 1 )
precision += 1 ;
var alpha = Math . acos ( Vector . dot ( prevNormal , nextNormal ) ) ,
theta = alpha / precision ;
for ( var j = 0 ; j < precision ; j ++ ) {
newVertices . push ( Vector . add ( Vector . rotate ( radiusVector , theta * j ) , scaledVertex ) ) ;
}
}
return newVertices ;
} ;
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/geometry/Vertices.js
2014-05-01 09:09:06 -04:00
// Begin src/render/Render.js
2014-02-19 09:15:05 -05:00
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* The ` Matter.Render ` module is the default ` render.controller ` used by a ` Matter.Engine ` .
* This renderer is HTML5 canvas based and supports a number of drawing options including sprites and viewports .
*
* It is possible develop a custom renderer module based on ` Matter.Render ` and pass an instance of it to ` Engine.create ` via ` options.render ` .
* A minimal custom renderer object must define at least three functions : ` create ` , ` clear ` and ` world ` ( see ` Matter.Render ` ) .
*
* See also ` Matter.RenderPixi ` for an alternate WebGL , scene - graph based renderer .
2014-02-28 20:10:08 -05:00
*
2014-05-01 09:09:06 -04:00
* @ class Render
2014-02-28 20:10:08 -05:00
* /
2014-05-01 09:09:06 -04:00
var Render = { } ;
2014-02-19 09:15:05 -05:00
2014-05-01 09:09:06 -04:00
( function ( ) {
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Creates a new renderer . The options parameter is an object that specifies any properties you wish to override the defaults .
* All properties have default values , and many are pre - calculated automatically based on other properties .
* See the properites section below for detailed information on what you can pass via the ` options ` object .
2014-02-28 20:10:08 -05:00
* @ method create
2014-06-09 14:40:24 -04:00
* @ param { object } [ options ]
2014-05-01 09:09:06 -04:00
* @ return { render } A new renderer
2014-02-28 20:10:08 -05:00
* /
2014-05-01 09:09:06 -04:00
Render . create = function ( options ) {
var defaults = {
controller : Render ,
element : null ,
canvas : null ,
options : {
width : 800 ,
height : 600 ,
background : '#fafafa' ,
wireframeBackground : '#222' ,
hasBounds : false ,
enabled : true ,
wireframes : true ,
showSleeping : true ,
showDebug : false ,
showBroadphase : false ,
showBounds : false ,
showVelocity : false ,
showCollisions : false ,
showAxes : false ,
showPositions : false ,
showAngleIndicator : false ,
showIds : false ,
showShadows : false
}
2014-02-19 09:15:05 -05:00
} ;
2014-05-01 09:09:06 -04:00
var render = Common . extend ( defaults , options ) ;
2014-02-19 09:15:05 -05:00
2014-05-01 09:09:06 -04:00
render . canvas = render . canvas || _createCanvas ( render . options . width , render . options . height ) ;
render . context = render . canvas . getContext ( '2d' ) ;
render . textures = { } ;
2014-02-19 09:15:05 -05:00
2014-05-01 09:09:06 -04:00
render . bounds = render . bounds || {
min : {
x : 0 ,
y : 0
} ,
max : {
x : render . options . width ,
y : render . options . height
}
} ;
2014-03-30 14:45:30 -04:00
2014-05-01 09:09:06 -04:00
Render . setBackground ( render , render . options . background ) ;
2014-03-22 13:51:49 -04:00
if ( Common . isElement ( render . element ) ) {
render . element . appendChild ( render . canvas ) ;
} else {
Common . log ( 'No "render.element" passed, "render.canvas" was not inserted into document.' , 'warn' ) ;
}
2014-02-19 09:15:05 -05:00
return render ;
} ;
2014-03-24 16:11:42 -04:00
/ * *
* Clears the renderer . In this implementation , this is a noop .
* @ method clear
* @ param { RenderPixi } render
* /
Render . clear = function ( render ) {
// nothing required to clear this renderer implentation
// if a scene graph is required, clear it here (see RenderPixi.js)
} ;
2014-03-22 13:51:49 -04:00
/ * *
* Sets the background CSS property of the canvas
* @ method setBackground
* @ param { render } render
* @ param { string } background
* /
Render . setBackground = function ( render , background ) {
if ( render . currentBackground !== background ) {
var cssBackground = background ;
if ( /(jpg|gif|png)$/ . test ( background ) )
cssBackground = 'url(' + background + ')' ;
render . canvas . style . background = cssBackground ;
render . canvas . style . backgroundSize = "contain" ;
render . currentBackground = background ;
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
2014-06-09 14:40:24 -04:00
* Renders the given ` engine ` ' s ` Matter.World ` object .
* This is the entry point for all rendering and should be called every time the scene changes .
2014-02-28 20:10:08 -05:00
* @ method world
* @ param { engine } engine
* /
2014-02-19 09:15:05 -05:00
Render . world = function ( engine ) {
var render = engine . render ,
world = engine . world ,
canvas = render . canvas ,
context = render . context ,
options = render . options ,
2014-05-01 09:09:06 -04:00
allBodies = Composite . allBodies ( world ) ,
allConstraints = Composite . allConstraints ( world ) ,
bodies = [ ] ,
constraints = [ ] ,
2014-02-19 09:15:05 -05:00
i ;
if ( options . wireframes ) {
2014-03-22 13:51:49 -04:00
Render . setBackground ( render , options . wireframeBackground ) ;
2014-02-19 09:15:05 -05:00
} else {
2014-03-22 13:51:49 -04:00
Render . setBackground ( render , options . background ) ;
2014-02-19 09:15:05 -05:00
}
2014-03-22 13:51:49 -04:00
// clear the canvas with a transparent fill, to allow the canvas background to show
context . globalCompositeOperation = 'source-in' ;
context . fillStyle = "transparent" ;
2014-02-19 09:15:05 -05:00
context . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
2014-03-22 13:51:49 -04:00
context . globalCompositeOperation = 'source-over' ;
2014-02-19 09:15:05 -05:00
2014-05-01 09:09:06 -04:00
// handle bounds
2014-05-05 14:32:51 -04:00
var boundsWidth = render . bounds . max . x - render . bounds . min . x ,
boundsHeight = render . bounds . max . y - render . bounds . min . y ,
boundsScaleX = boundsWidth / render . options . width ,
boundsScaleY = boundsHeight / render . options . height ;
2014-05-01 09:09:06 -04:00
if ( options . hasBounds ) {
// filter out bodies that are not in view
for ( i = 0 ; i < allBodies . length ; i ++ ) {
var body = allBodies [ i ] ;
if ( Bounds . overlaps ( body . bounds , render . bounds ) )
bodies . push ( body ) ;
}
// filter out constraints that are not in view
for ( i = 0 ; i < allConstraints . length ; i ++ ) {
var constraint = allConstraints [ i ] ,
bodyA = constraint . bodyA ,
bodyB = constraint . bodyB ,
pointAWorld = constraint . pointA ,
pointBWorld = constraint . pointB ;
if ( bodyA ) pointAWorld = Vector . add ( bodyA . position , constraint . pointA ) ;
if ( bodyB ) pointBWorld = Vector . add ( bodyB . position , constraint . pointB ) ;
if ( ! pointAWorld || ! pointBWorld )
continue ;
if ( Bounds . contains ( render . bounds , pointAWorld ) || Bounds . contains ( render . bounds , pointBWorld ) )
constraints . push ( constraint ) ;
}
2014-05-05 14:32:51 -04:00
// transform the view
context . scale ( 1 / boundsScaleX , 1 / boundsScaleY ) ;
2014-05-01 09:09:06 -04:00
context . translate ( - render . bounds . min . x , - render . bounds . min . y ) ;
} else {
constraints = allConstraints ;
bodies = allBodies ;
}
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( ! options . wireframes || ( engine . enableSleeping && options . showSleeping ) ) {
// fully featured rendering of bodies
2014-03-24 16:11:42 -04:00
Render . bodies ( engine , bodies , context ) ;
2014-03-22 13:51:49 -04:00
} else {
// optimised method for wireframes only
2014-03-24 16:11:42 -04:00
Render . bodyWireframes ( engine , bodies , context ) ;
2014-03-22 13:51:49 -04:00
}
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( options . showBounds )
2014-03-24 16:11:42 -04:00
Render . bodyBounds ( engine , bodies , context ) ;
2014-03-22 13:51:49 -04:00
if ( options . showAxes || options . showAngleIndicator )
2014-03-24 16:11:42 -04:00
Render . bodyAxes ( engine , bodies , context ) ;
2014-03-22 13:51:49 -04:00
if ( options . showPositions )
2014-03-24 16:11:42 -04:00
Render . bodyPositions ( engine , bodies , context ) ;
2014-03-22 13:51:49 -04:00
if ( options . showVelocity )
2014-03-24 16:11:42 -04:00
Render . bodyVelocity ( engine , bodies , context ) ;
2014-03-22 13:51:49 -04:00
if ( options . showIds )
2014-03-24 16:11:42 -04:00
Render . bodyIds ( engine , bodies , context ) ;
2014-02-19 09:15:05 -05:00
if ( options . showCollisions )
2014-03-22 13:51:49 -04:00
Render . collisions ( engine , engine . pairs . list , context ) ;
2014-03-24 16:11:42 -04:00
Render . constraints ( constraints , context ) ;
2014-02-19 09:15:05 -05:00
if ( options . showBroadphase && engine . broadphase . current === 'grid' )
Render . grid ( engine , engine . broadphase [ engine . broadphase . current ] . instance , context ) ;
if ( options . showDebug )
Render . debug ( engine , context ) ;
2014-05-01 09:09:06 -04:00
2014-05-05 14:32:51 -04:00
if ( options . hasBounds ) {
// revert view transforms
context . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ;
}
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-02-28 20:10:08 -05:00
* @ method debug
* @ param { engine } engine
* @ param { RenderingContext } context
* /
2014-02-19 09:15:05 -05:00
Render . debug = function ( engine , context ) {
var c = context ,
world = engine . world ,
render = engine . render ,
options = render . options ,
2014-03-24 16:11:42 -04:00
bodies = Composite . allBodies ( world ) ,
2014-02-19 09:15:05 -05:00
space = " " ;
if ( engine . timing . timestamp - ( render . debugTimestamp || 0 ) >= 500 ) {
var text = "" ;
text += "fps: " + Math . round ( engine . timing . fps ) + space ;
2014-03-22 13:51:49 -04:00
if ( engine . metrics . extended ) {
text += "delta: " + engine . timing . delta . toFixed ( 3 ) + space ;
text += "correction: " + engine . timing . correction . toFixed ( 3 ) + space ;
2014-03-24 16:11:42 -04:00
text += "bodies: " + bodies . length + space ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( engine . broadphase . controller === Grid )
text += "buckets: " + engine . metrics . buckets + space ;
text += "\n" ;
text += "collisions: " + engine . metrics . collisions + space ;
text += "pairs: " + engine . pairs . list . length + space ;
text += "broad: " + engine . metrics . broadEff + space ;
text += "mid: " + engine . metrics . midEff + space ;
text += "narrow: " + engine . metrics . narrowEff + space ;
}
2014-02-19 09:15:05 -05:00
render . debugString = text ;
render . debugTimestamp = engine . timing . timestamp ;
}
if ( render . debugString ) {
c . font = "12px Arial" ;
if ( options . wireframes ) {
c . fillStyle = 'rgba(255,255,255,0.5)' ;
} else {
c . fillStyle = 'rgba(0,0,0,0.5)' ;
}
var split = render . debugString . split ( '\n' ) ;
for ( var i = 0 ; i < split . length ; i ++ ) {
c . fillText ( split [ i ] , 50 , 50 + i * 18 ) ;
}
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method constraints
* @ param { constraint [ ] } constraints
2014-02-28 20:10:08 -05:00
* @ param { RenderingContext } context
* /
2014-03-22 13:51:49 -04:00
Render . constraints = function ( constraints , context ) {
var c = context ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
for ( var i = 0 ; i < constraints . length ; i ++ ) {
var constraint = constraints [ i ] ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( ! constraint . render . visible || ! constraint . pointA || ! constraint . pointB )
continue ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
var bodyA = constraint . bodyA ,
bodyB = constraint . bodyB ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
if ( bodyA ) {
c . beginPath ( ) ;
c . moveTo ( bodyA . position . x + constraint . pointA . x , bodyA . position . y + constraint . pointA . y ) ;
} else {
c . beginPath ( ) ;
c . moveTo ( constraint . pointA . x , constraint . pointA . y ) ;
}
if ( bodyB ) {
c . lineTo ( bodyB . position . x + constraint . pointB . x , bodyB . position . y + constraint . pointB . y ) ;
} else {
c . lineTo ( constraint . pointB . x , constraint . pointB . y ) ;
}
c . lineWidth = constraint . render . lineWidth ;
c . strokeStyle = constraint . render . strokeStyle ;
c . stroke ( ) ;
}
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodyShadows
2014-02-28 20:10:08 -05:00
* @ param { engine } engine
2014-03-22 13:51:49 -04:00
* @ param { body [ ] } bodies
2014-02-28 20:10:08 -05:00
* @ param { RenderingContext } context
* /
2014-03-22 13:51:49 -04:00
Render . bodyShadows = function ( engine , bodies , context ) {
2014-02-19 09:15:05 -05:00
var c = context ,
2014-03-22 13:51:49 -04:00
render = engine . render ,
options = render . options ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( ! body . render . visible )
continue ;
if ( body . circleRadius ) {
c . beginPath ( ) ;
c . arc ( body . position . x , body . position . y , body . circleRadius , 0 , 2 * Math . PI ) ;
c . closePath ( ) ;
} else {
c . beginPath ( ) ;
c . moveTo ( body . vertices [ 0 ] . x , body . vertices [ 0 ] . y ) ;
for ( var j = 1 ; j < body . vertices . length ; j ++ ) {
c . lineTo ( body . vertices [ j ] . x , body . vertices [ j ] . y ) ;
}
c . closePath ( ) ;
}
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
var distanceX = body . position . x - render . options . width * 0.5 ,
distanceY = body . position . y - render . options . height * 0.2 ,
distance = Math . abs ( distanceX ) + Math . abs ( distanceY ) ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
c . shadowColor = 'rgba(0,0,0,0.15)' ;
c . shadowOffsetX = 0.05 * distanceX ;
c . shadowOffsetY = 0.05 * distanceY ;
c . shadowBlur = 1 + 12 * Math . min ( 1 , distance / 1000 ) ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
c . fill ( ) ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
c . shadowColor = null ;
c . shadowOffsetX = null ;
c . shadowOffsetY = null ;
c . shadowBlur = null ;
}
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodies
2014-02-28 20:10:08 -05:00
* @ param { engine } engine
2014-03-22 13:51:49 -04:00
* @ param { body [ ] } bodies
2014-02-28 20:10:08 -05:00
* @ param { RenderingContext } context
* /
2014-03-22 13:51:49 -04:00
Render . bodies = function ( engine , bodies , context ) {
2014-02-19 09:15:05 -05:00
var c = context ,
render = engine . render ,
2014-03-22 13:51:49 -04:00
options = render . options ,
i ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
for ( i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( ! body . render . visible )
continue ;
2014-03-30 14:45:30 -04:00
if ( body . render . sprite && body . render . sprite . texture && ! options . wireframes ) {
2014-03-22 13:51:49 -04:00
// body sprite
var sprite = body . render . sprite ,
texture = _getTexture ( render , sprite . texture ) ;
if ( options . showSleeping && body . isSleeping )
c . globalAlpha = 0.5 ;
c . translate ( body . position . x , body . position . y ) ;
c . rotate ( body . angle ) ;
c . drawImage ( texture , texture . width * - 0.5 * sprite . xScale , texture . height * - 0.5 * sprite . yScale ,
texture . width * sprite . xScale , texture . height * sprite . yScale ) ;
// revert translation, hopefully faster than save / restore
c . rotate ( - body . angle ) ;
c . translate ( - body . position . x , - body . position . y ) ;
if ( options . showSleeping && body . isSleeping )
c . globalAlpha = 1 ;
2014-02-19 09:15:05 -05:00
} else {
2014-03-22 13:51:49 -04:00
// body polygon
if ( body . circleRadius ) {
c . beginPath ( ) ;
c . arc ( body . position . x , body . position . y , body . circleRadius , 0 , 2 * Math . PI ) ;
} else {
c . beginPath ( ) ;
c . moveTo ( body . vertices [ 0 ] . x , body . vertices [ 0 ] . y ) ;
for ( var j = 1 ; j < body . vertices . length ; j ++ ) {
c . lineTo ( body . vertices [ j ] . x , body . vertices [ j ] . y ) ;
}
c . closePath ( ) ;
}
if ( ! options . wireframes ) {
if ( options . showSleeping && body . isSleeping ) {
c . fillStyle = Common . shadeColor ( body . render . fillStyle , 50 ) ;
} else {
c . fillStyle = body . render . fillStyle ;
}
c . lineWidth = body . render . lineWidth ;
c . strokeStyle = body . render . strokeStyle ;
c . fill ( ) ;
c . stroke ( ) ;
} else {
c . lineWidth = 1 ;
c . strokeStyle = '#bbb' ;
if ( options . showSleeping && body . isSleeping )
c . strokeStyle = 'rgba(255,255,255,0.2)' ;
c . stroke ( ) ;
}
2014-02-19 09:15:05 -05:00
}
}
2014-03-22 13:51:49 -04:00
} ;
/ * *
* Optimised method for drawing body wireframes in one pass
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodyWireframes
* @ param { engine } engine
* @ param { body [ ] } bodies
* @ param { RenderingContext } context
* /
Render . bodyWireframes = function ( engine , bodies , context ) {
var c = context ,
i ,
j ;
c . beginPath ( ) ;
for ( i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( ! body . render . visible )
continue ;
2014-02-19 09:15:05 -05:00
c . moveTo ( body . vertices [ 0 ] . x , body . vertices [ 0 ] . y ) ;
2014-03-22 13:51:49 -04:00
for ( j = 1 ; j < body . vertices . length ; j ++ ) {
2014-02-19 09:15:05 -05:00
c . lineTo ( body . vertices [ j ] . x , body . vertices [ j ] . y ) ;
}
2014-03-22 13:51:49 -04:00
c . lineTo ( body . vertices [ 0 ] . x , body . vertices [ 0 ] . y ) ;
2014-02-19 09:15:05 -05:00
}
2014-03-22 13:51:49 -04:00
c . lineWidth = 1 ;
c . strokeStyle = '#bbb' ;
c . stroke ( ) ;
} ;
/ * *
* Draws body bounds
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodyBounds
* @ param { engine } engine
* @ param { body [ ] } bodies
* @ param { RenderingContext } context
* /
Render . bodyBounds = function ( engine , bodies , context ) {
var c = context ,
render = engine . render ,
options = render . options ;
c . beginPath ( ) ;
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( body . render . visible )
c . rect ( body . bounds . min . x , body . bounds . min . y , body . bounds . max . x - body . bounds . min . x , body . bounds . max . y - body . bounds . min . y ) ;
}
if ( options . wireframes ) {
c . strokeStyle = 'rgba(255,255,255,0.08)' ;
2014-02-19 09:15:05 -05:00
} else {
2014-03-22 13:51:49 -04:00
c . strokeStyle = 'rgba(0,0,0,0.1)' ;
2014-02-19 09:15:05 -05:00
}
2014-03-22 13:51:49 -04:00
c . lineWidth = 1 ;
c . stroke ( ) ;
} ;
/ * *
* Draws body angle indicators and axes
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodyAxes
* @ param { engine } engine
* @ param { body [ ] } bodies
* @ param { RenderingContext } context
* /
Render . bodyAxes = function ( engine , bodies , context ) {
var c = context ,
render = engine . render ,
options = render . options ,
i ,
j ;
c . beginPath ( ) ;
for ( i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( ! body . render . visible )
continue ;
if ( options . showAxes ) {
// render all axes
for ( j = 0 ; j < body . axes . length ; j ++ ) {
var axis = body . axes [ j ] ;
c . moveTo ( body . position . x , body . position . y ) ;
c . lineTo ( body . position . x + axis . x * 20 , body . position . y + axis . y * 20 ) ;
}
2014-02-19 09:15:05 -05:00
} else {
2014-03-22 13:51:49 -04:00
// render a single axis indicator
c . moveTo ( body . position . x , body . position . y ) ;
c . lineTo ( ( body . vertices [ 0 ] . x + body . vertices [ body . vertices . length - 1 ] . x ) / 2 ,
( body . vertices [ 0 ] . y + body . vertices [ body . vertices . length - 1 ] . y ) / 2 ) ;
2014-02-19 09:15:05 -05:00
}
}
2014-03-22 13:51:49 -04:00
if ( options . wireframes ) {
c . strokeStyle = 'indianred' ;
} else {
c . strokeStyle = 'rgba(0,0,0,0.3)' ;
}
c . lineWidth = 1 ;
c . stroke ( ) ;
} ;
/ * *
* Draws body positions
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodyPositions
* @ param { engine } engine
* @ param { body [ ] } bodies
* @ param { RenderingContext } context
* /
Render . bodyPositions = function ( engine , bodies , context ) {
var c = context ,
render = engine . render ,
options = render . options ,
body ,
i ;
c . beginPath ( ) ;
// render current positions
for ( i = 0 ; i < bodies . length ; i ++ ) {
body = bodies [ i ] ;
if ( body . render . visible ) {
c . arc ( body . position . x , body . position . y , 3 , 0 , 2 * Math . PI , false ) ;
c . closePath ( ) ;
2014-02-19 09:15:05 -05:00
}
}
2014-03-22 13:51:49 -04:00
if ( options . wireframes ) {
c . fillStyle = 'indianred' ;
} else {
c . fillStyle = 'rgba(0,0,0,0.5)' ;
}
c . fill ( ) ;
c . beginPath ( ) ;
// render previous positions
for ( i = 0 ; i < bodies . length ; i ++ ) {
body = bodies [ i ] ;
if ( body . render . visible ) {
c . arc ( body . positionPrev . x , body . positionPrev . y , 2 , 0 , 2 * Math . PI , false ) ;
c . closePath ( ) ;
2014-02-19 09:15:05 -05:00
}
}
2014-03-22 13:51:49 -04:00
c . fillStyle = 'rgba(255,165,0,0.8)' ;
c . fill ( ) ;
} ;
/ * *
* Draws body velocity
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodyVelocity
* @ param { engine } engine
* @ param { body [ ] } bodies
* @ param { RenderingContext } context
* /
Render . bodyVelocity = function ( engine , bodies , context ) {
var c = context ,
render = engine . render ,
options = render . options ;
c . beginPath ( ) ;
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( ! body . render . visible )
continue ;
2014-02-19 09:15:05 -05:00
c . moveTo ( body . position . x , body . position . y ) ;
c . lineTo ( body . position . x + ( body . position . x - body . positionPrev . x ) * 2 , body . position . y + ( body . position . y - body . positionPrev . y ) * 2 ) ;
}
2014-03-22 13:51:49 -04:00
c . lineWidth = 3 ;
c . strokeStyle = 'cornflowerblue' ;
c . stroke ( ) ;
} ;
/ * *
* Draws body ids
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method bodyIds
* @ param { engine } engine
* @ param { body [ ] } bodies
* @ param { RenderingContext } context
* /
Render . bodyIds = function ( engine , bodies , context ) {
var c = context ;
for ( var i = 0 ; i < bodies . length ; i ++ ) {
var body = bodies [ i ] ;
if ( ! body . render . visible )
continue ;
2014-02-19 09:15:05 -05:00
c . font = "12px Arial" ;
c . fillStyle = 'rgba(255,255,255,0.5)' ;
c . fillText ( body . id , body . position . x + 10 , body . position . y - 10 ) ;
}
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-03-22 13:51:49 -04:00
* @ method collisions
2014-02-28 20:10:08 -05:00
* @ param { engine } engine
2014-03-22 13:51:49 -04:00
* @ param { pair [ ] } pairs
2014-02-28 20:10:08 -05:00
* @ param { RenderingContext } context
* /
2014-03-22 13:51:49 -04:00
Render . collisions = function ( engine , pairs , context ) {
2014-02-19 09:15:05 -05:00
var c = context ,
2014-03-22 13:51:49 -04:00
options = engine . render . options ,
pair ,
collision ,
i ,
j ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
c . beginPath ( ) ;
// render collision positions
for ( i = 0 ; i < pairs . length ; i ++ ) {
pair = pairs [ i ] ;
collision = pair . collision ;
for ( j = 0 ; j < pair . activeContacts . length ; j ++ ) {
var contact = pair . activeContacts [ j ] ,
vertex = contact . vertex ;
c . rect ( vertex . x - 1.5 , vertex . y - 1.5 , 3.5 , 3.5 ) ;
2014-02-19 09:15:05 -05:00
}
}
2014-03-22 13:51:49 -04:00
if ( options . wireframes ) {
c . fillStyle = 'rgba(255,255,255,0.7)' ;
} else {
c . fillStyle = 'orange' ;
}
c . fill ( ) ;
c . beginPath ( ) ;
2014-02-19 09:15:05 -05:00
2014-03-22 13:51:49 -04:00
// render collision normals
for ( i = 0 ; i < pairs . length ; i ++ ) {
pair = pairs [ i ] ;
collision = pair . collision ;
if ( pair . activeContacts . length > 0 ) {
var normalPosX = pair . activeContacts [ 0 ] . vertex . x ,
normalPosY = pair . activeContacts [ 0 ] . vertex . y ;
if ( pair . activeContacts . length === 2 ) {
normalPosX = ( pair . activeContacts [ 0 ] . vertex . x + pair . activeContacts [ 1 ] . vertex . x ) / 2 ;
normalPosY = ( pair . activeContacts [ 0 ] . vertex . y + pair . activeContacts [ 1 ] . vertex . y ) / 2 ;
}
c . moveTo ( normalPosX - collision . normal . x * 8 , normalPosY - collision . normal . y * 8 ) ;
c . lineTo ( normalPosX , normalPosY ) ;
2014-02-19 09:15:05 -05:00
}
}
2014-03-22 13:51:49 -04:00
if ( options . wireframes ) {
c . strokeStyle = 'rgba(255,165,0,0.7)' ;
} else {
c . strokeStyle = 'orange' ;
}
c . lineWidth = 1 ;
c . stroke ( ) ;
2014-02-19 09:15:05 -05:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-02-28 20:10:08 -05:00
* @ method grid
* @ param { engine } engine
* @ param { grid } grid
* @ param { RenderingContext } context
* /
2014-02-19 09:15:05 -05:00
Render . grid = function ( engine , grid , context ) {
var c = context ,
options = engine . render . options ;
if ( options . wireframes ) {
c . strokeStyle = 'rgba(255,180,0,0.1)' ;
} else {
c . strokeStyle = 'rgba(255,180,0,0.5)' ;
}
2014-03-22 13:51:49 -04:00
c . beginPath ( ) ;
2014-02-19 09:15:05 -05:00
var bucketKeys = Common . keys ( grid . buckets ) ;
for ( var i = 0 ; i < bucketKeys . length ; i ++ ) {
var bucketId = bucketKeys [ i ] ;
if ( grid . buckets [ bucketId ] . length < 2 )
continue ;
var region = bucketId . split ( ',' ) ;
c . rect ( 0.5 + parseInt ( region [ 0 ] , 10 ) * grid . bucketWidth ,
0.5 + parseInt ( region [ 1 ] , 10 ) * grid . bucketHeight ,
grid . bucketWidth ,
grid . bucketHeight ) ;
}
2014-03-22 13:51:49 -04:00
c . lineWidth = 1 ;
c . stroke ( ) ;
2014-02-19 09:15:05 -05:00
} ;
2014-05-01 09:09:06 -04:00
/ * *
* Description
2014-06-09 14:40:24 -04:00
* @ private
2014-05-01 09:09:06 -04:00
* @ method inspector
* @ param { inspector } inspector
* @ param { RenderingContext } context
* /
Render . inspector = function ( inspector , context ) {
var engine = inspector . engine ,
mouse = engine . input . mouse ,
selected = inspector . selected ,
c = context ,
render = engine . render ,
options = render . options ,
bounds ;
2014-05-05 14:32:51 -04:00
if ( options . hasBounds ) {
var boundsWidth = render . bounds . max . x - render . bounds . min . x ,
boundsHeight = render . bounds . max . y - render . bounds . min . y ,
boundsScaleX = boundsWidth / render . options . width ,
boundsScaleY = boundsHeight / render . options . height ;
context . scale ( 1 / boundsScaleX , 1 / boundsScaleY ) ;
2014-05-01 09:09:06 -04:00
context . translate ( - render . bounds . min . x , - render . bounds . min . y ) ;
2014-05-05 14:32:51 -04:00
}
2014-05-01 09:09:06 -04:00
for ( var i = 0 ; i < selected . length ; i ++ ) {
var item = selected [ i ] . data ;
context . translate ( 0.5 , 0.5 ) ;
context . lineWidth = 1 ;
context . strokeStyle = 'rgba(255,165,0,0.9)' ;
context . setLineDash ( [ 1 , 2 ] ) ;
switch ( item . type ) {
case 'body' :
// render body selections
bounds = item . bounds ;
context . beginPath ( ) ;
context . rect ( Math . floor ( bounds . min . x - 3 ) , Math . floor ( bounds . min . y - 3 ) ,
Math . floor ( bounds . max . x - bounds . min . x + 6 ) , Math . floor ( bounds . max . y - bounds . min . y + 6 ) ) ;
context . closePath ( ) ;
context . stroke ( ) ;
break ;
case 'constraint' :
// render constraint selections
var point = item . pointA ;
if ( item . bodyA )
point = item . pointB ;
context . beginPath ( ) ;
context . arc ( point . x , point . y , 10 , 0 , 2 * Math . PI ) ;
context . closePath ( ) ;
context . stroke ( ) ;
break ;
}
context . setLineDash ( [ 0 ] ) ;
context . translate ( - 0.5 , - 0.5 ) ;
}
// render selection region
if ( inspector . selectStart !== null ) {
context . translate ( 0.5 , 0.5 ) ;
context . lineWidth = 1 ;
context . strokeStyle = 'rgba(255,165,0,0.6)' ;
context . fillStyle = 'rgba(255,165,0,0.1)' ;
bounds = inspector . selectBounds ;
context . beginPath ( ) ;
context . rect ( Math . floor ( bounds . min . x ) , Math . floor ( bounds . min . y ) ,
Math . floor ( bounds . max . x - bounds . min . x ) , Math . floor ( bounds . max . y - bounds . min . y ) ) ;
context . closePath ( ) ;
context . stroke ( ) ;
context . fill ( ) ;
context . translate ( - 0.5 , - 0.5 ) ;
}
if ( options . hasBounds )
2014-05-05 14:32:51 -04:00
context . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ;
2014-05-01 09:09:06 -04:00
} ;
2014-02-28 20:10:08 -05:00
/ * *
* Description
* @ method _createCanvas
* @ private
* @ param { } width
* @ param { } height
* @ return canvas
* /
2014-02-19 09:15:05 -05:00
var _createCanvas = function ( width , height ) {
var canvas = document . createElement ( 'canvas' ) ;
canvas . width = width ;
canvas . height = height ;
canvas . oncontextmenu = function ( ) { return false ; } ;
canvas . onselectstart = function ( ) { return false ; } ;
return canvas ;
} ;
2014-03-22 13:51:49 -04:00
/ * *
* Gets the requested texture ( an Image ) via its path
* @ method _getTexture
* @ private
* @ param { render } render
* @ param { string } imagePath
* @ return { Image } texture
* /
var _getTexture = function ( render , imagePath ) {
var image = render . textures [ imagePath ] ;
if ( image )
return image ;
image = render . textures [ imagePath ] = new Image ( ) ;
image . src = imagePath ;
return image ;
} ;
2014-06-09 14:40:24 -04:00
/ *
*
* Properties Documentation
*
* /
/ * *
* A back - reference to the ` Matter.Render ` module .
*
* @ property controller
* @ type render
* /
/ * *
* A reference to the element where the canvas is to be inserted ( if ` render.canvas ` has not been specified )
*
* @ property element
* @ type HTMLElement
* @ default null
* /
/ * *
* The canvas element to render to . If not specified , one will be created if ` render.element ` has been specified .
*
* @ property canvas
* @ type HTMLCanvasElement
* @ default null
* /
/ * *
* The configuration options of the renderer .
*
* @ property options
* @ type { }
* /
/ * *
* The target width in pixels of the ` render.canvas ` to be created .
*
* @ property options . width
* @ type number
* @ default 800
* /
/ * *
* The target height in pixels of the ` render.canvas ` to be created .
*
* @ property options . height
* @ type number
* @ default 600
* /
/ * *
* A flag that specifies if ` render.bounds ` should be used when rendering .
*
* @ property options . hasBounds
* @ type boolean
* @ default false
* /
/ * *
* A ` Bounds ` object that specifies the drawing view region .
* Rendering will be automatically transformed and scaled to fit within the canvas size ( ` render.options.width ` and ` render.options.height ` ) .
* This allows for creating views that can pan or zoom around the scene .
* You must also set ` render.options.hasBounds ` to ` true ` to enable bounded rendering .
*
* @ property bounds
* @ type bounds
* /
/ * *
* The 2 d rendering context from the ` render.canvas ` element .
*
* @ property context
* @ type CanvasRenderingContext2D
* /
/ * *
* The sprite texture cache .
*
* @ property textures
* @ type { }
* /
2014-02-19 09:15:05 -05:00
} ) ( ) ;
; // End src/render/Render.js
2014-03-22 13:51:49 -04:00
// Begin src/render/RenderPixi.js
/ * *
* See [ Demo . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [ DemoMobile . js ] ( https : //github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @ class RenderPixi
* /
var RenderPixi = { } ;
( function ( ) {
/ * *
* Creates a new Pixi . js WebGL renderer
* @ method create
* @ param { object } options
* @ return { RenderPixi } A new renderer
* /
RenderPixi . create = function ( options ) {
var defaults = {
controller : RenderPixi ,
element : null ,
canvas : null ,
options : {
width : 800 ,
height : 600 ,
background : '#fafafa' ,
wireframeBackground : '#222' ,
enabled : true ,
wireframes : true ,
showSleeping : true ,
showDebug : false ,
showBroadphase : false ,
showBounds : false ,
showVelocity : false ,
showCollisions : false ,
showAxes : false ,
showPositions : false ,
showAngleIndicator : false ,
showIds : false ,
showShadows : false
}
} ;
var render = Common . extend ( defaults , options ) ;
// init pixi
render . context = new PIXI . WebGLRenderer ( 800 , 600 , render . canvas , false , true ) ;
render . canvas = render . context . view ;
render . stage = new PIXI . Stage ( ) ;
// caches
render . textures = { } ;
render . sprites = { } ;
render . primitives = { } ;
// use a sprite batch for performance
render . spriteBatch = new PIXI . SpriteBatch ( ) ;
render . stage . addChild ( render . spriteBatch ) ;
// insert canvas
if ( Common . isElement ( render . element ) ) {
render . element . appendChild ( render . canvas ) ;
} else {
Common . log ( 'No "render.element" passed, "render.canvas" was not inserted into document.' , 'warn' ) ;
}
2014-03-24 16:11:42 -04:00
// prevent menus on canvas
render . canvas . oncontextmenu = function ( ) { return false ; } ;
render . canvas . onselectstart = function ( ) { return false ; } ;
2014-03-22 13:51:49 -04:00
return render ;
} ;
/ * *
* Clears the scene graph
* @ method clear
* @ param { RenderPixi } render
* /
RenderPixi . clear = function ( render ) {
var stage = render . stage ,
spriteBatch = render . spriteBatch ;
// clear stage
while ( stage . children [ 0 ] ) {
stage . removeChild ( stage . children [ 0 ] ) ;
}
// clear sprite batch
while ( spriteBatch . children [ 0 ] ) {
spriteBatch . removeChild ( spriteBatch . children [ 0 ] ) ;
}
var bgSprite = render . sprites [ 'bg-0' ] ;
// clear caches
render . textures = { } ;
render . sprites = { } ;
render . primitives = { } ;
// set background sprite
render . sprites [ 'bg-0' ] = bgSprite ;
if ( bgSprite )
spriteBatch . addChildAt ( bgSprite , 0 ) ;
// add sprite batch back into stage
render . stage . addChild ( render . spriteBatch ) ;
// reset background state
render . currentBackground = null ;
} ;
/ * *
* Sets the background of the canvas
* @ method setBackground
* @ param { RenderPixi } render
* @ param { string } background
* /
RenderPixi . setBackground = function ( render , background ) {
if ( render . currentBackground !== background ) {
var isColor = background . indexOf && background . indexOf ( '#' ) !== - 1 ,
bgSprite = render . sprites [ 'bg-0' ] ;
if ( isColor ) {
// if solid background color
var color = Common . colorToNumber ( background ) ;
render . stage . setBackgroundColor ( color ) ;
// remove background sprite if existing
if ( bgSprite )
render . spriteBatch . removeChild ( bgSprite ) ;
} else {
// initialise background sprite if needed
if ( ! bgSprite ) {
var texture = _getTexture ( render , background ) ;
bgSprite = render . sprites [ 'bg-0' ] = new PIXI . Sprite ( texture ) ;
bgSprite . position . x = 0 ;
bgSprite . position . y = 0 ;
render . spriteBatch . addChildAt ( bgSprite , 0 ) ;
}
}
render . currentBackground = background ;
}
} ;
/ * *
* Description
* @ method world
* @ param { engine } engine
* /
RenderPixi . world = function ( engine ) {
var render = engine . render ,
world = engine . world ,
context = render . context ,
stage = render . stage ,
options = render . options ,
2014-03-24 16:11:42 -04:00
bodies = Composite . allBodies ( world ) ,
constraints = Composite . allConstraints ( world ) ,
2014-03-22 13:51:49 -04:00
i ;
if ( options . wireframes ) {
RenderPixi . setBackground ( render , options . wireframeBackground ) ;
} else {
RenderPixi . setBackground ( render , options . background ) ;
}
2014-03-24 16:11:42 -04:00
for ( i = 0 ; i < bodies . length ; i ++ )
RenderPixi . body ( engine , bodies [ i ] ) ;
2014-03-22 13:51:49 -04:00
2014-03-24 16:11:42 -04:00
for ( i = 0 ; i < constraints . length ; i ++ )
RenderPixi . constraint ( engine , constraints [ i ] ) ;
2014-03-22 13:51:49 -04:00
context . render ( stage ) ;
} ;
/ * *
* Description
* @ method constraint
* @ param { engine } engine
* @ param { constraint } constraint
* /
RenderPixi . constraint = function ( engine , constraint ) {
var render = engine . render ,
bodyA = constraint . bodyA ,
bodyB = constraint . bodyB ,
pointA = constraint . pointA ,
pointB = constraint . pointB ,
stage = render . stage ,
constraintRender = constraint . render ,
primitiveId = 'c-' + constraint . id ,
primitive = render . primitives [ primitiveId ] ;
// initialise constraint primitive if not existing
if ( ! primitive )
primitive = render . primitives [ primitiveId ] = new PIXI . Graphics ( ) ;
// don't render if constraint does not have two end points
if ( ! constraintRender . visible || ! constraint . pointA || ! constraint . pointB ) {
primitive . clear ( ) ;
return ;
}
// add to scene graph if not already there
if ( stage . children . indexOf ( primitive ) === - 1 )
stage . addChild ( primitive ) ;
// render the constraint on every update, since they can change dynamically
primitive . clear ( ) ;
primitive . beginFill ( 0 , 0 ) ;
primitive . lineStyle ( constraintRender . lineWidth , Common . colorToNumber ( constraintRender . strokeStyle ) , 1 ) ;
if ( bodyA ) {
primitive . moveTo ( bodyA . position . x + pointA . x , bodyA . position . y + pointA . y ) ;
} else {
primitive . moveTo ( pointA . x , pointA . y ) ;
}
if ( bodyB ) {
primitive . lineTo ( bodyB . position . x + pointB . x , bodyB . position . y + pointB . y ) ;
} else {
primitive . lineTo ( pointB . x , pointB . y ) ;
}
primitive . endFill ( ) ;
} ;
/ * *
* Description
* @ method body
* @ param { engine } engine
* @ param { body } body
* /
RenderPixi . body = function ( engine , body ) {
var render = engine . render ,
bodyRender = body . render ;
if ( ! bodyRender . visible )
return ;
2014-03-30 14:45:30 -04:00
if ( bodyRender . sprite && bodyRender . sprite . texture ) {
2014-03-22 13:51:49 -04:00
var spriteId = 'b-' + body . id ,
sprite = render . sprites [ spriteId ] ,
spriteBatch = render . spriteBatch ;
// initialise body sprite if not existing
if ( ! sprite )
sprite = render . sprites [ spriteId ] = _createBodySprite ( render , body ) ;
// add to scene graph if not already there
if ( spriteBatch . children . indexOf ( sprite ) === - 1 )
spriteBatch . addChild ( sprite ) ;
// update body sprite
sprite . position . x = body . position . x ;
sprite . position . y = body . position . y ;
sprite . rotation = body . angle ;
} else {
var primitiveId = 'b-' + body . id ,
primitive = render . primitives [ primitiveId ] ,
stage = render . stage ;
// initialise body primitive if not existing
if ( ! primitive ) {
primitive = render . primitives [ primitiveId ] = _createBodyPrimitive ( render , body ) ;
primitive . initialAngle = body . angle ;
}
// add to scene graph if not already there
if ( stage . children . indexOf ( primitive ) === - 1 )
stage . addChild ( primitive ) ;
// update body primitive
primitive . position . x = body . position . x ;
primitive . position . y = body . position . y ;
primitive . rotation = body . angle - primitive . initialAngle ;
}
} ;
/ * *
* Creates a body sprite
* @ method _createBodySprite
* @ private
* @ param { RenderPixi } render
* @ param { body } body
* @ return { PIXI . Sprite } sprite
* /
var _createBodySprite = function ( render , body ) {
var bodyRender = body . render ,
texturePath = bodyRender . sprite . texture ,
texture = _getTexture ( render , texturePath ) ,
sprite = new PIXI . Sprite ( texture ) ;
sprite . anchor . x = 0.5 ;
sprite . anchor . y = 0.5 ;
return sprite ;
} ;
/ * *
* Creates a body primitive
* @ method _createBodyPrimitive
* @ private
* @ param { RenderPixi } render
* @ param { body } body
* @ return { PIXI . Graphics } graphics
* /
var _createBodyPrimitive = function ( render , body ) {
var bodyRender = body . render ,
options = render . options ,
primitive = new PIXI . Graphics ( ) ;
primitive . clear ( ) ;
if ( ! options . wireframes ) {
primitive . beginFill ( Common . colorToNumber ( bodyRender . fillStyle ) , 1 ) ;
primitive . lineStyle ( body . render . lineWidth , Common . colorToNumber ( bodyRender . strokeStyle ) , 1 ) ;
} else {
primitive . beginFill ( 0 , 0 ) ;
primitive . lineStyle ( 1 , Common . colorToNumber ( '#bbb' ) , 1 ) ;
}
primitive . moveTo ( body . vertices [ 0 ] . x - body . position . x , body . vertices [ 0 ] . y - body . position . y ) ;
for ( var j = 1 ; j < body . vertices . length ; j ++ ) {
primitive . lineTo ( body . vertices [ j ] . x - body . position . x , body . vertices [ j ] . y - body . position . y ) ;
}
primitive . lineTo ( body . vertices [ 0 ] . x - body . position . x , body . vertices [ 0 ] . y - body . position . y ) ;
primitive . endFill ( ) ;
// angle indicator
if ( options . showAngleIndicator || options . showAxes ) {
primitive . beginFill ( 0 , 0 ) ;
if ( options . wireframes ) {
primitive . lineStyle ( 1 , Common . colorToNumber ( '#CD5C5C' ) , 1 ) ;
} else {
primitive . lineStyle ( 1 , Common . colorToNumber ( body . render . strokeStyle ) ) ;
}
primitive . moveTo ( 0 , 0 ) ;
primitive . lineTo ( ( ( body . vertices [ 0 ] . x + body . vertices [ body . vertices . length - 1 ] . x ) / 2 ) - body . position . x ,
( ( body . vertices [ 0 ] . y + body . vertices [ body . vertices . length - 1 ] . y ) / 2 ) - body . position . y ) ;
primitive . endFill ( ) ;
}
return primitive ;
} ;
/ * *
* Gets the requested texture ( a PIXI . Texture ) via its path
* @ method _getTexture
* @ private
* @ param { RenderPixi } render
* @ param { string } imagePath
* @ return { PIXI . Texture } texture
* /
var _getTexture = function ( render , imagePath ) {
var texture = render . textures [ imagePath ] ;
if ( ! texture )
texture = render . textures [ imagePath ] = PIXI . Texture . fromImage ( imagePath ) ;
return texture ;
} ;
} ) ( ) ;
; // End src/render/RenderPixi.js
2014-02-19 09:15:05 -05:00
// aliases
2014-03-30 14:45:30 -04:00
World . add = Composite . add ;
World . remove = Composite . remove ;
2014-03-24 16:11:42 -04:00
World . addComposite = Composite . addComposite ;
2014-02-19 09:15:05 -05:00
World . addBody = Composite . addBody ;
World . addConstraint = Composite . addConstraint ;
2014-03-24 16:11:42 -04:00
World . clear = Composite . clear ;
2014-02-19 09:15:05 -05:00
// exports
Matter . Body = Body ;
Matter . Composite = Composite ;
Matter . World = World ;
Matter . Contact = Contact ;
Matter . Detector = Detector ;
Matter . Grid = Grid ;
2014-03-24 16:11:42 -04:00
Matter . Pairs = Pairs ;
2014-02-19 09:15:05 -05:00
Matter . Pair = Pair ;
Matter . Resolver = Resolver ;
Matter . SAT = SAT ;
Matter . Constraint = Constraint ;
Matter . MouseConstraint = MouseConstraint ;
Matter . Common = Common ;
Matter . Engine = Engine ;
Matter . Metrics = Metrics ;
Matter . Mouse = Mouse ;
Matter . Sleeping = Sleeping ;
Matter . Bodies = Bodies ;
Matter . Composites = Composites ;
Matter . Axes = Axes ;
Matter . Bounds = Bounds ;
Matter . Vector = Vector ;
Matter . Vertices = Vertices ;
Matter . Render = Render ;
2014-03-22 13:51:49 -04:00
Matter . RenderPixi = RenderPixi ;
2014-03-11 06:29:14 -04:00
Matter . Events = Events ;
2014-05-01 09:09:06 -04:00
Matter . Query = Query ;
2014-02-19 09:15:05 -05:00
// CommonJS module
if ( typeof exports !== 'undefined' ) {
if ( typeof module !== 'undefined' && module . exports ) {
exports = module . exports = Matter ;
}
exports . Matter = Matter ;
}
// AMD module
if ( typeof define === 'function' && define . amd ) {
define ( 'Matter' , [ ] , function ( ) {
return Matter ;
} ) ;
}
// browser
if ( typeof window === 'object' && typeof window . document === 'object' ) {
window . Matter = Matter ;
}
// End Matter namespace closure
} ) ( ) ;