ConstantVolumeJoint class
This joint is commonly used to simulate a soft body with a constant volume. A constant volume joint can only be created with three or more bodies. The area within a grouping of constant volume joints is kept constant, such that the compression of one part of the area will be offset by the expansion of other parts.
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 IllegalArgumentException( "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 IllegalArgumentException( "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 NotImplementedException(); } void getAnchorB(Vector argOut) { throw new NotImplementedException(); } void getReactionForce(num inv_dt, Vector argOut) { throw new NotImplementedException(); } num getReactionTorque(num inv_dt) { throw new NotImplementedException(); } }
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 IllegalArgumentException( "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 IllegalArgumentException( "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 #
Short-cut function to determine if either body is inactive.
bool get active() { return 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;
final Type runtimeType #
A representation of the runtime type of the object.
external Type get runtimeType;
List<num> targetLengths #
List<num> targetLengths;
num targetVolume #
num targetVolume;
Operators
bool operator ==(other) #
The equality operator.
The default behavior for all Object
s is to return true if and
only if this
and
other are the same object.
If a subclass overrides the equality operator it should override
the hashCode
method as well to maintain consistency.
bool operator ==(other) => identical(this, other);
Methods
new ConstantVolumeJoint(World _world, ConstantVolumeJointDef def) #
ConstantVolumeJoint(this._world, ConstantVolumeJointDef def) : super(def), _impulse = 0 { if (def.bodies.length <= 2) { throw new IllegalArgumentException( "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 IllegalArgumentException( "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; }
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
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.
void getAnchorA(Vector argOut) { throw new NotImplementedException(); }
void getAnchorB(Vector argOut) #
Get the anchor point on bodyB in world coordinates.
void getAnchorB(Vector argOut) { throw new NotImplementedException(); }
void getReactionForce(num inv_dt, Vector argOut) #
Get the reaction force on body2 at the joint anchor in Newtons.
void getReactionForce(num inv_dt, Vector argOut) { throw new NotImplementedException(); }
num getReactionTorque(num inv_dt) #
Get the reaction torque on body2 in N*m.
num getReactionTorque(num inv_dt) { throw new NotImplementedException(); }
int hashCode() #
Get a hash code for this object.
All objects have hash codes. Hash codes are guaranteed to be the
same for objects that are equal when compared using the equality
operator ==
. Other than that there are no guarantees about
the hash codes. They will not be consistent between runs and
there are no distribution guarantees.
If a subclass overrides hashCode
it should override the
equality operator as well to maintain consistency.
external int hashCode();
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; } }
new Joint(JointDef def) #
Joint(JointDef def) : type = def.type, _prev = null, _next = null, bodyA = def.bodyA, bodyB = def.bodyB, collideConnected = def.collideConnected, islandFlag = false, userData = def.userData, localCenterA = new Vector(), localCenterB = new Vector(), edgeA = new JointEdge(), edgeB = new JointEdge() { }
factory Joint.create(World argWorld, JointDef def) #
factory Joint.create(World argWorld, JointDef def) { switch(def.type){ case JointType.MOUSE: throw new NotImplementedException(); // return new MouseJoint(def); case JointType.DISTANCE: return new DistanceJoint(def); case JointType.PRISMATIC: throw new NotImplementedException(); // return new PrismaticJoint(def); case JointType.REVOLUTE: return new RevoluteJoint(def); case JointType.WELD: throw new NotImplementedException(); //return new WeldJoint(def); case JointType.FRICTION: return new FrictionJoint(def); case JointType.LINE: throw new NotImplementedException(); //return new LineJoint(def); case JointType.GEAR: throw new NotImplementedException(); //return new GearJoint(def); case JointType.PULLEY: throw new NotImplementedException(); //return new PulleyJoint(def); case JointType.CONSTANT_VOLUME: return new ConstantVolumeJoint(argWorld, def); } return null; }
noSuchMethod(String name, List args) #
noSuchMethod
is invoked when users invoke a non-existant method
on an object. The name of the method and the arguments of the
invocation are passed to noSuchMethod
. If noSuchMethod
returns a value, that value becomes the result of the original
invocation.
The default behavior of noSuchMethod
is to throw a
noSuchMethodError
.
external Dynamic noSuchMethod(String name, List args);
const Object() #
Creates a new Object
instance.
Object
instances have no meaningful state, and are only useful
through their identity. An Object
instance is equal to itself
only.
const Object();
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; } }
String toString() #
Returns a string representation of this object.
external String toString();