ConstantVolumeJoint class
class ConstantVolumeJoint extends Joint { List<Body> bodies; List<num> targetLengths; num targetVolume; List<Vector> normals; TimeStep step; num _impulse; World _world; List<DistanceJoint> distanceJoints; num frequencyHz; num dampingRatio; ConstantVolumeJoint(this._world, ConstantVolumeJointDef def) : super(def), _impulse = 0 { if (def.bodies.length <= 2) { throw new ArgumentError( "You cannot create a constant volume joint with less than three " "bodies."); } // Create a fixed size array with a capacity equal to the number of elements // in the growable array in the definition. bodies = new List.from(def.bodies); targetLengths = new List<num>(bodies.length); for (int i = 0; i < targetLengths.length; ++i) { final int next = (i == targetLengths.length - 1) ? 0 : i + 1; Vector temp = new Vector.copy(bodies[i].worldCenter); temp.subLocal(bodies[next].worldCenter); num dist = temp.length; targetLengths[i] = dist; } targetVolume = area; if (def.joints != null && def.joints.length != def.bodies.length) { throw new ArgumentError( "Incorrect joint definition. Joints have to correspond to " "the bodies"); } if (def.joints == null) { final djd = new DistanceJointDef(); distanceJoints = new List<DistanceJoint>(bodies.length); for (int i = 0; i < targetLengths.length; ++i) { final int next = (i == targetLengths.length - 1) ? 0 : i + 1; djd.frequencyHz = def.frequencyHz; djd.dampingRatio = def.dampingRatio; djd.initialize(bodies[i], bodies[next], bodies[i].worldCenter, bodies[next].worldCenter); distanceJoints[i] = _world.createJoint(djd); } } else { distanceJoints = new List<DistanceJoint>(def.joints.length); distanceJoints.setRange(0, def.joints.length, def.joints); } frequencyHz = def.frequencyHz; dampingRatio = def.dampingRatio; normals = new List<Vector>(bodies.length); for (int i = 0; i < normals.length; ++i) { normals[i] = new Vector(); } this.bodyA = bodies[0]; this.bodyB = bodies[1]; this.collideConnected = false; } void inflate(num factor) { targetVolume *= factor; } void destructor() { for (int i = 0; i < distanceJoints.length; ++i) { _world.destroyJoint(distanceJoints[i]); } } num get area { num result = 0.0; result += bodies[bodies.length - 1].worldCenter.x * bodies[0].worldCenter.y - bodies[0].worldCenter.x * bodies[bodies.length - 1].worldCenter.y; for (int i = 0; i < bodies.length - 1; ++i) { result += bodies[i].worldCenter.x * bodies[i + 1].worldCenter.y - bodies[i + 1].worldCenter.x * bodies[i].worldCenter.y; } result *= .5; return result; } /** Apply the position correction to the particles. */ bool constrainEdges(TimeStep argStep) { num perimeter = 0.0; for (int i = 0; i < bodies.length; ++i) { final int next = (i == bodies.length - 1) ? 0 : i + 1; num dx = bodies[next].worldCenter.x - bodies[i].worldCenter.x; num dy = bodies[next].worldCenter.y - bodies[i].worldCenter.y; num dist = Math.sqrt(dx * dx + dy * dy); if (dist < Settings.EPSILON) { dist = 1.0; } normals[i].x = dy / dist; normals[i].y = -dx / dist; perimeter += dist; } final delta = new Vector(); num deltaArea = targetVolume - area; num toExtrude = 0.5 * deltaArea / perimeter; // relaxationFactor bool done = true; for (int i = 0; i < bodies.length; ++i) { final int next = (i == bodies.length - 1) ? 0 : i + 1; delta.setCoords(toExtrude * (normals[i].x + normals[next].x), toExtrude * (normals[i].y + normals[next].y)); num norm = delta.length; if (norm > Settings.MAX_LINEAR_CORRECTION) { delta.mulLocal(Settings.MAX_LINEAR_CORRECTION / norm); } if (norm > Settings.LINEAR_SLOP) { done = false; } bodies[next].sweep.center.x += delta.x; bodies[next].sweep.center.y += delta.y; bodies[next].synchronizeTransform(); } return done; } void initVelocityConstraints(TimeStep argStep) { step = argStep; final d = new List<Vector>(bodies.length); for (int i = 0; i < bodies.length; i++) { d[i] = new Vector(); } for (int i = 0; i < bodies.length; ++i) { final int prev = (i == 0) ? bodies.length - 1 : i - 1; final int next = (i == bodies.length - 1) ? 0 : i + 1; d[i].setFrom(bodies[next].worldCenter); d[i].subLocal(bodies[prev].worldCenter); } if (step.warmStarting) { _impulse *= step.dtRatio; for (int i = 0; i < bodies.length; ++i) { bodies[i].linearVelocity.x += bodies[i].invMass * d[i].y * .5 * _impulse; bodies[i].linearVelocity.y += bodies[i].invMass * -d[i].x * .5 * _impulse; } } else { _impulse = 0.0; } } /** * Solves for the impact of this joint on the positions of the connected * bodies. Implements abstract method in [Joint]. */ bool solvePositionConstraints(num baumgarte) { return constrainEdges(step); } /** * Solves for the impact of this joint on the velocities of the connected * bodies. Implements abstract method in [Joint]. */ void solveVelocityConstraints(TimeStep argStep) { num crossMassSum = 0.0; num dotMassSum = 0.0; final d = new List<Vector>(bodies.length); for (int i = 0; i < bodies.length; i++) { d[i] = new Vector(); } for (int i = 0; i < bodies.length; ++i) { final int prev = (i == 0) ? bodies.length - 1 : i - 1; final int next = (i == bodies.length - 1) ? 0 : i + 1; d[i].setFrom(bodies[next].worldCenter); d[i].subLocal(bodies[prev].worldCenter); dotMassSum += (d[i].lengthSquared) / bodies[i].mass; crossMassSum += Vector.crossVectors(bodies[i].linearVelocity, d[i]); } num lambda = -2.0 * crossMassSum / dotMassSum; _impulse += lambda; for (int i = 0; i < bodies.length; ++i) { bodies[i].linearVelocity.x += bodies[i].invMass * d[i].y * .5 * lambda; bodies[i].linearVelocity.y += bodies[i].invMass * -d[i].x * .5 * lambda; } } void getAnchorA(Vector argOut) { throw new UnimplementedError(); } void getAnchorB(Vector argOut) { throw new UnimplementedError(); } void getReactionForce(num inv_dt, Vector argOut) { throw new UnimplementedError(); } num getReactionTorque(num inv_dt) { throw new UnimplementedError(); } }
Extends
Joint > ConstantVolumeJoint
Constructors
new ConstantVolumeJoint(World _world, ConstantVolumeJointDef def) #
ConstantVolumeJoint(this._world, ConstantVolumeJointDef def) : super(def), _impulse = 0 { if (def.bodies.length <= 2) { throw new ArgumentError( "You cannot create a constant volume joint with less than three " "bodies."); } // Create a fixed size array with a capacity equal to the number of elements // in the growable array in the definition. bodies = new List.from(def.bodies); targetLengths = new List<num>(bodies.length); for (int i = 0; i < targetLengths.length; ++i) { final int next = (i == targetLengths.length - 1) ? 0 : i + 1; Vector temp = new Vector.copy(bodies[i].worldCenter); temp.subLocal(bodies[next].worldCenter); num dist = temp.length; targetLengths[i] = dist; } targetVolume = area; if (def.joints != null && def.joints.length != def.bodies.length) { throw new ArgumentError( "Incorrect joint definition. Joints have to correspond to " "the bodies"); } if (def.joints == null) { final djd = new DistanceJointDef(); distanceJoints = new List<DistanceJoint>(bodies.length); for (int i = 0; i < targetLengths.length; ++i) { final int next = (i == targetLengths.length - 1) ? 0 : i + 1; djd.frequencyHz = def.frequencyHz; djd.dampingRatio = def.dampingRatio; djd.initialize(bodies[i], bodies[next], bodies[i].worldCenter, bodies[next].worldCenter); distanceJoints[i] = _world.createJoint(djd); } } else { distanceJoints = new List<DistanceJoint>(def.joints.length); distanceJoints.setRange(0, def.joints.length, def.joints); } frequencyHz = def.frequencyHz; dampingRatio = def.dampingRatio; normals = new List<Vector>(bodies.length); for (int i = 0; i < normals.length; ++i) { normals[i] = new Vector(); } this.bodyA = bodies[0]; this.bodyB = bodies[1]; this.collideConnected = false; }
Properties
final bool active #
inherited from Joint
Short-cut function to determine if either body is inactive.
bool get active => bodyA.active && bodyB.active;
final num area #
num get area { num result = 0.0; result += bodies[bodies.length - 1].worldCenter.x * bodies[0].worldCenter.y - bodies[0].worldCenter.x * bodies[bodies.length - 1].worldCenter.y; for (int i = 0; i < bodies.length - 1; ++i) { result += bodies[i].worldCenter.x * bodies[i + 1].worldCenter.y - bodies[i + 1].worldCenter.x * bodies[i].worldCenter.y; } result *= .5; return result; }
num dampingRatio #
num dampingRatio;
List<DistanceJoint> distanceJoints #
List<DistanceJoint> distanceJoints;
num frequencyHz #
num frequencyHz;
List<num> targetLengths #
List<num> targetLengths;
num targetVolume #
num targetVolume;
Methods
bool constrainEdges(TimeStep argStep) #
Apply the position correction to the particles.
bool constrainEdges(TimeStep argStep) { num perimeter = 0.0; for (int i = 0; i < bodies.length; ++i) { final int next = (i == bodies.length - 1) ? 0 : i + 1; num dx = bodies[next].worldCenter.x - bodies[i].worldCenter.x; num dy = bodies[next].worldCenter.y - bodies[i].worldCenter.y; num dist = Math.sqrt(dx * dx + dy * dy); if (dist < Settings.EPSILON) { dist = 1.0; } normals[i].x = dy / dist; normals[i].y = -dx / dist; perimeter += dist; } final delta = new Vector(); num deltaArea = targetVolume - area; num toExtrude = 0.5 * deltaArea / perimeter; // relaxationFactor bool done = true; for (int i = 0; i < bodies.length; ++i) { final int next = (i == bodies.length - 1) ? 0 : i + 1; delta.setCoords(toExtrude * (normals[i].x + normals[next].x), toExtrude * (normals[i].y + normals[next].y)); num norm = delta.length; if (norm > Settings.MAX_LINEAR_CORRECTION) { delta.mulLocal(Settings.MAX_LINEAR_CORRECTION / norm); } if (norm > Settings.LINEAR_SLOP) { done = false; } bodies[next].sweep.center.x += delta.x; bodies[next].sweep.center.y += delta.y; bodies[next].synchronizeTransform(); } return done; }
void destructor() #
Override to handle destruction of joint.
docs inherited from Joint
void destructor() { for (int i = 0; i < distanceJoints.length; ++i) { _world.destroyJoint(distanceJoints[i]); } }
void getAnchorA(Vector argOut) #
Get the anchor point on bodyA in world coordinates.
docs inherited from Joint
void getAnchorA(Vector argOut) { throw new UnimplementedError(); }
void getAnchorB(Vector argOut) #
Get the anchor point on bodyB in world coordinates.
docs inherited from Joint
void getAnchorB(Vector argOut) { throw new UnimplementedError(); }
void getReactionForce(num inv_dt, Vector argOut) #
Get the reaction force on body2 at the joint anchor in Newtons.
docs inherited from Joint
void getReactionForce(num inv_dt, Vector argOut) { throw new UnimplementedError(); }
num getReactionTorque(num inv_dt) #
Get the reaction torque on body2 in N*m.
docs inherited from Joint
num getReactionTorque(num inv_dt) { throw new UnimplementedError(); }
void inflate(num factor) #
void inflate(num factor) { targetVolume *= factor; }
void initVelocityConstraints(TimeStep argStep) #
void initVelocityConstraints(TimeStep argStep) { step = argStep; final d = new List<Vector>(bodies.length); for (int i = 0; i < bodies.length; i++) { d[i] = new Vector(); } for (int i = 0; i < bodies.length; ++i) { final int prev = (i == 0) ? bodies.length - 1 : i - 1; final int next = (i == bodies.length - 1) ? 0 : i + 1; d[i].setFrom(bodies[next].worldCenter); d[i].subLocal(bodies[prev].worldCenter); } if (step.warmStarting) { _impulse *= step.dtRatio; for (int i = 0; i < bodies.length; ++i) { bodies[i].linearVelocity.x += bodies[i].invMass * d[i].y * .5 * _impulse; bodies[i].linearVelocity.y += bodies[i].invMass * -d[i].x * .5 * _impulse; } } else { _impulse = 0.0; } }
bool solvePositionConstraints(num baumgarte) #
Solves for the impact of this joint on the positions of the connected bodies. Implements abstract method in Joint.
bool solvePositionConstraints(num baumgarte) { return constrainEdges(step); }
void solveVelocityConstraints(TimeStep argStep) #
Solves for the impact of this joint on the velocities of the connected bodies. Implements abstract method in Joint.
void solveVelocityConstraints(TimeStep argStep) { num crossMassSum = 0.0; num dotMassSum = 0.0; final d = new List<Vector>(bodies.length); for (int i = 0; i < bodies.length; i++) { d[i] = new Vector(); } for (int i = 0; i < bodies.length; ++i) { final int prev = (i == 0) ? bodies.length - 1 : i - 1; final int next = (i == bodies.length - 1) ? 0 : i + 1; d[i].setFrom(bodies[next].worldCenter); d[i].subLocal(bodies[prev].worldCenter); dotMassSum += (d[i].lengthSquared) / bodies[i].mass; crossMassSum += Vector.crossVectors(bodies[i].linearVelocity, d[i]); } num lambda = -2.0 * crossMassSum / dotMassSum; _impulse += lambda; for (int i = 0; i < bodies.length; ++i) { bodies[i].linearVelocity.x += bodies[i].invMass * d[i].y * .5 * lambda; bodies[i].linearVelocity.y += bodies[i].invMass * -d[i].x * .5 * lambda; } }