Last active
December 14, 2015 01:59
-
-
Save REAS/0dc169be1ebf19a62358 to your computer and use it in GitHub Desktop.
Example code for Print, Paper, Process: Marbling and Technology at the Hammer Museum
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
PRINT PAPER PROCESS: MARBLING AND TECHNOLOGY | |
23 FEB 2013, HAMMER MUSEUM | |
UCLA ARTS SOFTWARE STUDIO <http://software.arts.ucla.edu> | |
(Based on code in Dan Shiffman's Nature of Code <http://natureofcode.com/>) | |
Operate the program with the mouse, keyboard, or modify the code. | |
Click to draw a new field | |
Click right to draw a uniform field | |
Click left to draw a scattered field | |
BASIC USE | |
Spacebar - save PDF | |
MODIFY | |
+ - increase size | |
- - descrease size | |
R - reset particles | |
D - density of field | |
GRAPHICS | |
1 - circles | |
2 - lines | |
3 - triangles | |
COLOR | |
5 - grays | |
6 - contrast | |
7 - color | |
HIDE/REVEAL | |
F - field hide | |
P - particle hide | |
*/ | |
// Add you name here | |
String name = "Your-Name-Here"; | |
// Change the number of particles | |
int numParticles = 2400; // Try up to 20000; | |
// | |
// More control over the desity of the grid | |
int gridSize = 20; | |
// These allow more control over the particle motion | |
float speedLow = 2; | |
float speedHigh = 5; | |
float forceLow = 0.1; | |
float forceHigh = 0.5; | |
// Set the initial size range of the particles | |
float minSize = 10; | |
float maxSize = 30; | |
// Change the colors for color mode (7) | |
// Click "Color Selector" from the Tools menu | |
color c1 = #8DD30D; | |
color c2 = #F0CE46; | |
color c3 = #70BDBF; | |
color c4 = #E3B0E3; | |
color c5 = #2079D1; | |
color[] colors = {c1, c2, c3, c4, c5}; | |
color backgroundColor = #FFFFFF; | |
int smoothAmount = 8; // Can be 0, 2, 4, 8 | |
// | |
import processing.pdf.*; | |
FlowField flowfield; // FlowField object | |
ArrayList<Particle> particles; // An ArrayList of Particles | |
float fieldDiff = 0.1; | |
boolean captureField = false; | |
int graphicsMode = 1; | |
boolean showField = true; | |
boolean showParticles = true; | |
int colorMode = 1; | |
float scalar = 0.3; | |
void setup() { | |
size(756, 576, P2D); | |
smooth(smoothAmount); | |
generateField(); | |
generateParticles(); | |
} | |
void generateParticles() { | |
particles = new ArrayList<Particle>(); | |
for (int i = 0; i < numParticles; i++) { | |
float rx = random(width); | |
float ry = random(height); | |
float speed = random(speedLow, speedHigh); | |
float force = random(forceLow, forceHigh); | |
particles.add(new Particle(new PVector(rx, ry), speed, force, minSize, maxSize)); | |
} | |
} | |
void generateField() { | |
flowfield = new FlowField(gridSize); | |
} | |
void draw() { | |
// Start creating the PDF | |
if (captureField == true) { | |
beginRecord(PDF, "Field-" + name + "-" + nf(frameCount, 8) + ".pdf"); | |
} | |
// Make the background a solid color | |
background(backgroundColor); | |
// Draw the vector field | |
if (showField) { | |
flowfield.display(); | |
} | |
// Tell all the Particles to follow the flow field | |
if (showParticles) { | |
for (Particle v : particles) { | |
v.follow(flowfield); | |
v.run(); | |
v.display(); | |
} | |
} | |
// Finish the PDF | |
if (captureField == true) { | |
endRecord(); | |
captureField = false; | |
} | |
} | |
// Make a new flowfield | |
void mousePressed() { | |
fieldDiff = map(mouseX, 0, width, 0.5, 0.01); | |
flowfield.init(); | |
} | |
/** | |
* | |
* FLOW FIELD | |
* | |
*/ | |
class FlowField { | |
// A flow field is a two dimensional array of PVectors | |
PVector[][] field; | |
int cols, rows; // Columns and Rows | |
int resolution; // How large is each "cell" of the flow field | |
int halfRes; | |
FlowField(int res) { | |
resolution = res; | |
halfRes = resolution/2; | |
// Determine the number of columns and rows based on sketch's width and height | |
cols = width/resolution; | |
rows = height/resolution; | |
field = new PVector[cols][rows]; | |
init(); | |
} | |
void init() { | |
// Reseed noise so we get a new flow field every time | |
noiseSeed((int)random(10000)); | |
float xoff = 0; | |
for (int i = 0; i < cols; i++) { | |
float yoff = 0; | |
for (int j = 0; j < rows; j++) { | |
float theta = map(noise(xoff, yoff), 0, 1, 0, TWO_PI); | |
// Polar to cartesian coordinate transformation to get x and y components of the vector | |
field[i][j] = new PVector(cos(theta), sin(theta)); | |
yoff += fieldDiff; | |
} | |
xoff += fieldDiff; | |
} | |
} | |
// Draw every vector | |
void display() { | |
pushMatrix(); | |
translate(resolution, resolution); | |
for (int i = 0; i < cols; i++) { | |
for (int j = 0; j < rows; j++) { | |
drawVector(field[i][j], i*resolution, j*resolution, resolution-2); | |
} | |
} | |
popMatrix(); | |
} | |
// Renders a vector object 'v' as an arrow and a location 'x,y' | |
void drawVector(PVector v, float x, float y, float scayl) { | |
pushMatrix(); | |
float arrowsize = 4; | |
// Translate to location to render vector | |
translate(x, y); | |
stroke(102); | |
strokeWeight(0.5); | |
// Call vector heading function to get direction (note that pointing up is a heading of 0) and rotate | |
rotate(v.heading2D()); | |
// Calculate length of vector & scale it to be bigger or smaller if necessary | |
float len = v.mag()*scayl*0.5; | |
// Draw three lines to make an arrow (draw pointing up since we've rotate to the proper direction) | |
line(-len, 0, len, 0); | |
//line(len,0,len-arrowsize,+arrowsize/2); | |
//line(len,0,len-arrowsize,-arrowsize/2); | |
popMatrix(); | |
} | |
PVector lookup(PVector lookup) { | |
int column = int(constrain(lookup.x/resolution, 0, cols-1)); | |
int row = int(constrain(lookup.y/resolution, 0, rows-1)); | |
return field[column][row].get(); | |
} | |
} | |
/** | |
* | |
* KEYBOARD | |
* | |
*/ | |
void keyPressed() { | |
if (key == 'd' || key == 'D') { | |
gridSize += 10; | |
if (gridSize > 50) { | |
gridSize = 10; | |
} | |
generateField(); | |
} | |
if (key == 'f' || key == 'F') { | |
showField = !showField; | |
} | |
if (key == 'p' || key == 'P') { | |
showParticles = !showParticles; | |
} | |
if (key == 'r' || key == 'R') { | |
generateParticles(); | |
} | |
if (key == ' ') { | |
captureField = true; | |
} | |
if (key == '1') { | |
graphicsMode = 1; | |
} | |
if (key == '2') { | |
graphicsMode = 2; | |
} | |
if (key == '3') { | |
graphicsMode = 3; | |
} | |
if (key == '5') { | |
colorMode = 1; | |
for (Particle v : particles) { | |
v.setColor(); | |
} | |
} | |
if (key == '6') { | |
colorMode = 2; | |
for (Particle v : particles) { | |
v.setColor(); | |
} | |
} | |
if (key == '7') { | |
colorMode = 3; | |
for (Particle v : particles) { | |
v.setColor(); | |
} | |
} | |
if (key == '=' || key == '+') { | |
scalar += 0.1; | |
} | |
if (key == '-' || key == '_') { | |
scalar -= 0.1; | |
} | |
scalar = constrain(scalar, 0.1, 2.0); | |
} | |
/** | |
* | |
* PARTICLE | |
* | |
*/ | |
class Particle { | |
// The usual stuff | |
PVector location; | |
PVector velocity; | |
PVector acceleration; | |
float r; | |
float maxforce; // Maximum steering force | |
float maxspeed; // Maximum speed | |
color c; | |
float alpha = 204; | |
Particle(PVector l, float ms, float mf, float minrad, float maxrad) { | |
location = l.get(); | |
r = random(minrad, maxrad); | |
maxspeed = ms; | |
maxforce = mf; | |
acceleration = new PVector(0, 0); | |
velocity = new PVector(0, 0); | |
setColor(); | |
} | |
void setColor() { | |
if (colorMode == 1) { | |
alpha = 204; | |
c = color(random(0, 204)); | |
} | |
else if (colorMode == 2) { | |
alpha = 255; | |
if (random(1) > 0.5) { | |
c = color(255); | |
} | |
else { | |
c = color(0); | |
} | |
} | |
else { | |
alpha = 204; | |
int rpick = int(random(5)); | |
c = colors[rpick]; | |
} | |
} | |
void run() { | |
update(); | |
borders(); | |
} | |
void display() { | |
displayParticle(); | |
} | |
// Implementing Reynolds' flow field following algorithm | |
// http://www.red3d.com/cwr/steer/FlowFollow.html | |
void follow(FlowField flow) { | |
// What is the vector at that spot in the flow field? | |
PVector desired = flow.lookup(location); | |
// Scale it up by maxspeed | |
desired.mult(maxspeed); | |
// Steering is desired minus velocity | |
PVector steer = PVector.sub(desired, velocity); | |
steer.limit(maxforce); // Limit to maximum steering force | |
applyForce(steer); | |
} | |
void applyForce(PVector force) { | |
// We could add mass here if we want A = F / M | |
acceleration.add(force); | |
} | |
// Method to update location | |
void update() { | |
// Update velocity | |
velocity.add(acceleration); | |
// Limit speed | |
velocity.limit(maxspeed); | |
location.add(velocity); | |
// Reset accelertion to 0 each cycle | |
acceleration.mult(0); | |
} | |
void displayParticle() { | |
// Draw a triangle rotated in the direction of velocity | |
float theta = velocity.heading2D() + HALF_PI; | |
float rr = r * scalar; | |
pushMatrix(); | |
translate(location.x, location.y); | |
rotate(theta); | |
if (graphicsMode == 1) { | |
stroke(c, alpha); | |
strokeCap(ROUND); | |
strokeWeight(rr); | |
point(0, 0); | |
} | |
else if (graphicsMode == 2) { | |
stroke(c, alpha); | |
strokeWeight(rr/4.0); | |
strokeCap(SQUARE); | |
stroke(c); | |
line(-rr, 0, rr, 0); | |
} | |
else if (graphicsMode == 3) { | |
noStroke(); | |
fill(c, alpha); | |
beginShape(TRIANGLES); | |
vertex(0, -rr); | |
vertex(-rr/2, rr); | |
vertex(rr/2, rr); | |
endShape(); | |
} | |
popMatrix(); | |
} | |
// Wraparound | |
void borders() { | |
if (location.x < -r) location.x = width+r; | |
if (location.y < -r) location.y = height+r; | |
if (location.x > width+r) location.x = -r; | |
if (location.y > height+r) location.y = -r; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment