## 08 Three Dimensions

Slideshow Version

# Three Dimensions

This module covers a more sophisticated means of creating complexity, through classes of objects.

Extra Credit: Flatland, A Romance of Many Dimensions, by Edwin Abbott (2nd Edition, 1884) (link)

## Assignment

There is no coding assignment this week. Instead students should prepare to describe their Final Project in detail during the next class. Every student will be required to present their ideas, direction, and beginning work.

## 3D Geometry

Most of Processing’s drawing functions have a 3-dimensional equivalent.

Functions like line() are called “overloaded” because there are two sets of parameters you could possibly pass to it. Processing knows only by the number and type of values you pass it whether to select the 2D or 3D version.
RENDERING MODE

size() does more than determine the size of the sketch. It also determines whether you’re drawing 2-dimensionally or 3-dimensionally.

Default Java2D renderer.

size( 700, 400 );
size( 700, 400, JAVA2D );

Processing’s 2D renderer.

size( 700, 400, P2D );

Processing’s 3D renderer. Enables 3D support.

size( 700, 400, P3D );

Processing’s OpenGL renderer. This requires importing the OpenGL library.

size( 700, 400, OPENGL );

## Some Three-Dimensional Graphics

Getting started in 3D is just a matter of looking at the Processing API for functions that include z values.

Just specify more coordinates for the 3D versions of each function.

line( x1, y1, z1, x2, y2, z2 );
vertex( x1, y1, z1 );

### Coordinates

In the default coordinate system, negative z-values go away from you, and positive ones go toward you.

(In physics one remembers “The Right-hand rule.” In Processing, it may be easy to reverse that…)

### Form and Shape

From this point on, the challenge becomes how to represent form through computational means, through the mathematical and logical relationships that can be established by code.

Lines and depth.

void setup(){
size(700, 400, P3D);
stroke(0, 100);
}

void draw(){
background(255);
for( int x=0; x <= width; x+=50 ){
for( int y=0; y <= height; y+=50 ){
line(x, y, 0, x, y, mouseY-height/2);
}
}
}

Generic spiral.

void setup(){
size(700, 400, P3D);
noFill();
}

void draw(){
background(255);
beginShape();
for( float r = 0; r < TWO_PI*20; r += 0.1 ){
vertex( 200*cos(r), 200*sin(r), -r*10 );
}
endShape();
}

## Translation – example 01

void setup(){
size(700, 400);
noFill();
}

void draw(){
background(255);
float f = frameCount/20.0;
//move the coordinate system to the center
translate(width/2, height/2);
//move the coordinate system to a point on a circle
rect(-15,-15,30,30);

println ("The frame count is " + frameCount + ", so I'm using f as " + f + ", and the sin is " + sin(f) );
}

## Translation – example 02 – with Classes

Classes are usually written to have their own draw() function. When paired with changes in coordinate systems, this can create a very powerful workflow. In this case, the “planet” need not know where it is, merely how to draw itself.

myRect planet;

void setup() {
size(700, 400);
noFill();
planet = new myRect(60);
}

void draw() {
background(255);
float f = frameCount/20.0;
//move the coordinate system to the center
translate(width/2, height/2);
//move the coordinate system to a point on a circle
//have the class draw itself.
planet.drawRect();

println ("The frame count is " + frameCount + ", so I'm using f as " + f + ", and the sin is " + sin(f) );
}

class myRect {
int size;
myRect(int s) {
size = s;
}
void drawRect() {
rect(-size/2, -size/2, size/2, size/2);
}
}

## Rotation – example 01

void setup() {
size(700, 400);
noFill();
}

void draw() {
background(255);
float f = frameCount/20.0;
//move the coordinate system to the center
translate(width/2, height/2);
//rotate the coordinate system to the center
rotate (f);
rect(0,0,30,30);
}

## Rotation – example 02 – with Classes

myRect planet;

void setup() {
size(700, 400);
noFill();
planet = new myRect(60);
}

void draw() {
background(255);
float f = frameCount/20.0;
//move the coordinate system to the center
translate(width/2, height/2);
//move the coordinate system to a point on a circle
//rotate the coordinate system
rotate(f);
//have the class draw itself.
planet.drawRect();
}

class myRect {
int size;
myRect(int s) {
size = s;
}
void drawRect() {
rect(-size/2, -size/2, size/2, size/2);
}
}

## Rotation – example 03 – CRAZY with Classes

Let’s draw a “planet” orbiting, with a “moon” orbiting it, and another “moon” orbiting the first:

myRect planet1;
myRect planet2;
myRect planet3;

void setup() {
size(800, 600);
noFill();
planet1 = new myRect(40);
planet2 = new myRect(20);
planet3 = new myRect(10);
}

void draw() {
background(255);
float f1 = frameCount/40.0;
float f2 = frameCount/25.0;
float f3 = frameCount/15.0;
//move the coordinate system to the center
translate(width/2, height/2);

//rotate the coordinate system
rotate(f1);
//have the class draw itself.
planet1.drawRect();

// HEY WHILE WERE HERE LET'S DRAW THE MOON!...

//rotate the coordinate system FOR A MOON
rotate(f2);
//have the class draw itself.
planet2.drawRect();

// LET'S GIVE THE MOON A MOON!...

//rotate the coordinate system FOR A MOON
rotate(f3);
//have the class draw itself.
planet3.drawRect();
}

class myRect {
int size;
myRect(int s) {
size = s;
}
void drawRect() {
rect(-size/2, -size/2, size/2, size/2);
}
}

## Rotation – example 03 – CRAZY with Classes

That was some messy code. We’re not using Classes to organize the data. Let’s try to organize this…

int numPlanets = 5;
Planet[] planets = new Planet[ numPlanets ];

void setup() {
size( 800, 800);
for (int i=0; i < numPlanets; i++) {
float r;
if(i==0){r=0;}else{r=160-(i-1)*30;}
planets[i] = new Planet( 105.0-(i*20), r );
}
}

void draw() {
background(255);
float f = frameCount/80.0;

translate(width/2, height/2);

for (int i=0; i < numPlanets; i++) {
rotate(f*i);
planets[i].drawRect();
}
}

class Planet {
float rectSize;

Planet(float s, float r) {
rectSize = s;
}
void drawRect() {
rect(-rectSize/2, -rectSize/2, rectSize/2, rectSize/2);
}
}

## Scaling – example 01

void setup() {
size(700, 500);
}

void draw() {
background(255);
rect(200,200,250,200);
scale( abs(sin(frameCount*.01) ) );
rect(200,200,250,200);
println( sin(frameCount*.01) );
}

## Scaling – example 02 – map()

The map() function comes in handy with rotate() and scale(). Let’s look at an example…

void setup() {
size(700, 500);
}

void draw() {
background(255);
rect(200,200,250,200);
scale( map(mouseX, 0, width, 0, 1) );
rect(200,200,250,200);
}

## Order Matters : Translation + Rotation

stroke(255,0,0);
rotate(-PI/4);
translate(width/2,height/2);
rect(15,15,-15,-15);

## Order Matters : Rotation + Translation

stroke(255,0,0);
translate(width/2,height/2);
rotate(-PI/4);
rect(15,15,-15,-15);

## pushMatrix() popMatrix() + 3D

void setup() {
size(700, 500, P3D);
}

void draw() {
background(255);
translate(width/2, height/2);

pushMatrix();
translate( sin(frameCount*.01)*300, 0, 0 );
rotate( sin(frameCount*.1) );
box(40);
popMatrix();

pushMatrix();
rotate( sin(frameCount*.1) );
translate( sin(frameCount*.01)*300, 0, 0 );
box(40);
popMatrix();

box(40);

}

## 3D Rotations – Phyllotaxis Example

int numDots = 400;
float ellipseSize = 12;
float rot = 0;

float c; //constant scaling factor
float n; //index number of the floret

void setup() {
size(700, 500, P3D);
c = 10;
}

void draw() {
background(255);
fill(0,0,255,150);
//ellipseSize = ellipseSize + sin(frameCount*.01)*10;
//c = c + sin(frameCount*.02)*10;

translate(width/2, height/2);
for(n=0; n < numDots; n++){
pushMatrix();

r = c * sqrt(n);
float angle = n * 137.5;

translate(r,0,0);

rotateX( sin(frameCount*.04)*2  );
ellipse(-ellipseSize, -ellipseSize, ellipseSize, ellipseSize);
popMatrix();
}

ellipseSize = 12;
c = 10;
}

Now, let’s push this pattern into 3D…

## Pyllo-3D (The Gurkin)

int numDots = 500;
float ellipseSize = 12;
float rot = 0;

float c; //constant scaling factor
float n; //index number of the floret

void setup() {
size(700, 500, P3D);
c = 10;
}

void draw() {
background(255);
fill(0,0,255,240);
c = c + sin(frameCount*.02)*10;

translate(width/4, height/2);
for(n=0; n < numDots; n++){
pushMatrix();

r = c * sqrt(n);
float angle = n * 137.5;

rotateY(PI/3);
translate(r,0,(r*r)/100);

rotateX( (frameCount*.04) );
rotateY( (frameCount*.04) );
box(10);
popMatrix();
}

ellipseSize = 12;
c = 10;
}

## 3D Rotations – Orbits (Again)

Remember the Planets from earlier? Let’s add on 3D Rotation…

int numPlanets = 6;
Planet[] planets = new Planet[ numPlanets ];

void setup() {
size( 800, 800, P3D);
for (int i=0; i < numPlanets; i++) {
float r;
if(i==0){r=0;}else{r=160-(i-1)*30;}
planets[i] = new Planet( 105.0-(i*20), r );
}
}

void draw() {
background(250);
float f = frameCount/80.0;

translate(width/2, height/2);

for (int i=0; i < numPlanets; i++) {
rotate(f*i);
rotateY(f*i); // THIS IS THE ONLY REAL CHANGE!
planets[i].drawBox();
}
}

class Planet {
float boxSize;