Dart Documentationbox2d_htmlConstantVolumeJoint

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 #

inherited from Joint

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;
}

List<Body> bodies #

List<Body> bodies;

Body bodyA #

inherited from Joint
Body bodyA;

Body bodyB #

inherited from Joint
Body bodyB;

bool collideConnected #

inherited from Joint
bool collideConnected;

num dampingRatio #

num dampingRatio;

List<DistanceJoint> distanceJoints #

List<DistanceJoint> distanceJoints;

JointEdge edgeA #

inherited from Joint
JointEdge edgeA;

JointEdge edgeB #

inherited from Joint
JointEdge edgeB;

num frequencyHz #

num frequencyHz;

num invIA #

inherited from Joint
num invIA;

num invIB #

inherited from Joint
num invIB;

num invMassA #

inherited from Joint
num invMassA;

num invMassB #

inherited from Joint
num invMassB;

bool islandFlag #

inherited from Joint
bool islandFlag;

final Vector localCenterA #

inherited from Joint
final Vector localCenterA;

final Vector localCenterB #

inherited from Joint
final Vector localCenterB;

List<Vector> normals #

List<Vector> normals;

final Type runtimeType #

inherited from Object

A representation of the runtime type of the object.

external Type get runtimeType;

TimeStep step #

TimeStep step;

List<num> targetLengths #

List<num> targetLengths;

num targetVolume #

num targetVolume;

int type #

inherited from Joint
int type;

Object userData #

inherited from Joint
Object userData;

Operators

bool operator ==(other) #

inherited from Object

The equality operator.

The default behavior for all Objects 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

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 NotImplementedException();
}

void getAnchorB(Vector argOut) #

Get the anchor point on bodyB in world coordinates.

docs inherited from Joint
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.

docs inherited from Joint
void getReactionForce(num inv_dt, Vector argOut) {
  throw new NotImplementedException();
}

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 NotImplementedException();
}

int hashCode() #

inherited from Object

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) #

inherited from Joint
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) #

inherited from Joint
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) #

inherited from Object

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() #

inherited from 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() #

inherited from Object

Returns a string representation of this object.

external String toString();