anchor.diff:    }
   }
   
  +
  +/**
  + * Keeps the particle inside the given sphere
  + */
  +class Sphere extends Constraint<Particle3>
  +{
  +  // settings
  +  Vector3 position = new Vector3.zero();
  +  double radius;
  +  bool isBouncy;
  +
  +  // internal
  +  double _radiusSq;
  +
  +  Sphere([this.radius = 100.0, this.isBouncy = true]);
  +
  +  prepare() => _radiusSq = radius * radius;
  +
  +  apply(Particle3 particle) {
  +    Vector3 delta = particle.position - position;
  +    double distSq = delta.length2;
  +
  +    if(distSq >= _radiusSq) {
  +      Vector3 constrained = position + delta.normalize().scale(radius);
  +      if(isBouncy) {
  +        particle.position.setFrom(constrained); // bouncy
  +      } else { 
  +        particle.setPosition(constrained); // no-bounce
  +      }
  +    }
  +  }
  +}
  
positive.code: 
  
  /**
   * Wraps the particle inside the given 3D box 
   */
  class BoxWrap extends Constraint<Particle3> 
  {
  
negative.code: part of physics;
  
  /**
   * Keeps the particle inside the given box shape
   */
  class Box extends Constraint<Particle3> 
  {
  