From Scratch: An Object

I officially earned an important geek merit badge just now: I wrote my first complex class from scratch. Sure, I’ve hacked around code before, and tinkered with excellent suggestions from ITP’s awesome residents & professors — but now, I identified the need for an object, and wrote the damm thing myself (starting, of course, with Amelia’s exhaustive geometry-drawing functions).

Something about the whole “from scratch” thing makes me think of good ‘ol Sagan. Source code after the break.

#include “testApp.h”

//————————————————————–
void testApp::setup(){

ofBackground(255,255,255);

//set booleans initially equal to false

directContact=false;
sleepPose = 0;

}

//————————————————————–
void testApp::update(){

}

//————————————————————–
void testApp::draw(){

for (int i = 0; i< sleepSet.size(); i++){
sleepSet[i]->drawShape();

}

}

//————————————————————–
void testApp::keyPressed(int key){

if (key == ‘l’){

printf(“how many sleepSets: %d\n”, int(sleepSet.size()));
printf(“xCounter: %d \n”, xCounter);
printf(“yCounter: %d \n”, yCounter);
printf(“numberOfSquaresElapsed: %d \n”, numberOfSquaresElapsed);
printf(“sleepPose: %d \n”, sleepPose);
printf(“numberOfSquaresElapsed: %d \n”, numberOfSquaresElapsed);

if (xCounter > 28){
xCounter=1;
yCounter=yCounter+2;
}else{
xCounter++;
}

//make container for each sleepState object (sleepSet)

sleepState* OBJ = new sleepState(numberOfSquaresElapsed);
sleepSet.push_back( OBJ );

sleepSet[numberOfSquaresElapsed]->storeSleepData(xCounter, yCounter, numberOfSquaresElapsed, directContact, sleepPose);

numberOfSquaresElapsed+=1;

}

//DEFINE PROXIMITY
if (key == ‘c’) {
directContact = !directContact;
}

//DEFINE POSITION

if (key == ’0′){
sleepPose = 0;
}
if (key == ’1′){
sleepPose = 1;
}
if (key == ’2′){
sleepPose = 2;
}
if (key == ’3′){
sleepPose = 3;
}
if (key == ’4′){
sleepPose = 4;
}
if (key == ’5′){
sleepPose = 5;
}
if (key == ’6′){
sleepPose = 6;
}
if (key == ’7′){
sleepPose = 7;
}
if (key == ’8′){
sleepPose = 8;
}

}

//
// sleepState.h
// REMQuilt.3.4 2
//
// Created by Justin Lange on 3/4/12.
// Copyright (c) 2012 Magic Hat Films. All rights reserved.
//

#ifndef REMQuilt_3_4_2_sleepState_h
#define REMQuilt_3_4_2_sleepState_h

#include “ofMain.h”

class sleepState
{
public:
sleepState( int _ID );

void drawShape();
void storeSleepData(int xCounter, int yCounter, int numberOfSquaresElapsed, bool directContact, int sleepPose);

int ID;

//sleep sensing variables

bool directContact;
int sleepPose;

//coordinates for entire quilt square

int xPosition;
int yPosition;
int screenWidth;
int screenHeight;
int quiltSquareWidth;
int quiltSquareHeight;

//coordinates for 2-minute quilt square geometry

int topCenterX;
int topCenterY;
int topRightX;
int topRightY;
int centerLeftX;
int centerLeftY;
int centerX;
int centerY;
int centerRightX;
int centerRightY;
int bottomLeftX;
int bottomLeftY;
int bottomCenterX;
int bottomCenterY;
int bottomRightX;
int bottomRightY;

int xCounter;
int yCounter;
int numberOfQuiltSquaresToDraw;
int numberOfSquaresElapsed;
int currentXCounter;
int currentYCounter;

};

#endif

//
// sleepState.cpp
// REMQuilt.3.4 2
//
// Created by Justin Lange on 3/4/12.
// Copyright (c) 2012 Magic Hat Films. All rights reserved.
//

#include
#include “sleepState.h”
//#define NUM 10

sleepState::sleepState( int _ID )
{
xCounter=1;
yCounter=1;

//set booleans initially equal to false

directContact=false;
sleepPose = 0;

//define coordinates for entire quilt square
xPosition=1;
yPosition=1;
screenWidth=1423;
screenHeight=1024;
// quiltSquareWidth= screenWidth/30;
// quiltSquareHeight= screenHeight/24;

quiltSquareWidth= 30;
quiltSquareHeight= 24;

//define coordinates for 2-minute quilt square geometry
topCenterX= 1;
topCenterY= 1;
topRightX=1;
topRightY=1;
centerLeftX=1;
centerLeftY=1;
centerX=1;
centerY=1;
centerRightX=1;
centerRightY=1;
bottomLeftX=1;
bottomLeftY=1;
bottomCenterX=1;
bottomCenterY=1;
bottomRightX=1;
bottomRightY=12;

numberOfQuiltSquaresToDraw=0;

}

void sleepState::storeSleepData(int xCounter, int yCounter, int numberOfSquaresElapsed, bool directContact, int sleepPose){

xPosition=(xCounter * quiltSquareWidth);
yPosition=(yCounter * quiltSquareWidth);
topCenterX= (xPosition + (quiltSquareWidth/2));
topCenterY= yPosition;
topRightX= (xPosition + quiltSquareWidth);
topRightY= ((xPosition + quiltSquareWidth),yPosition);
centerLeftX= xPosition;
centerLeftY= (yPosition + (quiltSquareHeight/2));
centerX= (xPosition + (quiltSquareWidth/2));
centerY= (yPosition + (quiltSquareHeight/2));
centerRightX= (xPosition+quiltSquareWidth);
centerRightY= (quiltSquareHeight/2);
bottomLeftX= (xPosition + quiltSquareHeight);
bottomLeftY= yPosition;
bottomCenterX= (xPosition+(quiltSquareWidth/2));
bottomCenterY= quiltSquareHeight;
bottomRightX= (xPosition+quiltSquareWidth);
bottomRightY= quiltSquareWidth;

sleepPose = sleepPose;
directContact = directContact;

}

void sleepState::drawShape()
{

//SET COLOR (based on proximity)
if (directContact==1) {
ofSetHexColor(0xA84D2C); // contact bright orange
}else{
ofSetHexColor(0x2E8270); // no contact blue
}

/*positions reference

0 no data
1 restless on back
2 restlessOnFront
3 restless looking right
4 restless looking left
5 still On Front
6 stillOnBack
7 stillLookingRight
8 stillLookingLeft

*/

//DRAW SHAPES
if (sleepPose==0){
//DRAW CIRCLE IN THE MIDDLE
ofFill();
ofCircle(centerX,centerY,(quiltSquareWidth/2));
}

if (sleepPose==1) {
ofFill();
//DRAW TWO TOP-UP TRIANGLES
ofTriangle(topCenterX, topCenterY, centerLeftX, centerLeftY, centerRightX, centerRightY); //first triangle
ofTriangle(centerX,centerY, bottomLeftX, bottomLeftY, bottomRightX, bottomRightY); //second triangle
}
if (sleepPose==2) {
ofFill();
//DRAW TWO TOP-DOWN TRIANGLES
ofTriangle(xPosition, yPosition, topRightX, topRightY,centerX,centerY); //first triangle
ofTriangle(centerLeftX, centerLeftY, centerRightX, centerRightY, bottomCenterX, bottomCenterY); //second triangle
}

if (sleepPose==3) {
ofFill();
//DRAW TWO RIGHT-FACING TRIANGLES
ofTriangle(centerRightX, centerRightY, topCenterX, topCenterY, bottomCenterX, bottomCenterY); //first triangle
ofTriangle(centerX,centerY, xPosition, yPosition, bottomLeftX, bottomLeftY); //second triangle

}
if (sleepPose==4) {
ofFill();
//DRAW TWO TOP-DOWN TRIANGLES
ofTriangle(centerRightX, centerRightY, topCenterX, topCenterY, bottomCenterX, bottomCenterY); //first triangle
ofTriangle(centerX,centerY, xPosition, yPosition, bottomLeftX, bottomLeftY); //second triangle

}
if (sleepPose==5) {
//DRAW ONE TOP-DOWN TRIANGLE
ofFill();
ofTriangle(xPosition, yPosition, topRightX, topRightY, bottomCenterX, bottomCenterY);
}
if (sleepPose==6) {
//DRAW ONE TOP-UP TRIANGLE
ofFill();
ofTriangle(bottomLeftX, bottomLeftY, topCenterX, topCenterY, bottomRightX, bottomRightY);

}
if (sleepPose==7) {
//DRAW ONE RIGHT-FACING TRIANGLE
ofFill();
ofTriangle(centerRightX, centerRightY, xPosition, yPosition, bottomLeftX, bottomLeftY);
}
if (sleepPose==8) {
//DRAW ONE LEFT-FACING TRIANGLE
ofFill();
ofTriangle(centerLeftX, centerLeftY, topRightX, topRightY, bottomRightX, bottomRightY);
}

}

//b[NUM];

Spatial Media coding progress

Graphically, the concept is fairly simple: assign each five minute interval of a quilt “square” and fill that square with a unique geometry, a pattern that represents how a sleeping couple slept during that five minutes.

We enjoyed sitting down together and reworking the code.

Amelia created a nice list of functions that called ofTriangle(). Because these were all booleans, it was difficult to envision how a variety of attributes could be applied simultaneously. First, we rewrote the code to assign a number to each sleeping position. By adding a few conditionals to keyPress, shortly it became possible to press a number between 0 and 8 changed the shape displayed on the top left corner of the screen. Adding

if(‘c’) coupleTouching != touching;

and, if(coupleTouching) color = blue
else color = orange

allowed us to model how a combination of different input would change a single quilt square.

Next, created a counter that incremented on another keyPress, and made that first x coordinate of the triangles — from which the rest of the coordinates of the triangle derived their position — equal to counter*quiltWidth. Once we added a conditional to increment the yPosition and reset X back to zero once we reached the edge of the screen, we were able to move a single quilt square, and change its position.

Next, we needed to find a way to draw multiple quilt Squares, each with different geometry, according to the same pattern with which we moved a single square across the screen. Because the geometry would be dependent on a unique set of data for each five-minute sleep interval, a simple for() loop would not suffice. I suggested we write a recursive function.

Before we were able to get it to load without hanging (likely stuck in an infinite loop) Amelia had to leave for an important meeting, and my other partner, Patrick, had to work with his group for a different class, so I decided to explore another option: giving the geometry its own class and vector. A dynamic array, I reasoned, would give us a flexible container to hold the unique shape and the variety of other data that we’d use to reference and visualize each interval of sleep.

Making PhoneGap less ‘web-appy’

The whole point of PhoneGap is to make web apps less, well, like web apps. So, when you click a an image with an href (a link to another page or function) and a big ugly orange bracket highlights the picture, it’s a bit of a give-away. And by give-away, I mean that takes away something: the seemless experience and aesthetic continuity of your app.

Fortunately, the fix is simple: just add this line to your CSS in body,html{}: -webkit-tap-highlight-color: rgba(255, 255, 255, 0);

Done.

Gaps in PhoneGap

PhoneGap doesn’t like my CSS. Or some of it, anyway.

My intent was simple: use the accelerometer to call a function that changes the image displayed on the screen. When the image failed to refresh, I went simpler: set “onmousedown” to run the displayImage() function that refreshes the image. Still no luck.

But when I deleted the CSS reference to the image’s class, everything worked splendidly. What gives?

Here’s the CSS I deleted:

.imageOverlay{
width: 80%;
height: 80%;
margin-left: auto;
margin-right: auto;
position: fixed;
z-index:1;

}

Somewhere in there lies the problem. Time to delete it all, and add it back, one suspicious line at a time.

EDIT: “position: fixed;” turns out to be a problematic line. So far, it seems OK if other overlapping images have a position attribute — but not the image that I’m replacing, using Javascript: document.getElementById(“adviceText”).src=texts[text];

Laser Cutter Workflow

We’re fortunate to have our own laser cutter in the ITP shop, the Epilog Mini Laser with a 12″x24″ cutting bed. Because students design on their personal laptops but must move these files to single PC terminal that is calibrated to control the machine, any project that requires any revisions after the first cut presents a workflow problem: any edits you make to the local file on your laptop or the laser cutter PC don’t sync to the other, and the ensuing deluge of multiple versions can get confusing.

Here’s an easy fix: combinine the power of your dropbox public folder and a URL shortner like to create easy one-way sync.

  • Move your design file to your DropBox public folder. You’ll want to make all of your edits from this file. Rename it — not just “ITP Laser template Copy”
  • ctrl-click on the file and select the DropBox context menu. Choose “Copy Public Link”
  • Navigate to tinyUrl.com and paste in the public link you’ve copied from your dropbox folder. It should look something like, “http://dl.dropbox.com/u/6283763/myMeaningfullyTitledIllustratorFileName.ai”
  • Under “custom alias” choose a short, easy-to-remember name for your tinyURL link, like, “shopNameTagJustin”
  • Walk over to the laser cutter PC, open up a browser, type in “www.tinyURL.com/shopNameTagJustin” (replacing “shopNameTagJustin” with whatever you chose for your tinyURL custom alias) and your file will download and open up in illustrator.
  • Make any future edits on your laptop, not the laser cutter PC. Make sure to “save” on your laptop, then type in the same tinyURL address on the laser cutter PC. Your updated file will download, as before.

There you have it. It’s important to note that this method only achieves one-way sync — you still need to make your changes to the file on your computer (and only on your computer), but at least you don’t need to repeatedly email the file to yourself. And, as long as you’re working off the file in your dropBox and you save after you change something (so that the changes are pushed to your Public Dropbox Folder), you can continue to use the same easy-to-remember tinyURL link to access the same file.

Projections From Above

In the world of ITP hackery, you often want to get a projector off the table and projecting on a less convenient surface than the nearest wall. Odds are, you’re going to have to build a mounting mechanism from scratch.

VJ KungFu has a nice little video about building a VJ projector mount . I used slightly different hardware, but the principles are the same. Here’s what I did:

  • look at the bottom of your projector. most projectors can accept machine screws, and specify their size.
  • attach paper securely to the bottom of the projector with tape, and use a pencil to make a small punch hole wherever you’ll be inserting a screw
  • similarly, trace the holes in your mounting bracket — you’ll see I used a pigeon plate (also called a “wall plate”). You can get them at B&H.
  • using a drill press, drill your holes slightly larger than the diameter of your machine screws.
  • use washers or bolts to get clearance between the projector and the board. You’ll need 1/4″ so that the tops of the bolts that attach the mounting plate don’t press into the bottom of the projector. Fasten, tighten, repeat.

Viola. Now you’re ready to mount your projector. If you’re mounting to a pipe, a cardellini clamp will be way more secure than the more common maffer clamps (also called “super clamps”). But if you’re clamping to lightweight aluminum poles, like the autopoles pictured, you can’t get too tight anyway without damaging pole, so you’ll have to get more creative.

I used two 40″ long arms, a few maffer clamps and a couple of 2.5″ grip heads. Setting up the autopole adjacent to the wall lets that surface take a lot of the load from the projector, which could otherwise cause the pole to bow out. Don’t try this at home.

Finally, whenever you’re mounting anything heavy above people’s heads, no matter how secure your build, it’s really important to add some kind of backup safety mechanism. I used a braided steel safety cable looped underneath the projector mounting board, and around a ventilation shaft directly above.

Eyes Wide Shut

If you could look at yourself with your eyes closed, what would you see? Usually, the only time we get to see our closed eyes is in photographs. I wondered, could we create an application that allows us to review the appearance of expressions when our eyes are closed in some kind of real-time experience?

It’s a question that I couldn’t have asked myself a year ago. The beauty of Kyle McDonald’s work, as I’ve discovered in his Appropriating New Technologies class, is that he makes sophisticated complex technologies available to new technologists and inventors, offering an otherwise inaccessible world as a sandbox in which to tinker and play.

Using Jason Saragih’s code and Kyle’s FaceTracker OpenFrameworks wrapper, I wrote about twenty lines of code. Being totally new to C++, I spent many hours just figuring out the syntax. At this point, it’s easy to view my project as just an incredibly elementary utilization of existing code — which, of course, it is. It’s also an opportunity to be grateful to be able to ask big questions, without starting from scratch.

SeeWithyourEyesClosed

How about, “iClosed”? You can view my source code here:

Mobile Web Homework #2

We were asked to play around with Firebug, a great little tool for watching changes to HTML and CSS markup languages change how webpages look on the fly. The tool is more elegant than my description suggests. Below, a before-and-after screenshot of some tomfoolery using Firebug on the PhoneGap test app code.

I also created a simple diagram of the development this app will require. For the first time, I’m realizing a minor conflict between the class’s need for transparency and documentation and my non-disclosure commitment to subway. I’ll have to discuss this with Sean, our professor. Our producer, Libby, wants the app close to done on February 20th. Talk about a tight turn-around!

In the meantime, I’m going to upload the workflow, but not the wireframes. Months from now, this post should contain both. If you stumble across this sometime in mid 2012 and it still doesn’t have the wireframes, please email me. :-)

The Burden of Dreams

Today, I collaberated with a friend to create a ‘Herzog Sleep Machine’ application: “The Burdon of Dreams”.

There’s an event at ITP called 3-in-3, which is an informal invitation for students to create one project each day. Obviously, this limits the scale, and puts the emphasis on completion, rather than the “fail-hard” attitude that sometimes typifies what we do at ITP. My goal was to work with Summer Dawn to create a complete version of what she imagined — a fully-functioning Herzog dream machine. She spent a lot of time prior to this Saturday compiling clips form Herzog’s films and interview, which she then organized on a spreadsheet and cut up into sound files and put them on disc. The programming was pretty easy — we used the minim sound library for processing, which gave us quick access to good, stereo MP3 playback.

The whole app ended up being about 100 mb. Not exactly lightweight, but it will work without an interview connection, and we exported both a PC and Mac stand-alone app.

I asked her a few questions about her process:

Summer Dawn: I don’t remember which Herzog film I saw first, but I remember going through a period about 6 years ago when I watched only Herzog, I just ate them up. It might’ve been Grizzely Man, first. And I just devoured them.I think I fell in love with his voice the first time I heard it. The rasp, the timbre. It’s like a lullaby. The low, even spacing. His particular cadence. It puts me into a trance.

Justin: I think we were talking about sleep, and dreams, and the way that the sounds around you can change what you dream about — and maybe I mentioned that I liked using a white noise machine — and you said something like, “I’ve always wanted to have a Herzog Sleep Machine”. Is that how you remember it?

Summer Dawn: For the longest time, I’ve felt that his voice would be the perfect ambient chatter in the background, like waves, or wind in the trees.

Justin: I guess we’ll really see how it “works” once you goes to sleep with it. I’m looking forward to hearing about — as Herzog might put it — ‘the dreams we share.’

FolkBox in 2012

What an intense first semester. I was honored to present a prototype of the FolkBox in the Winter Show. Folks responded very well (the people who attended, that is — although the machine performed admirably). While it was great to demo the functionality, still a lot was left to do in order to make it a fully functioning musical instrument. Mainly, the selection of chords was quite limited. In order to ensure that I had a fully functioning prototype, I limited the build to two rows, rather than three rows, of solenoids.

Over break, I’ve started to rebuild it, the way it should’ve been built from the beginning. The housing for the solenoids now uses acrylic, which makes it more rigid, and thus easier to position precisely. The weight is still an issue — this is largely due to the unnecessarily heavy metal bolts, but for now, I’ll leave it be. Once I have a “final” prototype (somehow I suspect that word will never deserve a full entrance into my vocabulary) then I can take note of the distance that the folk box needs to be positioned above the fretboard, and cut acrylic dowel to the proper size. For now, the adjustability of the bolts and nuts is worth its weight.

Getting the solenoids into the acrylic mounting plate — the layers of the housing unit — was a little tricky. The cut wasn’t right. Well, the cut was right, but it turns out that the solenoids have a bit of a “lip” that extrudes farther than the standard dimension of their cylindrical bodies. Enough to make squeezing it into brittle acrylic dicey. Very dicey. Now, I didn’t want to recut, because I wanted the housing to be very snug, in order to keep each solenoid exactly perpendicular to the freeboard — so I opted to file down the offending lip.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I also filed the inside of the holes, just a little. Finessing, frankly.

 

 

 

 

 

 

 

 

 

 

Got all of them in, finally, and assembled the unit.