Adam-Assignment-07-Projection
Science of the bladder –
http://tinyurl.com/ky2lfx9
Augmented Projection – Occupancy indicator from adambd on Vimeo.
Science of the bladder –
http://tinyurl.com/ky2lfx9
Augmented Projection – Occupancy indicator from adambd on Vimeo.
So, I knew where I wanted to do my projection: The underground. There are some fantastic-looking lamps around which I wanted to project different objects to interact with them. For example: These were two ideas I had. The first I was going to model using the box2D physics library to make things fall on top of/jump off the top of the lamps, and also potentially have one of the lamps as an attractor. The second I was going to model using springs, where the lamps would act as “anchors” and would radiate these lightening-like Van der Graff machine-like patterns. (Much spookier and season appropriate.)
So I started working on that. Here is the test projection I made in my dorm:
This is only the first idea. It actually worked really quite well. Note that in this version I had not yet added the lower boundary which was the top of the sofas at the Underground just underneath the lamps. (I forgot about that until I got there.) Also note that in this version I am projecting the lamps onto the wall just so I can tell where they go.
Of course, the project I made was about 1m x 0.6m. The wall was considerably larger (and proportioned differently) — and the projectior failed utterly to project any kind of image at all. So I tried changing the location… which essentially meant everything I had done was invalid. i.e., not square.
So… here is a quick video where I just get one of the examples to work with another alternative surrounding I was thinking of trying to work with, since the first one didn’t work out:
For my projection project, I wanted to utilize sound while using box2D. It was really difficult for me to come up with an idea. At first I thought about creating eyes onto an inanimate object to give it life. But it was too similar to the creature project. As I was wondering what to create, my eyes fell upon the different colored glazes I bought for my clay mini class. I projected light onto the different glazes at an angle and realized that if the camera were tilted at a certain angle, I could play with just the shadows of the glaze containers because they were higher on the wall than the actual objects.
Why not mess with the shadow’s of the object? The shadows are black, and if I project black onto them, they will be saved. This created another curious layer as to what we are allowed to interact with. By interacting with actual objects, we extract something from fantasy and implement it into reality. Playing with the shadows seem like an even more fantastical experience. While the object is untouched, the shadow, the “child” of the object, which is supposed to mirror the object, transforms separate from it’s “parent”.
There is a certain sensation of satisfaction we gain from clearing things out. Like when we’re done washing dishes, and there’s just gunk leftover in the sink. There’s satisfaction in swiping all of that down into the food disposal drain. Or when we’re playing Bejeweled, and we clear out a poop-load of diamonds in one move. This sensation was the inspiration for this project, and what I wanted to emulate.
Little balls will appear to fall into the bucket until it fills up, and when the user presses space, the bucket will flush and all of the balls will drain. If the user does not flush within a certain amount of time, the bucket will clog and explode balls in all directions. The idea behind this project was to create a tension between the user’s desire to drain the bucket and let it fill up. The more the bucket fills up, the more satisfying the drain is. But the user should not want to allow the bucket to fill for too long, as it will empty out in a messy manner.
Keystone is frustrating. The actual coding took way less time than trying to get Keystone to work on my program (especially since my computer won’t even render it correctly due to graphics card problems!) and calibrating it (probably an exaggeration but it certainly felt like it). So I learned this, yet again: logistics ruin the fun out of everything. Also, I didn’t expect the shadow of the stream of water–no way around that.
My main inspiration was to use sound that was already provided to me as part of the projection–and that sound was water hitting the sink as I washed my hands every day. I imagined the sound as bunch of electrically charged particles climbing up rather than flushing downwards into the drain, and it got to the point where I could hear electricity in the water.
(part of the drawing is Golan’s explanation on why my design isn’t feasible for now)
Initially I wanted creatures that would climb up the sink and then turn into birds when they reached the top corners, but Golan was against the idea since it would be too complex for an assignment. He instead suggested that I confine it to a flat area near the drain, which turned out alright. The project might develop further by implementing the original idea I had.
Nervous from Chloe Chia on Vimeo.
In the rush of things, I seem to have forgotten a few things. 1) The ability for art to elegantly express that which is hard to express, 2) the ability for art in catharsis, and 3) reaching out to people when in need of help.
I originally had numerous separate ideas for the assignments, but after numerous iterations and talk of being ‘poetic’, I realized that this creature that I made–originally intended to be a “live stress ball”–could have so much more meaning when placed into context. Especially if that context is my own studio in the Design school. Lately, I have been feeling very nervous in general about all of my classes, ever so precariously keeping the balance between them, outside activities, and mental states. The stress of it can be overwhelming, and leave one feeling very trapped as I do now.
But in the process of making the video, and watching my own creature almost ‘come to terms’ with his confines, I can’t help but wonder whether or not such stress and pressure that I feel is merely an artificial boundary I place upon myself. It is up to me to figure out, given the limitations upon my skills and my time, what am I capable of doing, rather than focusing just on the limits and failures.
This is definitely the same sort of thinking with which I approached these assignments, keeping it as simple as possible, and working with the code that I’m most comfortable with. On the programming end, there are still many things I would like to fix later on as I get more and more fluent in Processing. One of those things includes figuring out how to get the creature attracted to the mouse, since at the moment a mouse press only activates a different gravity vector (which, I admit, I modified throughout the process of filming). I couldn’t quite figure out how to recall each node that creates the spring skeleton of the creature…
Unfortunately Javascript mode doesn’t seem to like my sketch and refuses to run in the OpenProcessing applet… Will have to figure out how to work that kink.
The Keyfleas live on a two-dimensional flatland. They travel as a flock, over key mountains and through aluminum valleys. They avoid touching letterforms, since they suspect that the symbols are of some evil origin. On occasion, a hostile tentacle invades the flatland and disturbs its inhabitants.
Although I had several ideas for contexts in which an augmented projection could exist, most of them amounted to arbitrary particles careening across a surface. No poetry, no narrative. So instead of an architectural surface as originally planned, I project on an Apple Keyboard. My reasons for this are both practical and conceptual. The keys are clean and white, and the Pico projector can attach via Manfotto Magic Arm to a nearby table. This addresses the constraints of a low powered projector, as well as issues relating to variable lighting and surface conditions. My solution for key calibration was as follows: key-shaped boundaries are placed in the Box2D world using the mouse, and then the key is pressed in order to map that body to its corresponding key. This calibration process can be seen at the end of the video.
But these are only technical considerations; more important was choosing a context in which a narrative – albeit a simple one – could emerge. The suggestion that there are parasitic entities living in our devices is an interesting an unsettling one. An obvious inspiration for this project was Chris Sugrue’s “Delicate Boundaries”, where light bugs crawl out of the screen and onto the viewer’s hand.
A project which explores the delicate boundary between screen space and physical space
An improved Keyfleas might develop creatures with more character than mere filled ellipses (see Delicate Boundaries above). Or the shapes might pulsate and respond to keystrokes in a more intelligent manner than they currently do.
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();
}
}
Jun’s thought process for this project:
Need something creative to project on -> ceiling lamp in room with dome shaped shades look cool -> need to find place on campus with similar lamp -> Porter has ceiling fans, ceiling fans are objects on ceiling too -> ceiling fans spin, so need to make something that plays on the spinning motion of the fan
And I ended up with something like this.
The final product did end up looking more like a planetary system than I intended to. This is just because the initial velocity of the particles are set to be tangent to the circles, and the gravity toward the center makes the particles orbit. I also didn’t foresee the projection would be projected onto the ceiling too, but I think in a way that made it look cooler. This is a projection that can’t live without the place it is projected on. AKA, if you run the program on your computer, it looks very simple, to the point of being boring/dull. However, once it is projected onto the fan, the motion of the fan makes the projection seem mesmerizing.
Code (non-Keystone version):
import pbox2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.contacts.*;
PBox2D box2d;
ArrayList allParticles;
Surface surface;
boolean showOutline;
boolean showCircles;
float outerR;
float innerR;
void setup() {
size (600, 600);
showOutline = true;
showCircles = true;
outerR = 500;
innerR = 80;
smooth();
box2d = new PBox2D(this);
box2d.createWorld();
// box2d.setGravity(width/2, -height/2);
allParticles = new ArrayList ();
surface = new Surface(innerR/2);
}
void draw() {
background(0);
float make = random(0, 1.0);
if (make<0.1) {
float angle = random(0, TWO_PI);
float x = width/2+cos(angle)*outerR/2;
float y = height/2+sin(angle)*outerR/2;
Particle p = new Particle(x, y, 5, angle);
allParticles.add(p);
}
if (keyPressed) {
if (key=='r' || key=='R') {
for (Particle p: allParticles) {
p.done();
}
allParticles.clear();
}
else if (key=='c' || key=='C') {
if (showCircles) showCircles = false;
else showCircles = true;
}
else if (key=='o' || key=='O') {
if (showOutline) showOutline = false;
else showOutline = true;
}
else if (key==CODED) {
if (keyCode==UP) {
innerR+=2;
}
else if (keyCode==DOWN) {
innerR-=2;
}
else if (keyCode==LEFT) {
outerR-=2;
}
else if (keyCode==RIGHT) {
outerR+=2;
}
}
}
if (showCircles) {
float r;
noFill();
stroke(255);
strokeWeight(3);
r = innerR+0.95*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(245);
strokeWeight(2.5);
r = innerR+0.90*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(225);
strokeWeight(2.2);
r = innerR+0.82*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(195);
strokeWeight(1.5);
r = innerR+0.70*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(155);
strokeWeight(1.0);
r = innerR+0.55*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(105);
strokeWeight(1.0);
r = innerR+0.40*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(45);
strokeWeight(0.8);
r = innerR+0.20*(outerR-innerR);
ellipse(width/2, height/2, r, r);
}
if (showOutline) {
surface.display();
noFill();
stroke(0, 255, 0);
strokeWeight(1);
ellipse(width/2, height/2, outerR, outerR);
}
box2d.step();
for (Particle p: allParticles) {
Vec2 pos = new Vec2();
pos = box2d.getBodyPixelCoord(p.body);
//Find distance from center
float dx = width/2-pos.x;
float dy = -height/2+pos.y;
Vec2 grav = new Vec2(dx, dy);
float dfc = sqrt(dx*dx+dy*dy);
float r0 = p.r0;
float newR = map(dfc, 0, outerR/2, r0/5, r0);
p.applyForce(grav);
// p.r = newR;
if (dfc height+r*2) {
killBody();
return true;
}
return false;
}
//
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();
pushMatrix();
translate(pos.x,pos.y);
rotate(-a);
fill(100,140,200);
noStroke();
strokeWeight(1);
ellipse(0,0,r*2,r*2);
popMatrix();
}
// Here's our function that adds the particle to the Box2D world
void makeBody(float x, float y, float r) {
// Define a body
BodyDef bd = new BodyDef();
// Set its position
bd.position = box2d.coordPixelsToWorld(x,y);
bd.type = BodyType.DYNAMIC;
bd.linearDamping = 0.1f;
body = box2d.world.createBody(bd);
// Make the body's shape a circle
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
FixtureDef fd = new FixtureDef();
fd.shape = cs;
// Parameters that affect physics
fd.density = 1;
fd.friction = 0.01;
fd.restitution = 0.3;
// Attach fixture to body
body.createFixture(fd);
// Give it a random initial velocity (and angular velocity)
body.setLinearVelocity(new Vec2((50*cos(PI/2-a0)),(50*sin(PI/2-a0))));
body.setAngularVelocity(0);
}
void applyForce(Vec2 force) {
Vec2 pos = body.getWorldCenter();
body.applyForce(force, pos);
}
}
class Surface {
Body body;
float r;
Surface(float r_) {
r = r_;
makeBody(width/2,height/2,r);
body.setUserData(this);
}
void display() {
// We look at each body and get its screen position
Vec2 pos = box2d.getBodyPixelCoord(body);
pushMatrix();
noFill();
stroke(0,255,0);
strokeWeight(1);
ellipse(width/2, height/2, r*2, r*2);
popMatrix();
}
// Here's our function that adds the particle to the Box2D world
void makeBody(float x, float y, float r) {
// Define a body
BodyDef bd = new BodyDef();
// Set its position
bd.position = box2d.coordPixelsToWorld(x, y);
bd.type = BodyType.STATIC;
body = box2d.createBody(bd);
// Make the body's shape a circle
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
FixtureDef fd = new FixtureDef();
fd.shape = cs;
// Attach fixture to body
body.createFixture(fd);
}
}
The two bathroom sign people start rolling into each other, and upon collision they form an explosion and their baby transcends above (gender of baby is randomly determined).
I found a wall with the 2 gendered bathroom signs close to each other, and this just popped into my head. However, the problem was, even though the signs were close together, they were not close enough, as the hallway I was in was too narrow to project correctly. In the end I decided to keep my idea, and instead I used a chalkboard to be projected onto, which I felt to be more fitting into the theme of education (albeit incorrectly). Because I was projecting directly onto a chalkboard with a classroom’s projector, I did not run into any Keystone issues.
In the end, I feel like the chalkboard turned out to be better than the original idea I had with the bathroom signs. I do feel that I cheated a little by modifying the environment before projection and if I can make this somehow more public and put it less under my control, it would be more fun.