Fisica / JBox2D physics engine

From Processing

Jump to: navigation, search

This tutorial is about fisica, a wrapper around JBox2D physics engine. This library makes it much easier to create physical models by exposing an object oriented API. The tutorial is based on version 12 of fisica for Processing 2.

Contents

[hide]

Physics libraries for Processing

There are different physics libraries for Processing:

  • Traer Physics: Physics simulator for particles
  • toxiclibs: Module vertletphysics is a simple 3D physics simulator
  • PPhys2D: Built on Phys2D (alternative to JBox2D not maintained any more)
  • PBox2D: Collection of help functions for JBox2D
  • BoxWrap2d: Library built on top of JBox2D
  • fisica: Object oriented implementation of JBox2D

Motivation

Make the creation of 2D physics simulations in Processing as easy as possible without hiding any functionality. Main Characteristics:

  • Object orientation
  • Default values
  • Implementation of common user cases
  • Manage styles like Processing
  • Manage events like Processing

Allow advanced use of JBox2D:

  • Access to underlying objects to allow access to full JBox2D

Hello World

Initialize

  • Import the library
  • Call Fisica.init() in setup() with the sketch as argument
import fisica.*;
 
void setup() {
  size(400, 400);
 
  Fisica.init(this);
}

Create the world

  • Declare a global variable FWorld
  • Define a variable in setup() after initializing fisica
  • By default the world has 3 times the size of the sketch
  • Bodies leaving the world are stopped being simulated
import fisica.*;
 
FWorld world;
 
void setup() {
  size(400, 400);
 
  Fisica.init(this);
  world = new FWorld();
}

Execute and draw the world

  • Drawing and simulation are separated in two functions
  • Call FWorld step() and draw() functions in draw()
  • By default step() grows 1/60 seconds per step
  • By default draw() draws to your sketch
void draw() {
  world.step();
  world.draw();
}
  • FWorld.draw() doesn't delete the background, you have to call background()
  • Also you can make a debug drawing calling drawDebug()

The first body

  • Declare and construct a variable of type FBox
  • Add the body to the world
void setup() {
  // ...
  FBox b = new FBox(30, 50);
  b.setPosition(width/2, height/2);
  world.add(b);
}
  • By default 20 pixles == 1 meter. To change it call Fisica.setScale() after Fisica.init(this).
  • By default the world has gravity. To change it call world.setGravity()
  • The body "falls" from the screen.

Add borders to the world

  • Call world.setEdges() at setup() creates 4 static bodies which limits the world to the borders of the sketch.
  • This bodies are accessible: world.left, world.right, world.bottom, world.top
  • You can delete the ones you don't need.
  • By default this bodies are black.
void setup() {
  // ...
 
  world.setEdges();
  world.remove(world.top);
}
  • By default the bodies can be dragged with the mouse.

The world

Properties and actions

Borders: You can change some of the border properties:

   world.setEdgesFriction(0)
   world.setEdgesRestitution(1)

Gravity: The world has a gravity which affects all mobile bodies. It can be changed:

   world.setGravity(0, -100)

Draggable: Bodies of the world can be dragged by mouse. This can be turned off:

   world.setGrabbable(false)

Body at one position: You can determine a body (or all bodies) which are at one position of the world:

   world.getBody(200, 200)
   world.getBodies(200, 200)

Clear: All bodies and connections can be cleared:

   world.clear()

The bodies

FBox

  • Variables of type FBox have shape of a rectangle
  • Constructor has 2 arguments: width, height
FBox b = new FBox(30, 50);
b.setPosition(100, 100);
world.add(b);

Change the dimensions of the rectangle:

b.setWidth(40);
b.setHeight(40);

FCircle

  • Variables of type FCircle have shape of a circle
  • Constructor has 1 argument: diameter
FCircle c = new FCircle(30);
c.setPosition(200, 100);
world.add(c);

Change the dimensions of the circle:

c.setSize(40);

FPoly

  • Variables of type FPoly have shape of a polygon
  • Constructor without arguments
FPoly p = new FPoly();
p.vertex(-30, -20);
p.vertex( 30, -20);
p.vertex( 30, 0);
p.vertex( 10, 0);
p.vertex( 10, 40);
p.vertex(-10, 40);
p.vertex(-10, 0);
p.vertex(-30, 0);
p.setPosition(300, 100);
world.add(p);

setPosition(x, y) positions the point 0, 0 of the polygon at point x, y of the canvas

FLine

  • Variables of type FLine have the shape of a line
  • Always are static bodies
  • Constructor has 4 arguments: initX, initY, finalX, finalY
FLine l = new FLine(-150, 0, 150, 0);
l.setPosition(width/2, 250);
world.add(l);
  • setPosition(x, y) positions point 0, 0 of the line at point x, y of canvas
  • Bodies collide with lines only in one direction
  • Start and end points can be modified
l.setStart(150, 0);
l.setEnd(width-150, 0);

FCompound

  • Variables of type FCompound are compounded out of other bodies
  • Constructor has no arguments
FBox m1 = new FBox(6, 60);
 
FCircle m2 = new FCircle(20);
m2.setPosition(0, -30);
 
FCompound m = new FCompound();
m.addBody(m1);
m.addBody(m2);
m.setPosition(200, 300);
world.add(m);

Common properties of bodies

Dynamic properties

Position: position in pixels

   body.setPosition(100, 200)

Rotation: rotation in radians

   body.setRotation(PI/4)

Velocity: velocity in pixels per second

   body.setVelocity(50, 0)

Angular velocity: rotation velocity in radians per second

   body.setAngularVelocity(PI/16)

Material properties

Density: mass density of the object in gram per pixel 2

   body.setDensity(0.3)

Restitution: loss of velocity at collision (perpendicular force of a collision) [value from 0 to 1]

   body.setRestitution(0.3)

Friction: loss of velocity at friction (tangential force of a collision) [value from 0 to 1]

   body.setFriction(0.3)

Damping: loss of velocity at movement [value from 0 to 1]

   body.setDamping(0.3)

Angular damping: loss of velocity at rotation [value from 0 to 1]

   body.setAngularDamping(0.3)

Properties of style (drawing)

Fill color: fill color of bodies, same format then Processing

   body.setFill(100, 20, 130)

Stroke color: stroke color of bodies, same format then Processing

   body.setStroke(100, 20, 130)

Stroke weight: stroke weight of body, same format then Processing

   body.setFriction(0.3)

Image: draw body with a image

   PImage imagen = loadImage("canica.png");
   body.attachImage(imagen)

Other properties - I

Dynamics: bodies by default are dynamic, they can be set static:

   body.setStatic(true)

Rotateable: bodies by default are rotateable, this can be turned off:

   body.setRotatable(false)

Sleepable: when bodies don't have contacts during certain time the enter the state sleepable and are stopped being simulated

   body.setAllowSleeping(false)

Grabbable: you can grab the body with the mouse

   body.setGrabbable(false)

Drawable: by default all bodies of a world are drawn, this can be turned off:

   body.setDrawable(false)

Other properties - II

Sensor: The body gets in contact and collides with other bodies. Collisions can be turned off while maintaining contact detection to know when a body enters a certain region:

   body.setSensor(true)

Tags: giving tags to bodies makes it easier to detect contacts

   body.setName("hole")

Groups: by default all bodies contact with all. You can define groups to detect certain contacts. Bodies belonging to a negative group don't contact with bodies of the same group.

   body.setGroupIndex(-1)

Categories or filters: You can define categories and groups a body belongs to and which it can contact. But you need knowledge with binary masks:

   body.setCategoryBits(0x0002) // 0x0002  == 0000 0000 0000 0010 
   body.setFilterBits(0x0004) // 0x0004  == 0000 0000 0000 0100

Actions of the body

Impulse: Apply a impulse (similar to a hit)

   body.addImpulse(100, 0)
   body.addImpulse(100, 0, 0, 40)

Force: apply a force (similar to push, usually we apply it during some time)

   body.addForce(100, 0)
   body.addForce(100, 0, 0, 40)

Angular force: add a angular force (like torque)

   body.addTorque(PI/6)

Draw: draw the body. Specially interesting when a body hasn't been added to the world:

   body.draw()

Technical draw: draw the state of the body. Specially interesting during development:

   body.drawDebug()

Joints

FDistanceJoint - The spring

  • Variables of type FDistanceJoint are spring joints
  • Constructor has 2 arguments: body1, body2
FBox b1 = new FBox(70, 20);
b1.setPosition(100, 100);
world.add(b1);
FBox b2 = new FBox(20, 50);
b2.setPosition(300, 100);
world.add(b2);
FDistanceJoint spring = new FDistanceJoint(b1, b2);
world.add(spring);

Properties

  • By default the spring hooks into the center of the bodies. But you can hook it into any position relative to the center:
   spring.setAnchor1(30, 0)
   spring.setAnchor2(0, 40)
  • By default the spring is rigid. But it can be elastic controlling frequency and amortization:
   spring.setFrequency(0.5)
   spring.setDamping(0.1)
  • By default the balance between bodies is the distance between the bodies. But it can be arbitrary:
   spring.setLength(60)

FPrismaticJoint - The rotor

  • Variables of type FPrismaticJoint are rotor joints
  • Constructor has 2 arguments: body1, body2
  • Typically you use it with one fixed and another mobile body
FBox b2 = new FBox(20, 50);
b2.setPosition(300, 100);
world.add(b2);
FPrismaticJoint rotor = new FPrismaticJoint(world.bottom, b2);
world.add(rotor);

Properties

  • By default the rotor has a vertical orientation. But the rotor can be oriented at any direction:
   piston.setAxis(1, 0)
  • By default the movement on the rotor is not limited. But you can limit it:
   piston.setEnableLimit(true)
   piston.setLowerTranslation(-50)
   piston.setUpperTranslation(150)

FRevoluteJoint - The axis

  • Variables of type FRevoluteJoint are axis joints
  • Constructor has 2 arguments: body1, body2
FBox b1 = new FBox(70, 20);
b1.setPosition(100, 100);
world.add(b1);
FBox b2 = new FBox(20, 50);
b2.setPosition(300, 100);
world.add(b2);
FRevoluteJoint axis = new FRevoluteJoint(b1, b2);
world.add(axis);

Properties

  • By default the rotation axis is in the middle between the two bodies. But you can position it on any absolute point of the canvas:
   axis.setAnchor(width/2, height/2)
  • By default movement around the axis is not limited. But you can limit it:
   axis.setEnableLimit(true)
   axis.setLowerAngle(-PI/4)
   axis.setUpperAngle(PI/4)

Common properties of joints

Dynamic properties

Collision: Bodies united by joints collide between themselves. You can avoid it by:

   junta.setCollideConnected(false)

Reaction force: Reaction force of translation which the joint executes on the second body.

   junta.getReactionForceX()
   junta.getReactionForceY()

Angular reaction force: Angular reaction force (of torque) which executes the joint on the second body.

   junta.getReactionTorque()

Contacts

Contacts are events

  • Works similar to mouse events in Processing
  • Contact methods:
   void contactStarted(FContact contact) { // code to execute }
   void contactPersisted(FContact contact) { // code to execute }
   void contactEnded(FContact contact) { // code to execute }

Not everything is valid with contacts

A lot of actions are not permitted during contacts in JBox2D (e.g. add/remove bodies from the world). But in fisica we solved it. If you find any action that is not valid, contact us!

Properties of contacts

The bodies: two bodies of the contact

   contact.getBody1()
   contact.getBody2()
   contact.contains("ball", "hole")  // you can ask for them by tag
   contact.contains(body)  // you can ask by variable

Point of contact: point where the contact starts

   contact.getX()
   contact.getY()

Direction: direction of the contact (normal vector of the contact)

   contact.getNormalX()
   contact.getNormalY()

Velocity: velocity of the contact (relative velocity between bodies)

   contact.getVelocityX()
   contact.getVelocityY()

Separation: the simulation is not perfect, there can always exist a certain separation (o penetration) between bodies

   contact.getSeparation()

A different form of access to contacts

  • Contacts can be asked to the body
   body.getContacts()
  • Also can be asked for if the touch a certain body
   body.isTouchingBody(anotherbody)
  • Or just ask for the bodies which it touches
   body.getTouching()