The Nature Of Code   Chp05 - Physics Libraries

Example 5.1
Box2D Exercise

Click and drag to add box   p5.js

let boxes = []; //list to store all the box objects

function setup() {
  createCanvas(560, 390);
  let text = createP("Click and drag to add box");
  text.position(15, 5);
}

function draw() {
  background(220);

  //when the mouse is pressed, add a new box object
  if (mouseIsPressed) {
    let b = new Box(mouseX, mouseY);
    boxes.push(b);
  }
  //display all the box objects
  for (let i = 0; i < boxes.length; i++) {
    boxes[i].display();
  }
}

class Box {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.w = 16;
    this.h = 16;
  }

  display() {
    rectMode(CENTER);
    fill(63,63,147);
    stroke(50);
    strokeWeight(2);
    rect(this.x, this.y, this.w, this.h);
  }
}
							

Example 5.1
Box2D Exercise Solved

Click and drag to add box   p5.js

//reference to box2d world
let world;

//list to store all the box objects
let boxes = [];

function setup() {
  createCanvas(560, 390);
    let text = createP("Click and drag to add box");
  text.position(10, 5);
  
  //initialize box2d physics and create the world
  world = createWorld(new box2d.b2Vec2(0, 0));

}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  //when the mouse is pressed, add a new box object
  if (mouseIsPressed) {
    let b = new Box(mouseX, mouseY);
    boxes.push(b);
  }
  //display all the box objects
  for (let i = 0; i < boxes.length; i++) {
    boxes[i].display();
  }
}
class Box {
  constructor(x, y) {
    this.w = 16;
    this.h = 16;

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(scaleToWorld(this.w / 2), scaleToWorld(this.h / 2));

    // Some physics
    fd.density = 1.0;
    fd.friction = 0.5; //set physics parameter
    fd.restitution = 0.2;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);
  }

  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();
        
    //drawing object using rect()
    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,147);
    stroke(50);
    strokeWeight(2);
    rect(0, 0, this.w, this.h);
    pop();
  }
}
                            

Example 5.2
Falling Boxes Hitting Boundaries

p5.js

//reference to box2d world
let world;
//list to track fixed objects
let boundaries = [];
//list to store all the box objects
let boxes = [];

function setup() {
  createCanvas(560, 390);

  //initialize box2d physics and create the world
  world = createWorld();

  //add fixed boundaries
  boundaries.push(new Boundary(width / 4, height - 5, width / 2 - 50, 10));
  boundaries.push(new Boundary(3 * width / 4, height - 50, width / 2 - 50, 10));

  let b = new Box(width / 2, 30);
  boxes.push(b);
}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  //boxes fall from the top every so often
  if (random(1) < 0.2) {
    let b = new Box(width / 2, 30);
    boxes.push(b);
  }

  //display all the boundaries
  for (let i = 0; i < boundaries.length; i++) {
    boundaries[i].display();
  }

  //display all the box objects
  for (let i = boxes.length - 1; i >= 0; i--) {
    boxes[i].display();
    if (boxes[i].done()) {
      boxes.splice(i, 1);
    }
  }
}
class Box {
  constructor(x, y) {
    this.w = random(4, 40);
    this.h = random(4, 40);

    //build Box2D body and shape
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(scaleToWorld(this.w / 2), scaleToWorld(this.h / 2));

    //some physics
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion
  done() {
    //find the screen position of the particle
    let pos = scaleToPixels(this.body.GetPosition());
    //check if it is off the bottom of the screen
    if (pos.y > height + this.w * this.h) {
      this.killBody();
      return true;
    }
    return false;
  }

  //draw the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    //drawing object using rect()
    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,147);
    stroke(50);
    strokeWeight(2);
    rect(0, 0, this.w, this.h);
    pop();
  }
}
//boundary is simple rectangle with x,y,width,and height
class Boundary {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;

    let fd = new box2d.b2FixtureDef();
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    let bd = new box2d.b2BodyDef();

    bd.type = box2d.b2BodyType.b2_staticBody;
    bd.position.x = scaleToWorld(this.x);
    bd.position.y = scaleToWorld(this.y);
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(this.w / (scaleFactor * 2), this.h / (scaleFactor * 2));
    this.body = world.CreateBody(bd).CreateFixture(fd);
  }

  //draw the boundary
  display() {
    fill(100);
    stroke(50);
    rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);
  }
}
                            

Example 5.3
ChainShape With Three Hard-Coded Vertices

p5.js

//reference to box2d world
let world;
//list of particles
let particles = [];
//an object to store information about the uneven surface
let surface;

function setup() {
  createCanvas(560, 390);

  //initialize box2d physics and create the world
  world = createWorld();

  //create the surface
  surface = new Surface();
}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  //particles fall from the top every so often
  if (random(1) < 0.5) {
    let sz = random(4, 8);
    particles.push(new Particle(width / 2, 10, sz));
  }

  //draw the surface
  surface.display();

  //display all the particles
  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].display();
    if (particles[i].done()) {
      particles.splice(i, 1);
    }
  }
}
//a rectangular box
class Particle {
  constructor(x, y, r) {
    this.r = r;

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2CircleShape();
    fd.shape.m_radius = scaleToWorld(this.r);

    //some physics
    fd.density = 1.0;
    fd.friction = 0.1;
    fd.restitution = 0.3;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion
  done() {
    //find the screen position of the particle
    let transform = this.body.GetTransform();
    let pos = scaleToPixels(transform.position);
    //check if it is off the bottom of the screen
    if (pos.y > height + this.r * 2) {
      this.killBody();
      return true;
    }
    return false;
  }

  //draw the Particle
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    //drawing object using ellipse()
    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,147);
    stroke(150);
    strokeWeight(2);
    ellipse(0, 0, this.r * 2, this.r * 2);
    //add a line so we can see the rotation
    line(0, 0, this.r, 0);
    pop();
  }
}
//an uneven surface boundary and a fixed boundary class
class Surface {
  constructor() {
    this.surface = [];
    //keep track of the screen coordinates of the chain
    this.surface.push(new box2d.b2Vec2(0, height / 2));
    this.surface.push(new box2d.b2Vec2(width / 2, height / 2 + 100));
    this.surface.push(new box2d.b2Vec2(width, height / 2));

    for (let i = 0; i < this.surface.length; i++) {
      this.surface[i] = scaleToWorld(this.surface[i]);
    }

    //this is what box2d uses to put the surface in its world
    let chain = new box2d.b2ChainShape();
    chain.CreateChain(this.surface, this.surface.length);

    //a body to attach shape
    let bd = new box2d.b2BodyDef();
    this.body = world.CreateBody(bd);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = chain;

    //some physics
    fd.density = 1.0;
    fd.friction = 0.1;
    fd.restitution = 0.3;

    //attach the fixture
    this.body.CreateFixture(fd);
  }

  //a function to just draw the edge chain as a series of vertex points
  display() {
    strokeWeight(1);
    stroke(50);
    fill(100);
    beginShape();
    for (let i = 0; i < this.surface.length; i++) {
      let v = scaleToPixels(this.surface[i]);
      vertex(v.x, v.y);
    }
    vertex(width, height);
    vertex(0, height);
    endShape(CLOSE);
  }
}
                            

Example 5.4
Polygon Shapes

Click to add polygon shapes   p5.js

//a reference to our box2d world
let world;
//list of boundaries to track fixed objects
let boundaries = [];
//list of rectangles
let polygons = [];

function setup() {
  createCanvas(560, 390);
  let text = createP("Click and drag to add box");
  text.position(15, 5);

  //initialize box2d physics and create the world
  world = createWorld();

  //add a bunch of fixed boundaries
  boundaries.push(new Boundary(width / 4, height - 5, width / 2 - 50, 10, 0));
  boundaries.push(new Boundary(3 * width / 4, height - 50, width / 2 - 50, 10, 0));
  boundaries.push(new Boundary(width - 5, height / 2, 10, height, 0));
  boundaries.push(new Boundary(5, height / 2, 10, height, 0));
}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  //display all the boundaries
  for (let i = 0; i < boundaries.length; i++) {
    boundaries[i].display();
  }

  //display all the boxes
  for (let i = polygons.length - 1; i >= 0; i--) {
    polygons[i].display();
    if (polygons[i].done()) {
      polygons.splice(i, 1);
    }
  }
}

function mousePressed() {
  let cs = new CustomShape(mouseX, mouseY);
  polygons.push(cs);
}
// a boundary is a simple rectangle with x,y,width,and height
class Boundary {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;

    let fd = new box2d.b2FixtureDef();
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    let bd = new box2d.b2BodyDef();

    bd.type = box2d.b2BodyType.b2_staticBody;
    bd.position = scaleToWorld(this.x, this.y);
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(this.w / (scaleFactor * 2), this.h / (scaleFactor * 2));
    this.body = world.CreateBody(bd).CreateFixture(fd);
  }

  //draw the boundary, if it were at an angle we'd have to do something fancier
  display() {
    fill(100);
    stroke(50);
    rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);
  }
}
// cusomized rectangular box
class CustomShape {
  constructor(x, y) {

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();

    let vertices = [];
    vertices[3] = scaleToWorld(-15, 25);
    vertices[2] = scaleToWorld(15, 0);
    vertices[1] = scaleToWorld(20, -15);
    vertices[0] = scaleToWorld(-10, -10);

    //fixture holds shape
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsArray(vertices, vertices.length);

    //some physics
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion
  done() {
    //find the screen position of the particle
    let pos = scaleToPixels(this.body.GetPosition());
    //check if it is off the bottom of the screen
    if (pos.y > height + this.w * this.h) {
      this.killBody();
      return true;
    }
    return false;
  }

  //drawing the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    //draw it
    let f = this.body.GetFixtureList();
    let ps = f.GetShape();

    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,147);
    stroke(50);
    strokeWeight(2);
    ellipse(0, 0, 20, 20);
    beginShape();
    //for every vertex, convert to pixel vector
    for (let i = 0; i < ps.m_count; i++) {
      let v = scaleToPixels(ps.m_vertices[i]);
      vertex(v.x, v.y);
    }
    endShape(CLOSE);
    pop();
  }
}
                            

Example 5.5
Multi Shapes

Click to add shapes   p5.js

//a reference to box2d world
let world;

//a list use to track fixed objects
let boundaries = [];
//a list for all of rectangles
let pops = [];

function setup() {
  createCanvas(560, 390);
  let text = createP("Click to add shapes");
  text.position(15, 5);

  //initialize box2d physics and create the world
  world = createWorld();

  //add a bunch of fixed boundaries
  boundaries.push(new Boundary(width / 4, height - 5, width / 2 - 50, 10, 0));
  boundaries.push(new Boundary(3 * width / 4, height - 50, width / 2 - 50, 10, 0));
  boundaries.push(new Boundary(width - 5, height / 2, 10, height, 0));
  boundaries.push(new Boundary(5, height / 2, 10, height, 0));
}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  //display all the boundaries
  for (let i = 0; i < boundaries.length; i++) {
    boundaries[i].display();
  }

  //display all the boxes
  for (let i = pops.length - 1; i >= 0; i--) {
    pops[i].display();
    if (pops[i].done()) {
      pops.splice(i, 1);
    }
  }
}

function mousePressed() {
  let p = new Lollipop(mouseX, mouseY);
  pops.push(p);
}
//a boundary is a simple rectangle with x,y,width,and height
class Boundary {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;

    let fd = new box2d.b2FixtureDef();
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    let bd = new box2d.b2BodyDef();

    bd.type = box2d.b2BodyType.b2_staticBody;
    bd.position = scaleToWorld(this.x, this.y);
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(this.w / (scaleFactor * 2), this.h / (scaleFactor * 2));
    this.body = world.CreateBody(bd).CreateFixture(fd);
  }

  //draw the boundary, if it were at an angle we'd have to do something fancier
  display() {
    fill(100);
    stroke(50);
    rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);
  }
}
class Lollipop {
  constructor(x, y) {
    this.w = 8;
    this.h = 24;
    this.r = 8;

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define fixture #1
    let fd1 = new box2d.b2FixtureDef();
    //fixture holds shape
    fd1.shape = new box2d.b2PolygonShape();
    fd1.shape.SetAsBox(scaleToWorld(this.w / 2), scaleToWorld(this.h / 2));
    fd1.density = 1.0;
    fd1.friction = 0.5;
    fd1.restitution = 0.2;

    //define fixture #2
    let fd2 = new box2d.b2FixtureDef();
    fd2.shape = new box2d.b2CircleShape();
    fd2.shape.m_radius = scaleToWorld(this.r);
    let offset = scaleToWorld(new box2d.b2Vec2(0, -this.h / 2));
    fd2.shape.m_p = new box2d.b2Vec2(offset.x, offset.y);
    fd2.density = 1.0;
    fd2.friction = 0.5;
    fd2.restitution = 0.2;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd1);
    this.body.CreateFixture(fd2);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion
  done() {
    //find the screen position of the particle
    let pos = scaleToPixels(this.body.GetPosition());
    //check if it is off the bottom of the screen
    if (pos.y > height + this.w * this.h) {
      this.killBody();
      return true;
    }
    return false;
  }

  //draw the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    //draw it
    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,137);
    stroke(50);
    strokeWeight(2);

    rect(0, 0, this.w, this.h);
    ellipse(0, -this.h / 2, this.r * 2, this.r * 2);
    pop();
  }
}
                            

Example 5.6
Distance Joint

Click to add shapes   p5.js

//reference to our box2d world
let world;
//list use to track fixed objects
let boundaries = [];
//list for all rectangles
let pairs = [];

function setup() {
  createCanvas(560, 390);
  let text = createP("Click to add shapes");
  text.position(10, 5);

  //initialize box2d physics and create the world
  world = createWorld();

  //add a bunch of fixed boundaries
  boundaries.push(new Boundary(width / 4, height - 5, width / 2 - 50, 10));
  boundaries.push(new Boundary(3 * width / 4, height - 50, width / 2 - 50, 10));

}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  //display all the boundaries
  for (let i = 0; i < boundaries.length; i++) {
    boundaries[i].display();
  }

  //display all the pairs
  for (let i = pairs.length - 1; i >= 0; i--) {
    pairs[i].display();
    if (pairs[i].done()) {
      pairs.splice(i, 1);
    }
  }
}

function mousePressed() {
  let p = new Pair(mouseX, mouseY);
  pairs.push(p);
}
//a boundary is a simple rectangle with x,y,width,and height
class Boundary {
  constructor(x, y, w, h) {

    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;

    let fd = new box2d.b2FixtureDef();
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    let bd = new box2d.b2BodyDef();

    bd.type = box2d.b2BodyType.b2_staticBody;
    bd.position.x = scaleToWorld(this.x);
    bd.position.y = scaleToWorld(this.y);
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(this.w / (scaleFactor * 2), this.h / (scaleFactor * 2));
    this.body = world.CreateBody(bd).CreateFixture(fd);
  }

  //draw the boundary, if it were at an angle we'd have to do something fancier
  display() {
    fill(100);
    stroke(50);
    rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);
  }
}
class Particle {
  constructor(x, y) {
    this.r = 8;

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2CircleShape();
    fd.shape.m_radius = scaleToWorld(this.r);

    //some physics
    fd.density = 1.0;
    fd.friction = 0.1;
    fd.restitution = 0.3;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion
  done() {
    //find the screen position of the particle
    let pos = scaleToPixels(this.body.GetPosition());
    //check is it off the bottom of the screen
    if (pos.y > height + this.r * 2) {
      this.killBody();
      return true;
    }
    return false;
  }

  //drawing the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,147);
    stroke(150);
    strokeWeight(2);
    ellipse(0, 0, this.r * 2, this.r * 2);
    //add a line so we can see the rotation
    line(0, 0, this.r, 0);
    pop();
  }
}
//two particles connected with distance joints
class Pair {
  constructor(x, y) {
    this.len = 32;

    this.p1 = new Particle(x, y);
    this.p2 = new Particle(x + random(-1, 1), y + random(-1, 1));

    let djd = new box2d.b2DistanceJointDef();
    //connection between previous particle and this one
    djd.bodyA = this.p1.body;
    djd.bodyB = this.p2.body;
    //Equilibrium length
    djd.length = scaleToWorld(this.len);

    //these properties affect how springy the joint is
    djd.frequencyHz = 3; //try a value less than 5 (0 for no elasticity)
    djd.dampingRatio = 0.1; //ranges between 0 and 1 (1 for no springiness)

    //make the joint (we aren't storing a reference to the joint ourselves anywhere)
    //we might need to someday, but for now it's ok
    let dj = world.CreateJoint(djd);
  }

  done() {
    return this.p1.done() && this.p2.done();
  }

  display() {
    //get the body's position
    let pos1 = scaleToPixels(this.p1.body.GetPosition());
    let pos2 = scaleToPixels(this.p2.body.GetPosition());

    stroke(150);
    strokeWeight(2);
    line(pos1.x, pos1.y, pos2.x, pos2.y);

    this.p1.display();
    this.p2.display();
  }
}
                            

Example 5.7
Revolute Joint

Click mouse to toggle motor   p5.js

//reference to box2d world
let world;
//list for particles
let particles = [];
//an object to describe a Windmill (two bodies and one joint)
let windmill;

function setup() {
  createCanvas(560, 390);
  let text = createP("Click mouse to toggle motor");
  text.position(10, 5);

  //initialize box2d physics and create the world
  world = createWorld();

  //make the windmill at an x,y position
  windmill = new Windmill(width / 2, 175);
}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  if (random(1) < 0.1) {
    let sz = random(4, 8);
    particles.push(new Particle(random(width / 2 - 100, width / 2 + 100), -20, sz));
  }


  //display all the pairs
  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].display();
    if (particles[i].done()) {
      particles.splice(i, 1);
    }
  }

  //draw the windmill
  windmill.display();

  let status = "OFF";
  if (windmill.motorOn()) status = "ON";

}

function mousePressed() {
  windmill.toggleMotor();

  // let status = "OFF";
  // if (windmill.motorOn()) status = "ON";
  // txt.html("Click mouse to toggle motor.\nMotor: " + status);
}
//two particles connected with distance joints
class Windmill {
  constructor(x, y) {
    this.len = 32;

    this.box1 = new Box(x, y - 20, 120, 10, false);
    this.box2 = new Box(x, y, 10, 40, true);

    //define joint as between two bodies
    let rjd = new box2d.b2RevoluteJointDef();

    rjd.Initialize(this.box1.body, this.box2.body, this.box1.body.GetWorldCenter());

    //turning on a motor (optional)
    rjd.motorSpeed = PI * 2; //how fast
    rjd.maxMotorTorque = 1000.0; //how powerful
    rjd.enableMotor = false; //is it on
    
    //create the joint
    this.joint = world.CreateJoint(rjd);
  }

  display() {
    this.box2.display();
    this.box1.display();

    //draw anchor just for debug
    let anchor = scaleToPixels(this.box1.body.GetWorldCenter());
    fill(0);
    noStroke();
    ellipse(anchor.x, anchor.y, 8, 8);
  }

  //turn the motor on or off
  toggleMotor() {
    this.joint.EnableMotor(!this.joint.IsMotorEnabled());
  }

  motorOn() {
    return this.joint.IsMotorEnabled();
  }
}
//a circular particle
class Particle {
  constructor(x, y) {
    this.r = 8;

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2CircleShape();
    fd.shape.m_radius = scaleToWorld(this.r);

    //some physics
    fd.density = 1.0;
    fd.friction = 0.1;
    fd.restitution = 0.3;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion?
  done() {
    //find the screen position of the particle
    let pos = scaleToPixels(this.body.GetPosition());
    //check is it off the bottom of the screen
    if (pos.y > height + this.r * 2) {
      this.killBody();
      return true;
    }
    return false;
  }

  //drawing the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,147);
    stroke(200);
    strokeWeight(2);
    ellipse(0, 0, this.r * 3, this.r * 3);
    //add a line so we can see the rotation
    line(0, 0, this.r, 0);
    pop();
  }
}
class Box {
  constructor(x, y, w, h, lock) {
    this.w = w;
    this.h = h;

    //define a body
    let bd = new box2d.b2BodyDef();
    if (lock) bd.type = box2d.b2BodyType.b2_staticBody;
    else bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(scaleToWorld(this.w / 2), scaleToWorld(this.h / 2));

    //some physics
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion?
  done() {
    //find the screen position of the particle
    let pos = scaleToPixels(this.body.GetPosition());
    //check iss it off the bottom of the screen?
    if (pos.y > height + this.w * this.h) {
      this.killBody();
      return true;
    }
    return false;
  }

  //drawing the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(127);
    stroke(200);
    strokeWeight(2);
    rect(0, 0, this.w, this.h);
    pop();
  }
}
                            

Example 5.8
Mouse Joint

Drag and throw the box   p5.js

//reference to box2d world
let world;
//list use to track fixed objects
let boundaries = [];
//a single box
let box;
//spring that will attach to the box from the mouse
let spring;


function setup() {
  createCanvas(560, 390);
  let text = createP("Drag and throw the box");
  text.position(15, 5);

  //initialize box2d physics and create the world
  world = createWorld();

  //make the box
  box = new Box(width / 2, height / 2);

  //make the spring (it doesn't really get initialized until the mouse is clicked)
  spring = new Spring();

  //add a bunch of fixed boundaries
  boundaries.push(new Boundary(width / 2, height - 5, width, 10, 0));
  boundaries.push(new Boundary(width / 2, 5, width, 10, 0));
  boundaries.push(new Boundary(width - 5, height / 2, 10, height, 0));
  boundaries.push(new Boundary(5, height / 2, 10, height, 0));

}

function draw() {
  background(220);

  //we must always step through time
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  //always alert the spring to the new mouse position
  spring.update(mouseX, mouseY);

  //draw the boundaries
  for (let i = 0; i < boundaries.length; i++) {
    boundaries[i].display();
  }

  //draw the box
  box.display();
  //draw the spring (it only appears when active)
  spring.display();
}

//when the mouse is released, we done with the spring
function mouseReleased() {
  spring.destroy();
}

//when the mouse is pressed
function mousePressed() {
  //check to see if the mouse was clicked on the box
  if (box.contains(mouseX, mouseY)) {
    //if so, bind the mouse position to the box with a spring
    spring.bind(mouseX, mouseY, box);
  }
}
//class to describe the spring joint (displayed as a line)
class Spring {
  constructor(x, y) {
    //at first it doesn't exist
    this.mouseJoint = null;
  }

  //if it exists we set its target to the mouse location
  update(x, y) {
    if (this.mouseJoint !== null) {
      //always convert to world coordinates!
      let mouseWorld = scaleToWorld(x, y);
      this.mouseJoint.SetTarget(mouseWorld);
    }
  }

  display() {
    if (this.mouseJoint !== null) {

      let posA = this.mouseJoint.GetAnchorA();
      let posB = this.mouseJoint.GetAnchorB();

      //get the two anchor points
      let v1 = scaleToPixels(posA.x, posA.y);
      let v2 = scaleToPixels(posB.x, posB.y);
      //draw a line
      stroke(150);
      strokeWeight(2);

      line(v1.x, v1.y, v2.x, v2.y);
    }
  }

  //key function
  //attach the spring to an x,y location and box object's location
  bind(x, y, box) {
    //define the joint
    let md = new box2d.b2MouseJointDef();
    //body A is just a fake ground body for simplicity (there isn't anything at the mouse)
    md.bodyA = world.CreateBody(new box2d.b2BodyDef()); //world.GetGroundBody();
    //body B is the box's boxy
    md.bodyB = box.body;
    //get the mouse location in world coordinates
    let mp = scaleToWorld(x, y);
    //the target
    //println(mp.x + " " + mp.y);
    md.target = mp;
    //println(md.target.x + " " + md.target.y);

    //some stuff about how strong and bouncy the spring should be
    md.maxForce = 1000.0 * box.body.m_mass;
    md.frequencyHz = 5.0;
    md.dampingRatio = 0.9;

    //make the joint
    this.mouseJoint = world.CreateJoint(md);
  }

  destroy() {
    //we can get rid of the joint when the mouse is released
    if (this.mouseJoint !== null) {
      world.DestroyJoint(this.mouseJoint);
      this.mouseJoint = null;
    }
  }
}
//a boundary is a simple rectangle with x,y,width,and height
class Boundary {
  constructor(x, y, w, h) {

    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;

    let fd = new box2d.b2FixtureDef();
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    let bd = new box2d.b2BodyDef();

    bd.type = box2d.b2BodyType.b2_staticBody;
    bd.position.x = scaleToWorld(this.x);
    bd.position.y = scaleToWorld(this.y);
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(this.w / (scaleFactor * 2), this.h / (scaleFactor * 2));
    this.body = world.CreateBody(bd).CreateFixture(fd);
  }

  //draw the boundary, if it were at an angle we'd have to do something fancier
  display() {
    fill(100);
    stroke(50);
    rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);
  }
}
class Box {
  constructor(x, y) {
    this.w = 24;
    this.h = 24;

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(scaleToWorld(this.w / 2), scaleToWorld(this.h / 2));

    //some physics
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the shape to the body with the fixture
    this.body.CreateFixture(fd);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));
  }

  contains(x, y) {
    let worldPoint = scaleToWorld(x, y);
    let f = this.body.GetFixtureList();
    let inside = f.TestPoint(worldPoint);
    return inside;
  }

  //drawing the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(63,63,147);
    stroke(50);
    strokeWeight(2);
    rect(0, 0, this.w, this.h);
    pop();
  }
}
                            

Example 5.9
CollisionListening

p5.js

//a reference to box2d world
let world;
//list for particles
let particles = [];
let wall;

function setup() {
  createCanvas(560, 390);

  //initialize box2d physics and create the world
  world = createWorld();

  world.SetContactListener(new CustomListener());

  wall = new Boundary(width / 2, height - 5, width, 10);
}

function draw() {
  background(220);

  //we must always step through time!
  let timeStep = 1.0 / 30;
  //second and third arguments are velocity and position iterations
  world.Step(timeStep, 10, 10);

  if (random(1) < 0.1) {
    let sz = random(4, 8);
    particles.push(new Particle(random(width), 20, sz));
  }


  //look at all particles
  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].display();
    //particles that leave the screen, we delete them
    //they have to be deleted from both the box2d world and our list
    if (particles[i].done()) {
      particles.splice(i, 1);
    }
  }
  wall.display();
}
//a circular particle
class Particle {
  constructor(x, y, r) {
    this.r = r;

    this.col = color(63,63,147);

    //define a body
    let bd = new box2d.b2BodyDef();
    bd.type = box2d.b2BodyType.b2_dynamicBody;
    bd.position = scaleToWorld(x, y);

    //define a fixture
    let fd = new box2d.b2FixtureDef();
    //fixture holds shape
    fd.shape = new box2d.b2CircleShape();
    fd.shape.m_radius = scaleToWorld(this.r);

    //some physics
    fd.density = 1.0;
    fd.friction = 0.1;
    fd.restitution = 0.3;

    //create the body
    this.body = world.CreateBody(bd);
    //attach the fixture
    this.body.CreateFixture(fd);

    //some additional stuff
    this.body.SetLinearVelocity(new box2d.b2Vec2(random(-5, 5), random(2, 5)));
    this.body.SetAngularVelocity(random(-5, 5));

    this.body.SetUserData(this);
  }

  //change color when hit
  change() {
    this.col = color(255, 204, 0);
  }

  //this function removes the particle from the box2d world
  killBody() {
    world.DestroyBody(this.body);
  }

  //check if the particle ready for deletion?
  done() {
    //find the screen position of the particle
    let pos = scaleToPixels(this.body.GetPosition());
    //check is it off the bottom of the screen
    if (pos.y > height + this.r * 2) {
      this.killBody();
      return true;
    }
    return false;
  }

  //drawing the box
  display() {
    //get the body's position
    let pos = scaleToPixels(this.body.GetPosition());
    //get its angle of rotation
    let a = this.body.GetAngleRadians();

    //draw it!
    rectMode(CENTER);
    push();
    translate(pos.x, pos.y);
    rotate(a);
    fill(this.col);
    stroke(200);
    strokeWeight(2);
    ellipse(0, 0, this.r * 3, this.r * 3);
    //add a line so we can see the rotation
    line(0, 0, this.r, 0);
    pop();
  }
}
//a boundary is a simple rectangle with x,y,width,and height
class Boundary {
  constructor(x, y, w, h) {

    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;

    let fd = new box2d.b2FixtureDef();
    fd.density = 1.0;
    fd.friction = 0.5;
    fd.restitution = 0.2;

    let bd = new box2d.b2BodyDef();

    bd.type = box2d.b2BodyType.b2_staticBody;
    bd.position.x = scaleToWorld(this.x);
    bd.position.y = scaleToWorld(this.y);
    fd.shape = new box2d.b2PolygonShape();
    fd.shape.SetAsBox(this.w / (scaleFactor * 2), this.h / (scaleFactor * 2));
    this.body = world.CreateBody(bd).CreateFixture(fd);
  }

  //draw the boundary, if it were at an angle we'd have to do something fancier
  display() {
    fill(100);
    stroke(50);
    rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);
  }
}
//ContactListener to listen for collisions
class CustomListener {

  //collision event functions!
  BeginContact(contact) {
    //get both fixtures
    let f1 = contact.GetFixtureA();
    let f2 = contact.GetFixtureB();
    //get both bodies
    let b1 = f1.GetBody();
    let b2 = f2.GetBody();

    //get our objects that reference these bodies
    let o1 = b1.GetUserData();
    let o2 = b2.GetUserData();

    if (o1 instanceof Particle && o2 instanceof Particle) {
      o1.change();
      o2.change();
    }
  }

  //objects stop touching each other
  EndContact(contact) {};

  PreSolve(contact, manifold) {};

  PostSolve(contact, manifold) {};
}
                            

Example 5.10
Simple Spring With Toxiclibs

p5.js

//reference to physics world
let physics;
let p1;
let p2;

function setup() {
  createCanvas(560,390);
    let text = createP("Drag and throw the ball");
  text.position(10, 5);
  
  //initialize the physics
  physics=new VerletPhysics2D();
  physics.addBehavior(new GravityBehavior(new Vec2D(0,0.5)));

  //set the world's bounding box
  physics.setWorldBounds(new Rect(0,0,width,height));
  
  //make two particles
  p1 = new Particle(new Vec2D(width/2,20));
  p2 = new Particle(new Vec2D(width/2+160,20));
  //lock one in place
  p1.lock();

  //make a spring connecting both Particles
  let spring = new VerletSpring2D(p1,p2,160,0.01);

  //anything we make, we have to add into the physics world
  physics.addParticle(p1);
  physics.addParticle(p2);
  physics.addSpring(spring);
}

function draw() {
    //update the physics world
  physics.update();

  background(220);

  //draw a line between the particles
  stroke(50);
  strokeWeight(2);
  line(p1.x,p1.y,p2.x,p2.y);

  //display the particles
  p1.display();
  p2.display();

  //move the second one according to the mouse
  if (mouseIsPressed) {
    p2.lock();
    p2.x = mouseX;
    p2.y = mouseY;
    p2.unlock();
  } 
}
//child class constructor
class Particle extends VerletParticle2D {
  constructor(x,y) {
    super(x,y); 
  }

  display(){
    fill(63,63,147);
    stroke(50);
    strokeWeight(2);
    ellipse(this.x,this.y,32,32);
  }
}
                            

The Nature Of Code

  • Introduction
  • Vectors
  • Forces
  • Oscillation
  • Particle Systems
  • Physics Libraries
  • Autonomous Agents
  • speng2@wpi.edu  (Liz Peng)