Show:

File: src/constraint/Constraint.js

                        /**
                        * 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.
                        *
                        * See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).
                        *
                        * @class Constraint
                        */
                        
                        var Constraint = {};
                        
                        module.exports = Constraint;
                        
                        var Vertices = require('../geometry/Vertices');
                        var Vector = require('../geometry/Vector');
                        var Sleeping = require('../core/Sleeping');
                        var Bounds = require('../geometry/Bounds');
                        var Axes = require('../geometry/Axes');
                        var Common = require('../core/Common');
                        
                        (function() {
                        
                            Constraint._warming = 0.4;
                            Constraint._torqueDampen = 1;
                            Constraint._minLength = 0.000001;
                        
                            /**
                             * Creates a new constraint.
                             * All properties have default values, and many are pre-calculated automatically based on other properties.
                             * To simulate a revolute constraint (or pin joint) set `length: 0` and a high `stiffness` value (e.g. `0.7` or above).
                             * If the constraint is unstable, try lowering the `stiffness` value and / or increasing `engine.constraintIterations`.
                             * See the properties section below for detailed information on what you can pass via the `options` object.
                             * @method create
                             * @param {} options
                             * @return {constraint} constraint
                             */
                            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 = typeof constraint.length !== 'undefined' ? constraint.length : length;
                        
                                // option defaults
                                constraint.id = constraint.id || Common.nextId();
                                constraint.label = constraint.label || 'Constraint';
                                constraint.type = 'constraint';
                                constraint.stiffness = constraint.stiffness || (constraint.length > 0 ? 1 : 0.7);
                                constraint.damping = constraint.damping || 0;
                                constraint.angularStiffness = constraint.angularStiffness || 0;
                                constraint.angleA = constraint.bodyA ? constraint.bodyA.angle : constraint.angleA;
                                constraint.angleB = constraint.bodyB ? constraint.bodyB.angle : constraint.angleB;
                                constraint.plugin = {};
                        
                                // render
                                var render = {
                                    visible: true,
                                    lineWidth: 2,
                                    strokeStyle: '#ffffff',
                                    type: 'line',
                                    anchors: true
                                };
                        
                                if (constraint.length === 0 && constraint.stiffness > 0.1) {
                                    render.type = 'pin';
                                    render.anchors = false;
                                } else if (constraint.stiffness < 0.9) {
                                    render.type = 'spring';
                                }
                        
                                constraint.render = Common.extend(render, constraint.render);
                        
                                return constraint;
                            };
                        
                            /**
                             * Prepares for solving by constraint warming.
                             * @private
                             * @method preSolveAll
                             * @param {body[]} bodies
                             */
                            Constraint.preSolveAll = function(bodies) {
                                for (var i = 0; i < bodies.length; i += 1) {
                                    var body = bodies[i],
                                        impulse = body.constraintImpulse;
                        
                                    if (body.isStatic || (impulse.x === 0 && impulse.y === 0 && impulse.angle === 0)) {
                                        continue;
                                    }
                        
                                    body.position.x += impulse.x;
                                    body.position.y += impulse.y;
                                    body.angle += impulse.angle;
                                }
                            };
                        
                            /**
                             * Solves all constraints in a list of collisions.
                             * @private
                             * @method solveAll
                             * @param {constraint[]} constraints
                             * @param {number} timeScale
                             */
                            Constraint.solveAll = function(constraints, timeScale) {
                                // Solve fixed constraints first.
                                for (var i = 0; i < constraints.length; i += 1) {
                                    var constraint = constraints[i],
                                        fixedA = !constraint.bodyA || (constraint.bodyA && constraint.bodyA.isStatic),
                                        fixedB = !constraint.bodyB || (constraint.bodyB && constraint.bodyB.isStatic);
                        
                                    if (fixedA || fixedB) {
                                        Constraint.solve(constraints[i], timeScale);
                                    }
                                }
                        
                                // Solve free constraints last.
                                for (i = 0; i < constraints.length; i += 1) {
                                    constraint = constraints[i];
                                    fixedA = !constraint.bodyA || (constraint.bodyA && constraint.bodyA.isStatic);
                                    fixedB = !constraint.bodyB || (constraint.bodyB && constraint.bodyB.isStatic);
                        
                                    if (!fixedA && !fixedB) {
                                        Constraint.solve(constraints[i], timeScale);
                                    }
                                }
                            };
                        
                            /**
                             * Solves a distance constraint with Gauss-Siedel method.
                             * @private
                             * @method solve
                             * @param {constraint} constraint
                             * @param {number} timeScale
                             */
                            Constraint.solve = function(constraint, timeScale) {
                                var bodyA = constraint.bodyA,
                                    bodyB = constraint.bodyB,
                                    pointA = constraint.pointA,
                                    pointB = constraint.pointB;
                        
                                if (!bodyA && !bodyB)
                                    return;
                        
                                // update reference angle
                                if (bodyA && !bodyA.isStatic) {
                                    Vector.rotate(pointA, bodyA.angle - constraint.angleA, pointA);
                                    constraint.angleA = bodyA.angle;
                                }
                                
                                // update reference angle
                                if (bodyB && !bodyB.isStatic) {
                                    Vector.rotate(pointB, bodyB.angle - constraint.angleB, pointB);
                                    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 < Constraint._minLength) {
                                    currentLength = Constraint._minLength;
                                }
                        
                                // solve distance constraint with Gauss-Siedel method
                                var difference = (currentLength - constraint.length) / currentLength,
                                    stiffness = constraint.stiffness < 1 ? constraint.stiffness * timeScale : constraint.stiffness,
                                    force = Vector.mult(delta, difference * stiffness),
                                    massTotal = (bodyA ? bodyA.inverseMass : 0) + (bodyB ? bodyB.inverseMass : 0),
                                    inertiaTotal = (bodyA ? bodyA.inverseInertia : 0) + (bodyB ? bodyB.inverseInertia : 0),
                                    resistanceTotal = massTotal + inertiaTotal,
                                    torque,
                                    share,
                                    normal,
                                    normalVelocity,
                                    relativeVelocity;
                        
                                if (constraint.damping) {
                                    var zero = Vector.create();
                                    normal = Vector.div(delta, currentLength);
                        
                                    relativeVelocity = Vector.sub(
                                        bodyB && Vector.sub(bodyB.position, bodyB.positionPrev) || zero,
                                        bodyA && Vector.sub(bodyA.position, bodyA.positionPrev) || zero
                                    );
                        
                                    normalVelocity = Vector.dot(normal, relativeVelocity);
                                }
                        
                                if (bodyA && !bodyA.isStatic) {
                                    share = bodyA.inverseMass / massTotal;
                        
                                    // keep track of applied impulses for post solving
                                    bodyA.constraintImpulse.x -= force.x * share;
                                    bodyA.constraintImpulse.y -= force.y * share;
                        
                                    // apply forces
                                    bodyA.position.x -= force.x * share;
                                    bodyA.position.y -= force.y * share;
                        
                                    // apply damping
                                    if (constraint.damping) {
                                        bodyA.positionPrev.x -= constraint.damping * normal.x * normalVelocity * share;
                                        bodyA.positionPrev.y -= constraint.damping * normal.y * normalVelocity * share;
                                    }
                        
                                    // apply torque
                                    torque = (Vector.cross(pointA, force) / resistanceTotal) * Constraint._torqueDampen * bodyA.inverseInertia * (1 - constraint.angularStiffness);
                                    bodyA.constraintImpulse.angle -= torque;
                                    bodyA.angle -= torque;
                                }
                        
                                if (bodyB && !bodyB.isStatic) {
                                    share = bodyB.inverseMass / massTotal;
                        
                                    // keep track of applied impulses for post solving
                                    bodyB.constraintImpulse.x += force.x * share;
                                    bodyB.constraintImpulse.y += force.y * share;
                                    
                                    // apply forces
                                    bodyB.position.x += force.x * share;
                                    bodyB.position.y += force.y * share;
                        
                                    // apply damping
                                    if (constraint.damping) {
                                        bodyB.positionPrev.x += constraint.damping * normal.x * normalVelocity * share;
                                        bodyB.positionPrev.y += constraint.damping * normal.y * normalVelocity * share;
                                    }
                        
                                    // apply torque
                                    torque = (Vector.cross(pointB, force) / resistanceTotal) * Constraint._torqueDampen * bodyB.inverseInertia * (1 - constraint.angularStiffness);
                                    bodyB.constraintImpulse.angle += torque;
                                    bodyB.angle += torque;
                                }
                        
                            };
                        
                            /**
                             * Performs body updates required after solving constraints.
                             * @private
                             * @method postSolveAll
                             * @param {body[]} bodies
                             */
                            Constraint.postSolveAll = function(bodies) {
                                for (var i = 0; i < bodies.length; i++) {
                                    var body = bodies[i],
                                        impulse = body.constraintImpulse;
                        
                                    if (body.isStatic || (impulse.x === 0 && impulse.y === 0 && impulse.angle === 0)) {
                                        continue;
                                    }
                        
                                    Sleeping.set(body, false);
                        
                                    // update geometry and reset
                                    for (var j = 0; j < body.parts.length; j++) {
                                        var part = body.parts[j];
                                        
                                        Vertices.translate(part.vertices, impulse);
                        
                                        if (j > 0) {
                                            part.position.x += impulse.x;
                                            part.position.y += impulse.y;
                                        }
                        
                                        if (impulse.angle !== 0) {
                                            Vertices.rotate(part.vertices, impulse.angle, body.position);
                                            Axes.rotate(part.axes, impulse.angle);
                                            if (j > 0) {
                                                Vector.rotateAbout(part.position, impulse.angle, body.position, part.position);
                                            }
                                        }
                        
                                        Bounds.update(part.bounds, part.vertices, body.velocity);
                                    }
                        
                                    // dampen the cached impulse for warming next step
                                    impulse.angle *= Constraint._warming;
                                    impulse.x *= Constraint._warming;
                                    impulse.y *= Constraint._warming;
                                }
                            };
                        
                            /*
                            *
                            *  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"
                             * @readOnly
                             */
                        
                            /**
                             * 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
                             */
                        
                            /**
                             * A `String` that defines the constraint rendering type. 
                             * The possible values are 'line', 'pin', 'spring'.
                             * An appropriate render type will be automatically chosen unless one is given in options.
                             *
                             * @property render.type
                             * @type string
                             * @default 'line'
                             */
                        
                            /**
                             * A `Boolean` that defines if the constraint's anchor points should be rendered.
                             *
                             * @property render.anchors
                             * @type boolean
                             * @default true
                             */
                        
                            /**
                             * 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 damping of the constraint, 
                             * i.e. the amount of resistance applied to each body based on their velocities to limit the amount of oscillation.
                             * Damping will only be apparent when the constraint also has a very low `stiffness`.
                             * A value of `0.1` means the constraint will apply heavy damping, resulting in little to no oscillation.
                             * A value of `0` means the constraint will apply no damping.
                             *
                             * @property damping
                             * @type number
                             * @default 0
                             */
                        
                            /**
                             * A `Number` that specifies the target resting length of the constraint. 
                             * It is calculated automatically in `Constraint.create` from initial positions of the `constraint.bodyA` and `constraint.bodyB`.
                             *
                             * @property length
                             * @type number
                             */
                        
                            /**
                             * An object reserved for storing plugin-specific properties.
                             *
                             * @property plugin
                             * @type {}
                             */
                        
                        })();
                        
                            
0.13.0