UNPKG

matter-js

Version:

a 2D rigid body physics engine for the web

1,441 lines (1,281 loc) 49.5 kB
/** * The `Matter.Body` module contains methods for creating and manipulating rigid bodies. * For creating bodies with common configurations such as rectangles, circles and other polygons see the module `Matter.Bodies`. * * See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). * @class Body */ var Body = {}; module.exports = Body; var Vertices = require('../geometry/Vertices'); var Vector = require('../geometry/Vector'); var Sleeping = require('../core/Sleeping'); var Common = require('../core/Common'); var Bounds = require('../geometry/Bounds'); var Axes = require('../geometry/Axes'); (function() { Body._timeCorrection = true; Body._inertiaScale = 4; Body._nextCollidingGroupId = 1; Body._nextNonCollidingGroupId = -1; Body._nextCategory = 0x0001; Body._baseDelta = 1000 / 60; /** * 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. * Vertices must be specified in clockwise order. * See the properties section below for detailed information on what you can pass via the `options` object. * @method create * @param {} options * @return {body} body */ Body.create = function(options) { var defaults = { id: Common.nextId(), type: 'body', label: 'Body', parts: [], plugin: {}, angle: 0, vertices: Vertices.fromPath('L 0 0 L 40 0 L 40 40 L 0 40'), position: { x: 0, y: 0 }, force: { x: 0, y: 0 }, torque: 0, positionImpulse: { x: 0, y: 0 }, constraintImpulse: { x: 0, y: 0, angle: 0 }, totalContacts: 0, speed: 0, angularSpeed: 0, velocity: { x: 0, y: 0 }, angularVelocity: 0, isSensor: false, isStatic: false, isSleeping: false, motion: 0, sleepThreshold: 60, density: 0.001, restitution: 0, friction: 0.1, frictionStatic: 0.5, frictionAir: 0.01, collisionFilter: { category: 0x0001, mask: 0xFFFFFFFF, group: 0 }, slop: 0.05, timeScale: 1, render: { visible: true, opacity: 1, strokeStyle: null, fillStyle: null, lineWidth: null, sprite: { xScale: 1, yScale: 1, xOffset: 0, yOffset: 0 } }, events: null, bounds: null, chamfer: null, circleRadius: 0, positionPrev: null, anglePrev: 0, parent: null, axes: null, area: 0, mass: 0, inertia: 0, deltaTime: 1000 / 60, _original: null }; var body = Common.extend(defaults, options); _initProperties(body, options); return body; }; /** * Returns the next unique group index for which bodies will collide. * If `isNonColliding` is `true`, returns the next unique group index for which bodies will _not_ collide. * See `body.collisionFilter` for more information. * @method nextGroup * @param {bool} [isNonColliding=false] * @return {Number} Unique group index */ Body.nextGroup = function(isNonColliding) { if (isNonColliding) return Body._nextNonCollidingGroupId--; return Body._nextCollidingGroupId++; }; /** * Returns the next unique category bitfield (starting after the initial default category `0x0001`). * There are 32 available. See `body.collisionFilter` for more information. * @method nextCategory * @return {Number} Unique category bitfield */ Body.nextCategory = function() { Body._nextCategory = Body._nextCategory << 1; return Body._nextCategory; }; /** * Initialises body properties. * @method _initProperties * @private * @param {body} body * @param {} [options] */ var _initProperties = function(body, options) { options = options || {}; // init required properties (order is important) Body.set(body, { bounds: body.bounds || Bounds.create(body.vertices), positionPrev: body.positionPrev || Vector.clone(body.position), anglePrev: body.anglePrev || body.angle, vertices: body.vertices, parts: body.parts || [body], isStatic: body.isStatic, isSleeping: body.isSleeping, parent: body.parent || body }); Vertices.rotate(body.vertices, body.angle, body.position); Axes.rotate(body.axes, body.angle); Bounds.update(body.bounds, body.vertices, body.velocity); // allow options to override the automatically calculated properties Body.set(body, { axes: options.axes || body.axes, area: options.area || body.area, mass: options.mass || body.mass, inertia: options.inertia || body.inertia }); // render properties var defaultFillStyle = (body.isStatic ? '#14151f' : Common.choose(['#f19648', '#f5d259', '#f55a3c', '#063e7b', '#ececd1'])), defaultStrokeStyle = body.isStatic ? '#555' : '#ccc', defaultLineWidth = body.isStatic && body.render.fillStyle === null ? 1 : 0; body.render.fillStyle = body.render.fillStyle || defaultFillStyle; body.render.strokeStyle = body.render.strokeStyle || defaultStrokeStyle; body.render.lineWidth = body.render.lineWidth || defaultLineWidth; body.render.sprite.xOffset += -(body.bounds.min.x - body.position.x) / (body.bounds.max.x - body.bounds.min.x); body.render.sprite.yOffset += -(body.bounds.min.y - body.position.y) / (body.bounds.max.y - body.bounds.min.y); }; /** * Given a property and a value (or map of), sets the property(s) on the body, using the appropriate setter functions if they exist. * Prefer to use the actual setter functions in performance critical situations. * @method set * @param {body} body * @param {} settings A property name (or map of properties and values) to set on the body. * @param {} value The value to set if `settings` is a single property name. */ Body.set = function(body, settings, value) { var property; if (typeof settings === 'string') { property = settings; settings = {}; settings[property] = value; } for (property in settings) { if (!Object.prototype.hasOwnProperty.call(settings, property)) continue; value = settings[property]; switch (property) { case 'isStatic': Body.setStatic(body, value); break; case 'isSleeping': Sleeping.set(body, value); break; case 'mass': Body.setMass(body, value); break; case 'density': Body.setDensity(body, value); break; case 'inertia': Body.setInertia(body, value); break; case 'vertices': Body.setVertices(body, value); break; case 'position': Body.setPosition(body, value); break; case 'angle': Body.setAngle(body, value); break; case 'velocity': Body.setVelocity(body, value); break; case 'angularVelocity': Body.setAngularVelocity(body, value); break; case 'speed': Body.setSpeed(body, value); break; case 'angularSpeed': Body.setAngularSpeed(body, value); break; case 'parts': Body.setParts(body, value); break; case 'centre': Body.setCentre(body, value); break; default: body[property] = value; } } }; /** * Sets the body as static, including isStatic flag and setting mass and inertia to Infinity. * @method setStatic * @param {body} body * @param {bool} isStatic */ Body.setStatic = function(body, isStatic) { for (var i = 0; i < body.parts.length; i++) { var part = body.parts[i]; part.isStatic = isStatic; if (isStatic) { part._original = { restitution: part.restitution, friction: part.friction, mass: part.mass, inertia: part.inertia, density: part.density, inverseMass: part.inverseMass, inverseInertia: part.inverseInertia }; part.restitution = 0; part.friction = 1; part.mass = part.inertia = part.density = Infinity; part.inverseMass = part.inverseInertia = 0; part.positionPrev.x = part.position.x; part.positionPrev.y = part.position.y; part.anglePrev = part.angle; part.angularVelocity = 0; part.speed = 0; part.angularSpeed = 0; part.motion = 0; } else if (part._original) { part.restitution = part._original.restitution; part.friction = part._original.friction; part.mass = part._original.mass; part.inertia = part._original.inertia; part.density = part._original.density; part.inverseMass = part._original.inverseMass; part.inverseInertia = part._original.inverseInertia; part._original = null; } } }; /** * Sets the mass of the body. Inverse mass, density and inertia are automatically updated to reflect the change. * @method setMass * @param {body} body * @param {number} mass */ Body.setMass = function(body, mass) { var moment = body.inertia / (body.mass / 6); body.inertia = moment * (mass / 6); body.inverseInertia = 1 / body.inertia; body.mass = mass; body.inverseMass = 1 / body.mass; body.density = body.mass / body.area; }; /** * Sets the density of the body. Mass and inertia are automatically updated to reflect the change. * @method setDensity * @param {body} body * @param {number} density */ Body.setDensity = function(body, density) { Body.setMass(body, density * body.area); body.density = density; }; /** * Sets the moment of inertia of the body. This is the second moment of area in two dimensions. * Inverse inertia is automatically updated to reflect the change. Mass is not changed. * @method setInertia * @param {body} body * @param {number} inertia */ Body.setInertia = function(body, inertia) { body.inertia = inertia; body.inverseInertia = 1 / body.inertia; }; /** * 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 vertices must be decomposed into convex parts. * * @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.setMass(body, body.density * body.area); // 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.setInertia(body, Body._inertiaScale * Vertices.inertia(body.vertices, body.mass)); // update geometry Vertices.translate(body.vertices, body.position); Bounds.update(body.bounds, body.vertices, body.velocity); }; /** * Sets the parts of the `body` and updates mass, inertia and centroid. * Each part will have its parent set to `body`. * By default the convex hull will be automatically computed and set on `body`, unless `autoHull` is set to `false.` * Note that this method will ensure that the first part in `body.parts` will always be the `body`. * @method setParts * @param {body} body * @param {body[]} parts * @param {bool} [autoHull=true] */ Body.setParts = function(body, parts, autoHull) { var i; // add all the parts, ensuring that the first part is always the parent body parts = parts.slice(0); body.parts.length = 0; body.parts.push(body); body.parent = body; for (i = 0; i < parts.length; i++) { var part = parts[i]; if (part !== body) { part.parent = body; body.parts.push(part); } } if (body.parts.length === 1) return; autoHull = typeof autoHull !== 'undefined' ? autoHull : true; // find the convex hull of all parts to set on the parent body if (autoHull) { var vertices = []; for (i = 0; i < parts.length; i++) { vertices = vertices.concat(parts[i].vertices); } Vertices.clockwiseSort(vertices); var hull = Vertices.hull(vertices), hullCentre = Vertices.centre(hull); Body.setVertices(body, hull); Vertices.translate(body.vertices, hullCentre); } // sum the properties of all compound parts of the parent body var total = Body._totalProperties(body); body.area = total.area; body.parent = body; body.position.x = total.centre.x; body.position.y = total.centre.y; body.positionPrev.x = total.centre.x; body.positionPrev.y = total.centre.y; Body.setMass(body, total.mass); Body.setInertia(body, total.inertia); Body.setPosition(body, total.centre); }; /** * Set the centre of mass of the body. * The `centre` is a vector in world-space unless `relative` is set, in which case it is a translation. * The centre of mass is the point the body rotates about and can be used to simulate non-uniform density. * This is equal to moving `body.position` but not the `body.vertices`. * Invalid if the `centre` falls outside the body's convex hull. * @method setCentre * @param {body} body * @param {vector} centre * @param {bool} relative */ Body.setCentre = function(body, centre, relative) { if (!relative) { body.positionPrev.x = centre.x - (body.position.x - body.positionPrev.x); body.positionPrev.y = centre.y - (body.position.y - body.positionPrev.y); body.position.x = centre.x; body.position.y = centre.y; } else { body.positionPrev.x += centre.x; body.positionPrev.y += centre.y; body.position.x += centre.x; body.position.y += centre.y; } }; /** * Sets the position of the body. By default velocity is unchanged. * If `updateVelocity` is `true` then velocity is inferred from the change in position. * @method setPosition * @param {body} body * @param {vector} position * @param {boolean} [updateVelocity=false] */ Body.setPosition = function(body, position, updateVelocity) { var delta = Vector.sub(position, body.position); if (updateVelocity) { body.positionPrev.x = body.position.x; body.positionPrev.y = body.position.y; body.velocity.x = delta.x; body.velocity.y = delta.y; body.speed = Vector.magnitude(delta); } else { body.positionPrev.x += delta.x; body.positionPrev.y += delta.y; } for (var i = 0; i < body.parts.length; i++) { var part = body.parts[i]; part.position.x += delta.x; part.position.y += delta.y; Vertices.translate(part.vertices, delta); Bounds.update(part.bounds, part.vertices, body.velocity); } }; /** * Sets the angle of the body. By default angular velocity is unchanged. * If `updateVelocity` is `true` then angular velocity is inferred from the change in angle. * @method setAngle * @param {body} body * @param {number} angle * @param {boolean} [updateVelocity=false] */ Body.setAngle = function(body, angle, updateVelocity) { var delta = angle - body.angle; if (updateVelocity) { body.anglePrev = body.angle; body.angularVelocity = delta; body.angularSpeed = Math.abs(delta); } else { body.anglePrev += delta; } for (var i = 0; i < body.parts.length; i++) { var part = body.parts[i]; part.angle += delta; Vertices.rotate(part.vertices, delta, body.position); Axes.rotate(part.axes, delta); Bounds.update(part.bounds, part.vertices, body.velocity); if (i > 0) { Vector.rotateAbout(part.position, delta, body.position, part.position); } } }; /** * Sets the current linear velocity of the body. * Affects body speed. * @method setVelocity * @param {body} body * @param {vector} velocity */ Body.setVelocity = function(body, velocity) { var timeScale = body.deltaTime / Body._baseDelta; body.positionPrev.x = body.position.x - velocity.x * timeScale; body.positionPrev.y = body.position.y - velocity.y * timeScale; body.velocity.x = (body.position.x - body.positionPrev.x) / timeScale; body.velocity.y = (body.position.y - body.positionPrev.y) / timeScale; body.speed = Vector.magnitude(body.velocity); }; /** * Gets the current linear velocity of the body. * @method getVelocity * @param {body} body * @return {vector} velocity */ Body.getVelocity = function(body) { var timeScale = Body._baseDelta / body.deltaTime; return { x: (body.position.x - body.positionPrev.x) * timeScale, y: (body.position.y - body.positionPrev.y) * timeScale }; }; /** * Gets the current linear speed of the body. * Equivalent to the magnitude of its velocity. * @method getSpeed * @param {body} body * @return {number} speed */ Body.getSpeed = function(body) { return Vector.magnitude(Body.getVelocity(body)); }; /** * Sets the current linear speed of the body. * Direction is maintained. Affects body velocity. * @method setSpeed * @param {body} body * @param {number} speed */ Body.setSpeed = function(body, speed) { Body.setVelocity(body, Vector.mult(Vector.normalise(Body.getVelocity(body)), speed)); }; /** * Sets the current rotational velocity of the body. * Affects body angular speed. * @method setAngularVelocity * @param {body} body * @param {number} velocity */ Body.setAngularVelocity = function(body, velocity) { var timeScale = body.deltaTime / Body._baseDelta; body.anglePrev = body.angle - velocity * timeScale; body.angularVelocity = (body.angle - body.anglePrev) / timeScale; body.angularSpeed = Math.abs(body.angularVelocity); }; /** * Gets the current rotational velocity of the body. * @method getAngularVelocity * @param {body} body * @return {number} angular velocity */ Body.getAngularVelocity = function(body) { return (body.angle - body.anglePrev) * Body._baseDelta / body.deltaTime; }; /** * Gets the current rotational speed of the body. * Equivalent to the magnitude of its angular velocity. * @method getAngularSpeed * @param {body} body * @return {number} angular speed */ Body.getAngularSpeed = function(body) { return Math.abs(Body.getAngularVelocity(body)); }; /** * Sets the current rotational speed of the body. * Direction is maintained. Affects body angular velocity. * @method setAngularSpeed * @param {body} body * @param {number} speed */ Body.setAngularSpeed = function(body, speed) { Body.setAngularVelocity(body, Common.sign(Body.getAngularVelocity(body)) * speed); }; /** * Moves a body by a given vector relative to its current position. By default velocity is unchanged. * If `updateVelocity` is `true` then velocity is inferred from the change in position. * @method translate * @param {body} body * @param {vector} translation * @param {boolean} [updateVelocity=false] */ Body.translate = function(body, translation, updateVelocity) { Body.setPosition(body, Vector.add(body.position, translation), updateVelocity); }; /** * Rotates a body by a given angle relative to its current angle. By default angular velocity is unchanged. * If `updateVelocity` is `true` then angular velocity is inferred from the change in angle. * @method rotate * @param {body} body * @param {number} rotation * @param {vector} [point] * @param {boolean} [updateVelocity=false] */ Body.rotate = function(body, rotation, point, updateVelocity) { if (!point) { Body.setAngle(body, body.angle + rotation, updateVelocity); } else { var cos = Math.cos(rotation), sin = Math.sin(rotation), dx = body.position.x - point.x, dy = body.position.y - point.y; Body.setPosition(body, { x: point.x + (dx * cos - dy * sin), y: point.y + (dx * sin + dy * cos) }, updateVelocity); Body.setAngle(body, body.angle + rotation, updateVelocity); } }; /** * 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) { var totalArea = 0, totalInertia = 0; point = point || body.position; for (var i = 0; i < body.parts.length; i++) { var part = body.parts[i]; // scale vertices Vertices.scale(part.vertices, scaleX, scaleY, point); // update properties part.axes = Axes.fromVertices(part.vertices); part.area = Vertices.area(part.vertices); Body.setMass(part, body.density * part.area); // update inertia (requires vertices to be at origin) Vertices.translate(part.vertices, { x: -part.position.x, y: -part.position.y }); Body.setInertia(part, Body._inertiaScale * Vertices.inertia(part.vertices, part.mass)); Vertices.translate(part.vertices, { x: part.position.x, y: part.position.y }); if (i > 0) { totalArea += part.area; totalInertia += part.inertia; } // scale position part.position.x = point.x + (part.position.x - point.x) * scaleX; part.position.y = point.y + (part.position.y - point.y) * scaleY; // update bounds Bounds.update(part.bounds, part.vertices, body.velocity); } // handle parent body if (body.parts.length > 1) { body.area = totalArea; if (!body.isStatic) { Body.setMass(body, body.density * totalArea); Body.setInertia(body, totalInertia); } } // handle circles if (body.circleRadius) { if (scaleX === scaleY) { body.circleRadius *= scaleX; } else { // body is no longer a circle body.circleRadius = null; } } }; /** * Performs an update by integrating the equations of motion on the `body`. * This is applied every update by `Matter.Engine` automatically. * @method update * @param {body} body * @param {number} [deltaTime=16.666] */ Body.update = function(body, deltaTime) { deltaTime = (typeof deltaTime !== 'undefined' ? deltaTime : (1000 / 60)) * body.timeScale; var deltaTimeSquared = deltaTime * deltaTime, correction = Body._timeCorrection ? deltaTime / (body.deltaTime || deltaTime) : 1; // from the previous step var frictionAir = 1 - body.frictionAir * (deltaTime / Common._baseDelta), velocityPrevX = (body.position.x - body.positionPrev.x) * correction, velocityPrevY = (body.position.y - body.positionPrev.y) * correction; // update velocity with Verlet integration body.velocity.x = (velocityPrevX * frictionAir) + (body.force.x / body.mass) * deltaTimeSquared; body.velocity.y = (velocityPrevY * frictionAir) + (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; body.deltaTime = deltaTime; // 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; // transform the body geometry for (var i = 0; i < body.parts.length; i++) { var part = body.parts[i]; Vertices.translate(part.vertices, body.velocity); if (i > 0) { part.position.x += body.velocity.x; part.position.y += body.velocity.y; } if (body.angularVelocity !== 0) { Vertices.rotate(part.vertices, body.angularVelocity, body.position); Axes.rotate(part.axes, body.angularVelocity); if (i > 0) { Vector.rotateAbout(part.position, body.angularVelocity, body.position, part.position); } } Bounds.update(part.bounds, part.vertices, body.velocity); } }; /** * Updates properties `body.velocity`, `body.speed`, `body.angularVelocity` and `body.angularSpeed` which are normalised in relation to `Body._baseDelta`. * @method updateVelocities * @param {body} body */ Body.updateVelocities = function(body) { var timeScale = Body._baseDelta / body.deltaTime, bodyVelocity = body.velocity; bodyVelocity.x = (body.position.x - body.positionPrev.x) * timeScale; bodyVelocity.y = (body.position.y - body.positionPrev.y) * timeScale; body.speed = Math.sqrt((bodyVelocity.x * bodyVelocity.x) + (bodyVelocity.y * bodyVelocity.y)); body.angularVelocity = (body.angle - body.anglePrev) * timeScale; body.angularSpeed = Math.abs(body.angularVelocity); }; /** * Applies the `force` to the `body` from the force origin `position` in world-space, over a single timestep, including applying any resulting angular torque. * * Forces are useful for effects like gravity, wind or rocket thrust, but can be difficult in practice when precise control is needed. In these cases see `Body.setVelocity` and `Body.setPosition` as an alternative. * * The force from this function is only applied once for the duration of a single timestep, in other words the duration depends directly on the current engine update `delta` and the rate of calls to this function. * * Therefore to account for time, you should apply the force constantly over as many engine updates as equivalent to the intended duration. * * If all or part of the force duration is some fraction of a timestep, first multiply the force by `duration / timestep`. * * The force origin `position` in world-space must also be specified. Passing `body.position` will result in zero angular effect as the force origin would be at the centre of mass. * * The `body` will take time to accelerate under a force, the resulting effect depends on duration of the force, the body mass and other forces on the body including friction combined. * @method applyForce * @param {body} body * @param {vector} position The force origin in world-space. Pass `body.position` to avoid angular torque. * @param {vector} force */ Body.applyForce = function(body, position, force) { var offset = { x: position.x - body.position.x, y: position.y - body.position.y }; body.force.x += force.x; body.force.y += force.y; body.torque += offset.x * force.y - offset.y * force.x; }; /** * Returns the sums of the properties of all compound parts of the parent body. * @method _totalProperties * @private * @param {body} body * @return {} */ Body._totalProperties = function(body) { // from equations at: // https://ecourses.ou.edu/cgi-bin/ebook.cgi?doc=&topic=st&chap_sec=07.2&page=theory // http://output.to/sideway/default.asp?qno=121100087 var properties = { mass: 0, area: 0, inertia: 0, centre: { x: 0, y: 0 } }; // sum the properties of all compound parts of the parent body for (var i = body.parts.length === 1 ? 0 : 1; i < body.parts.length; i++) { var part = body.parts[i], mass = part.mass !== Infinity ? part.mass : 1; properties.mass += mass; properties.area += part.area; properties.inertia += part.inertia; properties.centre = Vector.add(properties.centre, Vector.mult(part.position, mass)); } properties.centre = Vector.div(properties.centre, properties.mass); return properties; }; /* * * Events Documentation * */ /** * Fired when a body starts sleeping (where `this` is the body). * * @event sleepStart * @this {body} The body that has started sleeping * @param {} event An event object * @param {} event.source The source object of the event * @param {} event.name The name of the event */ /** * Fired when a body ends sleeping (where `this` is the body). * * @event sleepEnd * @this {body} The body that has ended sleeping * @param {} event An event object * @param {} event.source The source object of the event * @param {} event.name The name of the event */ /* * * Properties Documentation * */ /** * An integer `Number` uniquely identifying number generated in `Body.create` by `Common.nextId`. * * @property id * @type number */ /** * _Read only_. Set by `Body.create`. * * A `String` denoting the type of object. * * @readOnly * @property type * @type string * @default "body" */ /** * An arbitrary `String` name to help the user identify and manage bodies. * * @property label * @type string * @default "Body" */ /** * _Read only_. Use `Body.setParts` to set. * * An array of bodies that make up this body. * The first body in the array must always be a self reference to the current body instance. * All bodies in the `parts` array together form a single rigid compound body. * Parts are allowed to overlap, have gaps or holes or even form concave bodies. * Parts themselves should never be added to a `World`, only the parent body should be. * Use `Body.setParts` when setting parts to ensure correct updates of all properties. * * @readOnly * @property parts * @type body[] */ /** * An object reserved for storing plugin-specific properties. * * @property plugin * @type {} */ /** * _Read only_. Updated by `Body.setParts`. * * A reference to the body that this is a part of. See `body.parts`. * This is a self reference if the body is not a part of another body. * * @readOnly * @property parent * @type body */ /** * A `Number` specifying the angle of the body, in radians. * * @property angle * @type number * @default 0 */ /** * _Read only_. Use `Body.setVertices` or `Body.setParts` to set. See also `Bodies.fromVertices`. * * 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 }]` * * Vertices must always be convex, in clockwise order and must not contain any duplicate points. * * Concave vertices should be decomposed into convex `parts`, see `Bodies.fromVertices` and `Body.setParts`. * * When set the vertices are translated such that `body.position` is at the centre of mass. * Many other body properties are automatically calculated from these vertices when set including `density`, `area` and `inertia`. * * The module `Matter.Vertices` contains useful methods for working with vertices. * * @readOnly * @property vertices * @type vector[] */ /** * _Read only_. Use `Body.setPosition` to set. * * A `Vector` that specifies the current world-space position of the body. * * @readOnly * @property position * @type vector * @default { x: 0, y: 0 } */ /** * A `Vector` that accumulates the total force applied to the body for a single update. * Force is zeroed after every `Engine.update`, so constant forces should be applied for every update they are needed. See also `Body.applyForce`. * * @property force * @type vector * @default { x: 0, y: 0 } */ /** * A `Number` that accumulates the total torque (turning force) applied to the body for a single update. See also `Body.applyForce`. * Torque is zeroed after every `Engine.update`, so constant torques should be applied for every update they are needed. * * Torques result in angular acceleration on every update, which depends on body inertia and the engine update delta. * * @property torque * @type number * @default 0 */ /** * _Read only_. Use `Body.setSpeed` to set. * * See `Body.getSpeed` for details. * * Equivalent to the magnitude of `body.velocity` (always positive). * * @readOnly * @property speed * @type number * @default 0 */ /** * _Read only_. Use `Body.setVelocity` to set. * * See `Body.getVelocity` for details. * * Equivalent to the magnitude of `body.angularVelocity` (always positive). * * @readOnly * @property velocity * @type vector * @default { x: 0, y: 0 } */ /** * _Read only_. Use `Body.setAngularSpeed` to set. * * See `Body.getAngularSpeed` for details. * * * @readOnly * @property angularSpeed * @type number * @default 0 */ /** * _Read only_. Use `Body.setAngularVelocity` to set. * * See `Body.getAngularVelocity` for details. * * * @readOnly * @property angularVelocity * @type number * @default 0 */ /** * _Read only_. Use `Body.setStatic` to set. * * A flag that indicates whether a body is considered static. A static body can never change position or angle and is completely fixed. * * @readOnly * @property isStatic * @type boolean * @default false */ /** * A flag that indicates whether a body is a sensor. Sensor triggers collision events, but doesn't react with colliding body physically. * * @property isSensor * @type boolean * @default false */ /** * _Read only_. Use `Sleeping.set` to set. * * 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. * * @readOnly * @property isSleeping * @type boolean * @default false */ /** * _Read only_. Calculated during engine update only when sleeping is enabled. * * A `Number` that loosely measures the amount of movement a body currently has. * * Derived from `body.speed^2 + body.angularSpeed^2`. See `Sleeping.update`. * * @readOnly * @property motion * @type number * @default 0 */ /** * A `Number` that defines the length of time during 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 */ /** * _Read only_. Use `Body.setDensity` to set. * * A `Number` that defines the density of the body (mass per unit area). * * Mass will also be updated when set. * * @readOnly * @property density * @type number * @default 0.001 */ /** * _Read only_. Use `Body.setMass` to set. * * A `Number` that defines the mass of the body. * * Density will also be updated when set. * * @readOnly * @property mass * @type number */ /** * _Read only_. Use `Body.setMass` to set. * * A `Number` that defines the inverse mass of the body (`1 / mass`). * * @readOnly * @property inverseMass * @type number */ /** * _Read only_. Automatically calculated when vertices, mass or density are set or set through `Body.setInertia`. * * A `Number` that defines the moment of inertia of the body. This is the second moment of area in two dimensions. * * Can be manually set to `Infinity` to prevent rotation of the body. See `Body.setInertia`. * * @readOnly * @property inertia * @type number */ /** * _Read only_. Automatically calculated when vertices, mass or density are set or calculated by `Body.setInertia`. * * A `Number` that defines the inverse moment of inertia of the body (`1 / inertia`). * * @readOnly * @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 static friction of the body (in the Coulomb friction model). * A value of `0` means the body will never 'stick' when it is nearly stationary and only dynamic `friction` is used. * The higher the value (e.g. `10`), the more force it will take to initially get the body moving when nearly stationary. * This value is multiplied with the `friction` property to make it easier to change `friction` and maintain an appropriate amount of static friction. * * @property frictionStatic * @type number * @default 0.5 */ /** * 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 `Object` that specifies the collision filtering properties of this body. * * Collisions between two bodies will obey the following rules: * - If the two bodies have the same non-zero value of `collisionFilter.group`, * they will always collide if the value is positive, and they will never collide * if the value is negative. * - If the two bodies have different values of `collisionFilter.group` or if one * (or both) of the bodies has a value of 0, then the category/mask rules apply as follows: * * Each body belongs to a collision category, given by `collisionFilter.category`. This * value is used as a bit field and the category should have only one bit set, meaning that * the value of this property is a power of two in the range [1, 2^31]. Thus, there are 32 * different collision categories available. * * Each body also defines a collision bitmask, given by `collisionFilter.mask` which specifies * the categories it collides with (the value is the bitwise AND value of all these categories). * * Using the category/mask rules, two bodies `A` and `B` collide if each includes the other's * category in its mask, i.e. `(categoryA & maskB) !== 0` and `(categoryB & maskA) !== 0` * are both true. * * @property collisionFilter * @type object */ /** * An Integer `Number`, that specifies the collision group this body belongs to. * See `body.collisionFilter` for more information. * * @property collisionFilter.group * @type object * @default 0 */ /** * A bit field that specifies the collision category this body belongs to. * The category value should have only one bit set, for example `0x0001`. * This means there are up to 32 unique collision categories available. * See `body.collisionFilter` for more information. * * @property collisionFilter.category * @type object * @default 1 */ /** * A bit mask that specifies the collision categories this body may collide with. * See `body.collisionFilter` for more information. * * @property collisionFilter.mask * @type object * @default -1 */ /** * A `Number` that specifies a thin boundary around the body where it is allowed to slightly sink into other bodies. * * This is required for proper collision response, including friction and restitution effects. * * The default should generally suffice in most cases. You may need to decrease this value for very small bodies that are nearing the default value in scale. * * @property slop * @type number * @default 0.05 */ /** * A `Number` that specifies per-body time scaling. * * @property timeScale * @type number * @default 1 */ /** * _Read only_. Updated during engine update. * * A `Number` that records the last delta time value used to update this body. * Used to calculate speed and velocity. * * @readOnly * @property deltaTime * @type number * @default 1000 / 60 */ /** * 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 */ /** * Sets the opacity to use when rendering. * * @property render.opacity * @type number * @default 1 */ /** * 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 offset in the x-axis for the sprite (normalised by texture width). * * @property render.sprite.xOffset * @type number * @default 0 */ /** * A `Number` that defines the offset in the y-axis for the sprite (normalised by texture height). * * @property render.sprite.yOffset * @type number * @default 0 */ /** * 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 0 */ /** * 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 */ /** * _Read only_. Calculated automatically when vertices are set. * * An array of unique axis vectors (edge normals) used for collision detection. * These are automatically calculated when vertices are set. * They are constantly updated by `Body.update` during the simulation. * * @readOnly * @property axes * @type vector[] */ /** * _Read only_. Calculated automatically when vertices are set. * * A `Number` that measures the area of the body's convex hull. * * @readOnly * @property area * @type string * @default */ /** * A `Bounds` object that defines the AABB region for the body. * It is automatically calculated when vertices are set and constantly updated by `Body.update` during simulation. * * @property bounds * @type bounds */ })();