Penny Door

There’s a little door in Doherty that can only be opened with the use of a coin. Coins clank off the sides of the door and each other, giving passerbys hints as to how to get in. Originally, I wanted to do a double projection that had glowing particles slipping in and out of the door, but that would’ve required multiple projectors + connecting multiple sketches together via osc, and I didn’t have the time to work it all out. I ended up having projection issues anyways–the projector I was using was too small and too weak to project onto the space!

I’d love to do more projection mapping work, perhaps next time with a little pico projector + RPi combo. I found it’s ridiculously difficult to line up the projectors, even with Keystone–I’d like to try my hand at computer vision next time.

import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;

import pbox2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.contacts.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.collision.shapes.Shape;



PImage penny;

Minim minim;
AudioSample clang;
AudioSample bigClang;

//door size
float door = 60.5 * 5 * 1.5;
float lastHit;
boolean caliDoor;

//box2D world
PBox2D box2d;

//stuff
ArrayList movers;
Boundary[] walls = new Boundary[4];

import deadpixel.keystone.*;
Keystone ks;
CornerPinSurface surface;
PGraphics offscreen;

void setup() {
  size(640*2, 360*2, P3D);
  ks = new Keystone(this);
  surface = ks.createCornerPinSurface(int(640*1.5), int(360*1.5), 20);
  offscreen = createGraphics(int(640*1.5), int(360*1.5), P3D);
  smooth();

  // http://en.wikipedia.org/wiki/Penny_(United_States_coin)
  penny = loadImage("2010_cent_obverse.png"); 

  minim = new Minim(this);
  // http://www.freesound.org/people/timgormly/sounds/170958/
  clang = minim.loadSample("clang.wav", 512);
  // http://www.freesound.org/people/DJ%20Chronos/sounds/123587/
  bigClang = minim.loadSample("loud_clang.aiff", 512);


  box2d = new PBox2D(this);
  box2d.createWorld();
  box2d.listenForCollisions();
  // No global gravity force
  box2d.setGravity(0, -2);

  movers = new ArrayList();
  for (int i = 0; i < 8; i++) {
    movers.add(new Mover(random(30, 60), offscreen.width/2, offscreen.height/2));
  }
  float cx = offscreen.width/2;
  float cy = offscreen.height/2;
  walls[0] = new Boundary((cx - door/2), cy, 1, offscreen.height);
  walls[1] = new Boundary(cx + door/2, cy, 1, offscreen.height);
  walls[2] = new Boundary(cx, cy - door/2, offscreen.width, 1);
  walls[3] = new Boundary(cx, cy + door/2, offscreen.width, 1);
}

void draw() {

  PVector surfaceMouse = surface.getTransformedMouse();
  offscreen.beginDraw();
  offscreen.background(0);

  // We must always step through time!
  box2d.step();

  for (Mover m : movers) {
    m.display();
  }
  for (int i = 0; i < 4; i++) {
    walls[i].display();
  }

  if (caliDoor == true) {
    offscreen.noFill();
    offscreen.strokeWeight(3);
    offscreen.stroke(255);
    offscreen.rect(width/2, height/2, door, door);
  } else {offscreen.stroke(0);}
  offscreen.endDraw();
  background(0);
  surface.render(offscreen);
}

// Collision event functions!
void beginContact(Contact cp) {

  clang.trigger();
}
// Objects stop touching each other
void endContact(Contact cp) {
}
void keyPressed() {
  switch(key) {
  case 'c':
    // enter/leave calibration mode, where surfaces can be warped 
    // and moved
    ks.toggleCalibration();
    if (caliDoor == false) {
      caliDoor = true;
    } 
    else {
      caliDoor = false;
    }
    break;

  case 'l':
    // loads the saved layout
    ks.load();
    break;

  case 's':
    // saves the layout
    ks.save();
    break;
  }
}

// The Nature of Code
// 
// Spring 2012
// PBox2D example

// A fixed boundary class

class Boundary {

  // A boundary is a simple rectangle with x,y,width,and height
  float x;
  float y;
  float w;
  float h;
  
  // But we also have to make a body for box2d to know about it
  Body b;

  Boundary(float x_,float y_, float w_, float h_) {
    x = x_;
    y = y_;
    w = w_;
    h = h_;

    // Define the polygon
    PolygonShape sd = new PolygonShape();
    // Figure out the box2d coordinates
    float box2dW = box2d.scalarPixelsToWorld(w/2);
    float box2dH = box2d.scalarPixelsToWorld(h/2);
    // We're just a box
    sd.setAsBox(box2dW, box2dH);


    // Create the body
    BodyDef bd = new BodyDef();
    bd.type = BodyType.STATIC;
    bd.position.set(box2d.coordPixelsToWorld(x,y));
    b = box2d.createBody(bd);
    
    // Attached the shape to the body using a Fixture
    b.createFixture(sd,1);
    
    b.setUserData(this);
  }

  // Draw the boundary, if it were at an angle we'd have to do something fancier
  void display() {
    fill(0);
    stroke(0);
    rectMode(CENTER);
    offscreen.rect(x,y,w,h);
  }

}

// The Nature of Code
// 
// Spring 2011
// PBox2D example

// Showing how to use applyForce() with box2d

class Mover {

  // We need to keep track of a Body and a radius
  Body body;
  float r;

  Mover(float r_, float x, float y) {
    r = r_;
    // Define a body
    BodyDef bd = new BodyDef();
    bd.type = BodyType.DYNAMIC;

    // Set its position
    bd.position = box2d.coordPixelsToWorld(x, y);
    body = box2d.world.createBody(bd);

    // Make the body's shape a circle
    CircleShape cs = new CircleShape();
    cs.m_radius = box2d.scalarPixelsToWorld(r);

    // Define a fixture
    FixtureDef fd = new FixtureDef();
    fd.shape = cs;
    // Parameters that affect physics
    fd.density = 0.8;
    fd.friction = 0.3;
    fd.restitution = 1.1;

    body.createFixture(fd);

    body.setLinearVelocity(new Vec2(random(-5, 5), random(-5, -5)));
    body.setAngularVelocity(random(-1, 1));
  }

  void applyForce(Vec2 v) {
    body.applyForce(v, body.getWorldCenter());
  }


  void display() {
    // We look at each body and get its screen position
    Vec2 pos = box2d.getBodyPixelCoord(body);
    // Get its angle of rotation
    float a = body.getAngle();
    offscreen.pushMatrix();
    offscreen.translate(pos.x, pos.y);
    offscreen.rotate(a);
    offscreen.image(penny,-r,-r,r*2, r*2);
    offscreen.popMatrix();
  }
}

Comments are closed.