//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Assignment-07a-Bar Chart
// prevent scrolling of page
// from "Single Touch with P5js" on coursescript.com
// http://coursescript.com/notes/interactivecomputing/mobile/touch/
document.ontouchmove = function(event){
event.preventDefault();
}
/////////////////////////////////
// INIT VARIABLES
/////////////////////////////////
//MODEL
var mode;
var selection;
//temp node
var tempNodeX, tempNodeY;
var tempNodeContent = "";
//data
var nodes = [];
var links = [];
//VIEW
//canvas vars
var canvasWidth, canvasHeight;
//colors
var colorBG, colorNode, colorSelection, colorLink;
//font
var font, fontSize;
/////////////////////////////////
// NODE CLASS
/////////////////////////////////
//CREATE NEW NODE, called when user clicks the canvas
function newNode(xIn, yIn) {
//update app state
mode = "GETTING NODE CONTENT";
//store temporary node location
tempNodeX = xIn;
tempNodeY = yIn;
tempNodeContent = "";
}
function saveNewNode() {
println("SAVE NEW NODE!");
//get bounding box for node
var bounds = fontRegular.textBounds(
tempNodeContent, tempNodeX, tempNodeY, fontSize);
var offset = 5;
//create new node instance using temp data
var newNode = {x: tempNodeX,
y: tempNodeY,
content: tempNodeContent,
left: bounds.x - offset,
right: bounds.x + bounds.w + offset,
top: bounds.y - offset,
bottom: bounds.y + bounds.h + offset,
move: moveNode,
draw: drawNode};
//store the new node
nodes.push(newNode);
//update app state
mode = "MAP";
}
function abortNewNode() {
println("ABORT NEW NODE!");
tempNodeContent = "";
mode = "MAP";
}
//MOVE SPECIFIED NODE
function moveNode(dx,dy) {
this.x += dx;
this.y += dy;
this.left += dx;
this.top += dy;
this.right += dx;
this.bottom += dy;
}
//DRAW SPECIFIED NODE
function drawNode() {
if (this == selection) {
//draw node border
setDrawingSettings("NODE BORDER SELECTION");
rect(this.left,this.top,this.right,this.bottom);
//draw node text
setDrawingSettings("NODE TEXT SELECTION");
text(this.content,this.x,this.y);
}
else {
//draw node border
setDrawingSettings("NODE BORDER");
rect(this.left,this.top,this.right,this.bottom);
//draw node text
setDrawingSettings("NODE TEXT");
text(this.content,this.x,this.y);
}
}
/////////////////////////////////
// LINK CLASS
/////////////////////////////////
//CREATE LINK BETWEEN TWO NODES
function newLink(fromIn, toIn) {
var newLink = {from: fromIn,
to: toIn,
draw: drawLink};
links.push(newLink);
}
function drawLink() {
setDrawingSettings("LINK");
line(this.from.x, this.from.y, this.to.x, this.to.y);
}
/////////////////////////////////
// VIEW
/////////////////////////////////
function setDrawingSettings(setting) {
if (setting == "NODE TEXT") {
noStroke();
fill(colorNode);
textAlign(CENTER);
}
else if (setting == "NODE TEXT SELECTION") {
noStroke();
fill(colorSelection);
textAlign(CENTER);
}
else if (setting == "NODE BORDER") {
stroke(colorNode);
fill(colorBG);
}
else if (setting == "NODE BORDER SELECTION") {
stroke(colorSelection);
fill(colorBG);
}
else if (setting == "LINK") {
stroke(colorLink);
noFill();
}
}
//DRAW TEMPORARY NODE WHILE EDITING
function drawTempNode() {
//drawing settings
setDrawingSettings("NODE TEXT SELECTION");
//draw text
text(tempNodeContent, tempNodeX, tempNodeY);
}
//DRAW ALL NODES IN MAP
function drawAllNodes() {
for (i=0; i<nodes.length; i++) {
node = nodes[i];
node.draw();
}
}
function drawAllLinks() {
for (i=0; i<links.length; i++) {
link = links[i];
link.draw();
}
}
/////////////////////////////////
// CONTROLLER
/////////////////////////////////
//CHECK CLICK IS ON EXISTING NODE, RETURN THAT NODE
function clickIsNode() {
//for each existing node...
for (i=0; i<nodes.length; i++) {
var node = nodes[i];
//check if click was within the node bounding box
if ((node.left <= mouseX) & (mouseX <= node.right) &&
(node.top <= mouseY) && (mouseY <= node.bottom)) {
//return the node object
return node;
}
}
return false;
}
function clickIsCanvas() {
if ((mouseX > 0) & (mouseX < width) &&
(mouseY > 0) && (mouseY < height)) {
return true;
}
else return false;
}
function mousePressedMap() {
//if node is already selected...
if (selection) {
//if user clicks the blank canvas, deselect
if (! clickIsNode()) {
println("DESELECT!");
selection = null;
}
//else if user clicks another node, create link
else if (clickIsNode()) {
if (keyCode == 16) { //shift-click another node, create link
println("NEW LINK!");
var secondSelection = clickIsNode();
newLink(selection,secondSelection);
//clear selection
selection = null;
}
else selection = clickIsNode();
}
}
//if nothing selected...
else {
//if user clicks an existing node, select that node
if (clickIsNode()) {
println("NODE SELECTED!");
selection = clickIsNode(); // <-- figure out a more efficient method...
}
else if (! clickIsNode()) {
//initialize new node
println("NEW NODE!");
newNode(mouseX,mouseY);
}
}
}
function mousePressed() {
if (mode == "MAP") {
mousePressedMap();
}
}
function mouseDraggedMap() {
//if user is dragging a selected node
if (selection) {
//get drag data
var dx = mouseX - pmouseX;
var dy = mouseY - pmouseY;
//move selected node
selection.move(dx,dy);
}
}
function mouseDragged() {
if (mode == "MAP") {
mouseDraggedMap();
}
}
function keyPressedMap() {
if (keyCode == 27) selection = null;
}
function keyPressedGettingNodeContent() {
//"ENTER", save new node
if (keyCode == 13) saveNewNode();
//"ESCAPE", abort new node
else if (keyCode === 27) abortNewNode();
//"BACKSPACE", delete previous character
else if (keyCode === 8) {
tempNodeContent =
tempNodeContent.substring(0, tempNodeContent.length-1);
}
//Other character...
else tempNodeContent += key;
}
function keyPressed() {
println("key = " + key);
println("keyCode = " + keyCode);
if (mode == "MAP") {
keyPressedMap();
}
else if (mode == "GETTING NODE CONTENT") {
keyPressedGettingNodeContent();
}
}
/////////////////////////////////
// RUN!
/////////////////////////////////
function preload() {
fontRegular = loadFont('cour.ttf');
fontBold = loadFont('courbd.ttf');
fontItalic = loadFont('couri.ttf');
fontBoldItalic = loadFont('courbi.ttf');
}
function setup() {
////////// INIT VARS //////////
// MODEL
mode = "MAP"; // initial app state
selection = null;
//canvas
canvasWidth = canvasHeight = 400;
//font
fontSize = 12;
//color
colorBG = color(255,255,255);
colorNode = color(0,0,0);
colorSelection = color(255,127,0);
colorLink = colorNode;
//DRAWING SETTINGS
rectMode(CORNERS);
textFont(fontBold);
// CANVAS SETUP
createCanvas(canvasWidth, canvasHeight);
}
function draw() {
background(colorBG); //update background
//draw nodes
drawAllLinks();
drawAllNodes();
if (mode == "GETTING NODE CONTENT") {
drawTempNode();
}
}
My final project was an attempt to make a digital mind-mapping interface. A mind-map is a tool for thinking through complex problems in which the user draws nodes which represent ideas and then draws connections between those nodes to develop complex relationships. A digital mind-mapping interface would transcend the traditional limits of the paper medium.
Unfortunately, the sketch uploaded to WordPress can’t run because I have been unable to find a way to load a font, which I need in order to use the textBounds() function for click detection.
When it’s working locally on my computer, the user can click or touch the screen to begin creating a node. The user can type in text for the node, then press enter to finalize the node. The user can continue to do this to create multiple nodes. If the user clicks an existing node, that node becomes selected. The user can click and drag a node to move it on the canvas. If the user has one node selected and shift-clicks another node, a link is created between those nodes. As the user moves nodes that are linked, the link is preserved.
To be honest, I haven’t been able to put a lot of time into this project, so this is where it’s at. I also developed a touch-screen drawing interface. I was hoping to integrate the two sketches into a mind-mapping interface involving touch-face drawing. Here is that sketch:
//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Final Project - Sketch - 2016/11/26
// prevent scrolling of page
// from "Single Touch with P5js" on coursescript.com
// http://coursescript.com/notes/interactivecomputing/mobile/touch/
document.ontouchmove = function(event){
event.preventDefault();
}
///////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
///////////////////////////////////////////////////////////////////////////////
//MODEL VARIABLES
var mode; //the current mode. possible values: "MAP", "NODE EDIT"
var currentSelection; //the currently selected object. null if nothing selected
var OX, OY; //x and y location of the map origin, relative to 0,0
var nodes = []; //array to store all node instances
var links = []; //array to store all link instances
var currentScribble;
//EVENT VARIABLES
var timeOfLastClick, //time in milliseconds of the previous mouse click
doubleClickThreshold; //time in milliseconds allowed for double-click
///////////////////////////////////////////////////////////////////////////////
// TEXT NODE CLASS
///////////////////////////////////////////////////////////////////////////////
function textNodeDraw() {
return;
}
function textNodeUpdateVPLocation() {
println("textNodeUpdateVPLocation()")
}
///////////////////////////////////////////////////////////////////////////////
// SCRIBBLE NODE CLASS
///////////////////////////////////////////////////////////////////////////////
function scribbleNodeAddPoint(x,y) {
println(this.ptsX);
this.ptsX.push(x);
this.ptsY.push(y);
}
function scribbleNodeDraw() {
stroke(255);
beginShape();
for (var i=0; i<this.ptsX.length; i++) {
var x = this.ptsX[i];
var y = this.ptsY[i];
vertex(x,y);
}
endShape();
}
function scribbleNodeUpdateVPLocation(dx,dy) {
//update drawing vertices relative to map origin
for (var i=0; i<this.ptsX.length; i++) {
this.ptsX[i] += dx;
this.ptsY[i] += dy;
}
}
///////////////////////////////////////////////////////////////////////////////
// MODEL HELPER FNS
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//NODES
function newNode(type,x,y) {
println("New " + type + " node!");
var newNode;
if (type == "TEXT") newNode = newTextNode(x,y);
else if (type == "SCRIBBLE") newNode = newScribbleNode(x,y);
//store new node in array of existing nodes
nodes.push(newNode);
//return new node
return newNode;
}
function newTextNode(xIn,yIn) {
println("Creating new TEXT node...");
//create new text node instance
var textNode = {x: xIn, //x pos of node relative to map origin
y: yIn, //y pos of node relative to map origin
type: "TEXT", //node type
updateVPLocation: textNodeUpdateVPLocation, //fn for update
draw: textNodeDraw}; //fn to draw node
return textNode;
}
function newScribbleNode(xIn,yIn) {
println("Creating new SCRIBBLE node...");
//create new scribble node instance
var scribbleNode = {x: xIn,
y: yIn,
type: "SCRIBBLE",
ptsX: [xIn],
ptsY: [yIn],
addPoint: scribbleNodeAddPoint,
updateVPLocation: scribbleNodeUpdateVPLocation,
draw: scribbleNodeDraw};
return scribbleNode;
}
//-----------------------------------------------------------------------------
//VIEW
function pan(dx,dy) {
//update node locations
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
node.updateVPLocation(dx,dy);
}
}
///////////////////////////////////////////////////////////////////////////////
// DRAW HELPER FNS
///////////////////////////////////////////////////////////////////////////////
function drawAllNodes() {
noFill();
strokeWeight(3);
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
node.draw();
}
}
///////////////////////////////////////////////////////////////////////////////
// EVENTS
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//TOUCH STARTED
//called when user touches the screen within the javascript canvas
function touchStarted() {
println("Touch!");
println(touches);
if (mode == "MAP") touchStartedMap();
else if (mode == "NODE EDIT") touchStartedNodeEdit();
//Important! Returning false prevents mousePressed() from also firing,
//which would confuse the double-click functionality.
return false;
}
//called when user touches the screen while in the MAP mode
function touchStartedMap() {
var timeOfCurrentTouch = millis()
var timeSinceLastTouch = timeOfCurrentTouch- timeOfLastClick;
//if user touches blank space on canvas
//if single-tap, DESELECT
if ((timeSinceLastTouch > doubleClickThreshold) &
(touches.length < 2)) {
//deselect
currentSelection = null;
}
//else if double-tap, NEW TEXT NODE
else if ((timeSinceLastTouch <= doubleClickThreshold) &
(touches.length < 2)) {
//create new text node
newNode("TEXT",touchX,touchY);
}
//else if user touches an existing node
//store time of current click
timeOfLastClick = timeOfCurrentTouch;
}
//-----------------------------------------------------------------------------
//TOUCH MOVED
//called when user touches and drags their finger
function touchMoved() {
if (mode == "MAP") touchMovedMap();
// else if (mode == "NODE EDIT") touchMovedNodeEdit();
}
//called when user touches and drags their finger while in MAP mode
function touchMovedMap() {
//if single finger drag, SCRIBBLE
if (touches.length == 1) {
//if not already scribbling, NEW SCRIBBLE NODE
if (currentScribble == null) {
currentScribble = newNode("SCRIBBLE",touchX,touchY);
}
//else if already scribbling, ADD POINT to current scribble
else currentScribble.addPoint(touchX,touchY);
}
//else if two finger drag, PAN
else if (touches.length == 2) {
println("Pan!")
pan(touchX-ptouchX, touchY-ptouchY);
}
}
//-----------------------------------------------------------------------------
//TOUCH ENDED
//called when user releases his/her finger from the screen
function touchEnded() {
if (mode == "MAP") touchEndedMap();
// else if (mode == "NODE EDIT") touchEndedNodeEdit();
}
//called when user realeases his/her finger from the screen while in map mode
function touchEndedMap() {
//if user is drawing scribble, stop scribbling
if (currentScribble != null) {
currentScribble = null;
}
}
///////////////////////////////////////////////////////////////////////////////
// RUN!
///////////////////////////////////////////////////////////////////////////////
//function used to initialize the variables
function initVars() {
// INIT VARS
timeOfLastClick = 0;
doubleClickThreshold = 200;
currentScribble = null;
//SET MODEL STATE
mode = "MAP";
//Canvas
canvasWidth = canvasHeight = 400;
}
//function used to initialize the canvas
function initCanvas() {
//create canvas
createCanvas(canvasWidth, canvasHeight);
}
function setup() {
initVars();
initCanvas();
}
function draw() {
background(0); //update background
drawAllNodes();
}
]]>//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Assignment-07a-Bar Chart
/////////////////////////////////
// INIT VARIABLES
/////////////////////////////////
//canvas vars
var canvasWidth, canvasHeight;
//INPUT IMAGE
//option 1:
//var imgUrl = "http://i.imgur.com/b24JQcX.jpg?1"; // url for the input image
//option 2:
var imgUrl = "http://i.imgur.com/koPvfoe.jpg"; // url for the input image
var img; //the input image object
//2d list of booleans tracking which pixels have been visited
//visited = true; not visited = false
var visitedPixels = [];
//
var turtles = []; //store all turtle instances in list
var newTurtles = []; //store turtles to add on next frame
var deadTurtles = []; //store turtles to remove on next frame
var colorTurtle, colorBackground;
//
var darknessThreshold, brightnessThreshold;
var maxNumberOfTurtles;
/////////////////////////////////
// RUN!
/////////////////////////////////
function preload() {
img = loadImage(imgUrl);
}
function setup() {
// INIT VARS
//set canvas size based on imput image
canvasWidth = 800;
canvasHeight = 800;
//set control variables
darknessThreshold = 50;
brightnessThreshold = 70;
maxNumberOfTurtles = 25;
//set colors
colorTurtle = color(255);
colorBackground = color(0);
//populate visited pixels list such that it is a 2d list of booleans
//representing each pixel in the canvas. false means that the pixel
//has not been visited by turtles yet.
for (var y = 0; y < canvasHeight; y++) {
//create list to store row of values
var row = [];
//populate row list with all falses for n columns
for (var x = 0; x < canvasWidth; x++) {
row.push(false);
}
//add row list to 2d visitedPixels array
visitedPixels.push(row);
}
// CANVAS SETUP
createCanvas(canvasWidth, canvasHeight);
background(colorBackground);
}
function draw() {
//if program termination condition hasn't been met...
if (fin() != true) {
//for each turtle in list of active turtles...
for (var i=0; i<turtles.length; i++) {
//get turtle object
var turtle = turtles[i];
//walk
walkTurtle(turtle);
}
}
//add new turtles to list of all turtles for next generation
updateTurtlesList();
}
/////////////////////////////////
// HELPER FNS
/////////////////////////////////
function walkTurtle(turtle) {
//start drawing
turtle.penDown();
//move forward 1 pixel
turtle.forward(1);
//check if pixel visited already, continue if unvisited
if (isValidStep(turtle)) {
var x = int(turtle.x); //sometimes these values become floats...
var y = int(turtle.y); //so use int() to ensure integer value
//determine turtle's fate based on pixel brightness:
var pix = img.get(x,y);
//if pixel too dark, turtle dies
if (brightness(pix) > darknessThreshold) {
// println("Turtle die!");
killTurtle(turtle);
}
//else if pixel bright enough, spawn children turtles at location
else if (brightness(pix) > brightnessThreshold) {
println("Spawn!")
spawnTurtle(x, y, 90); //spawn turtle to left
spawnTurtle(x, y, -90); //spawn turtle to right
}
else {
}
}
//else, if pixel is visited already, erase. Then, respawn elsewhere
else {
// println("Respanw!");
//move backward 1 pixel
turtle.back(1);
//respawn somewhere else
respawnTurtle(turtle);
}
}
function isValidStep(turtle) {
//get x and y pixel position of turtle
var x = int(turtle.x); //sometimes these values become floats...
var y = int(turtle.y); //so use int() to ensure integer value
//if turtle off of canvas
if ((x <= 0) || (x >= canvasWidth) || (y <= 0) || (y >= canvasHeight)) {
return false;
}
//if pixel already visited, step is not valid. return false
else if (visitedPixels[x][y] == true) {return false;}
//if pixel not visited, step is valid. return true
else {
//update visited pixels
visitedPixels[x][y] = true;
return true;
}
}
function spawnTurtle(x,y,dir) {
if (turtles.length < maxNumberOfTurtles) {
//create new turtle instance
var turtle = makeTurtle(x,y);
//rotate turtle based on input dir
turtle.left(dir);
//set turtle color
turtle.color = colorTurtle;
//store turtle in list of turtles to be added to master list
newTurtles.push(turtle);
}
}
function respawnTurtle(turtle) {
//get random x and y pixel coordinates
var x = int(random(canvasWidth));
var y = int(random(canvasHeight));
var theta = 90*(int(random(4)));
//move turtle to new pixel
turtle.penUp();
turtle.goto(x,y);
turtle.penDown();
//turn turtle
turtle.left(theta);
//if move is to a cell that hasn't been visit, continue
if (isValidStep(turtle)) {
//update visited pixels
visitedPixels[x][y] = true;
//exit function
return;
}
//if move is to cell that has already been visited, try again
else {respawnTurtle(turtle);}
}
//function to remove turtle from list of active turtles
function killTurtle(turtle) {
deadTurtles.push(turtle);
// //new list to store all active turtles
// var newTurtlesList = [];
// //get index of turtle to remove from list of turtles
// var indexOfTurtleToRemove = turtles.indexOf(turtle);
// println(indexOfTurtleToRemove ==
// //loop over list of turtles, build new list, excluding newly dead turtle
// for (var i=0; i<turtles.length; i++) {
// //if turtle is not the one we are killing, add it to the list
// if (i != indexOfTurtleToRemove) {newTurtlesList.push(turtles[i]);}
// }
// //update turtles list
// turtles = newTurtlesList;
}
//function for adding newly spawned turned to list of active turtles
function updateTurtlesList(){
var newTurtlesList = [];
for (var i=0; i<turtles.length; i++) {
if ((deadTurtles.indexOf(turtles[i])) == -1) {
newTurtlesList.push(turtles[i]);
}
}
//for each new turtle spawned in past generation...
for (var j=0; j<newTurtles.length; j++) {
//add new turtle to list of active turtles
newTurtlesList.push(newTurtles[j]);
}
turtles = newTurtlesList;
}
// function for ending the program if all cells have been visited
function fin() {
//check if cells have been visited or not
for (var x=0; x<visitedPixels.length; x++) {
for (var y=0; y<visitedPixels[0].length; y++) {
//if any cell is found unvisited, return false to continue program
if (visitedPixels[x][y] == false) {
return false;
}
}
}
//if get through all cells and all have been visited, finish
return true;
}
/////////////////////////////////
// EVENTS
/////////////////////////////////
function mousePressed() {
//spawn new turtle at mouse x and y with random N/S/E/W direction
spawnTurtle(int(mouseX),int(mouseY),90*(int(random(4))));
}
/////////////////////////////////
// TURTLE CLASS
/////////////////////////////////
function turtleLeft(d){this.angle-=d;}function turtleRight(d){this.angle+=d;}
function turtleForward(p){var rad=radians(this.angle);var newx=this.x+cos(rad)*p;
var newy=this.y+sin(rad)*p;this.goto(newx,newy);}function turtleBack(p){
this.forward(-p);}function turtlePenDown(){this.penIsDown=true;}
function turtlePenUp(){this.penIsDown = false;}function turtleGoTo(x,y){
if(this.penIsDown){stroke(this.color);strokeWeight(this.weight);
line(this.x,this.y,x,y);}this.x = x;this.y = y;}function turtleDistTo(x,y){
return sqrt(sq(this.x-x)+sq(this.y-y));}function turtleAngleTo(x,y){
var absAngle=degrees(atan2(y-this.y,x-this.x));
var angle=((absAngle-this.angle)+360)%360.0;return angle;}
function turtleTurnToward(x,y,d){var angle = this.angleTo(x,y);if(angle< 180){
this.angle+=d;}else{this.angle-=d;}}function turtleSetColor(c){this.color=c;}
function turtleSetWeight(w){this.weight=w;}function turtleFace(angle){
this.angle = angle;}function makeTurtle(tx,ty){var turtle={x:tx,y:ty,
angle:0.0,penIsDown:true,color:color(128),weight:1,left:turtleLeft,
right:turtleRight,forward:turtleForward, back:turtleBack,penDown:turtlePenDown,
penUp:turtlePenUp,goto:turtleGoTo, angleto:turtleAngleTo,
turnToward:turtleTurnToward,distanceTo:turtleDistTo, angleTo:turtleAngleTo,
setColor:turtleSetColor, setWeight:turtleSetWeight,face:turtleFace};
return turtle;}
In this sketch, I was trying to have a series of turtles walk about an image and respond based on the brightness of the pixels. The rules I was attempting to employ:
What I was able to achieve creates an interesting composition; however, I don’t think it is reacting to the image as I intended.
]]>//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Assignment-07a-Bar Chart
/////////////////////////////////
// INIT VARIABLES
/////////////////////////////////
//canvas vars
var canvasWidth, canvasHeight;
//artwork vars
var horizon;
var landscapes = []; //array to store landscape objects
//cactus vars
var minCactusSize, maxCactusSize;
var minCactusSpeed, maxCactusSpeed;
var minCactusY, maxCactusY;
var cacti = []; //array to store cactus objects
//colors
var cactusFill, cactusStroke;
var skyColor, skyColor1, skyColor2, skyFaderVal, skyFaderSpeed;
/////////////////////////////////
// HELPER FNS
/////////////////////////////////
function drawMovingLandscape() {
//draw backdrop
for (var i=0; i<landscapes.length; i++) landscapes[i].draw();
//draw cacti
for (var j=0; j<cacti.length; j++) cacti[j].draw();
}
function moveCacti() {
//move each cactus in stored list of cacti
for (var i=0; i<cacti.length; i++) cacti[i].move();
}
function removeCactiOutOfView() {
var cactiToKeep = [];
for (var i=0; i<cacti.length; i++) {
if (cacti[i].x + cacti[i].size/2 > 0) {
cactiToKeep.push(cacti[i]);
}
}
cacti = cactiToKeep;
}
// function adapted from Stack Overflow thread
// http://stackoverflow.com/questions/12788051/
// how-to-sort-an-associative-array-by-value
function sortArray(a) {
a.sort(function(a,b) {
return a.y - b.y;
})
}
function updateSkyColor() {
//update sky fader val
skyFaderVal = ((millis()*skyFaderSpeed)%1000)/1000;
if (Math.floor(millis()/1000*skyFaderSpeed)%2 == 0) {
skyColor = lerpColor(skyColor1, skyColor2, skyFaderVal);
}
else {
skyColor = lerpColor(skyColor2, skyColor1, skyFaderVal);
}
}
/////////////////////////////////
// LANDSCAPE CLASS
/////////////////////////////////
function makeLandscape(inCY, inRange, inSpeed, inDetail, inColor, inStroke) {
landscape = {cy: inCY,
range: inRange,
speed: inSpeed,
detail: inDetail,
c: inColor,
s: inStroke,
draw: drawLandscape
}
landscapes.push(landscape);
}
function drawLandscape() {
// Landscape generator adapted from code provided by 15-104 staff
fill(this.c);
stroke(this.s)
beginShape();
vertex(0,canvasHeight);
for (var x=0; x<canvasWidth; x++) {
var t = (x*this.detail) + (millis()*this.speed);
var y = map(noise(t), 0, 1, this.cy-this.range/2, this.cy+this.range/2);
vertex(x,y);
}
vertex(canvasWidth, canvasHeight);
endShape(CLOSE);
}
/////////////////////////////////
// CACTUS CLASS
/////////////////////////////////
function makeCactus(scale) {
//get properties
inSize = map(scale, 0, 1, maxCactusSize, minCactusSize);
inX = canvasWidth + 100 + inSize;
inY = map(scale, 0, 1, maxCactusY, minCactusY);
inSpeed = map(scale, 0, 1, maxCactusSpeed, minCactusSpeed);
//create cactus instance
cactus = {x: inX,
y: inY,
size: inSize,
speed: inSpeed,
draw: drawCactus,
move: moveCactus
}
//store new cactus in cacti array
cacti.push(cactus);
}
function moveCactus() {
this.x -= this.speed;
}
function drawCactus() {
//set drawing properties
// fill(-1);
stroke(cactusStroke);
//draw trunk
beginShape();
vertex(
this.x-this.size*0.1,
this.y);
vertex(
this.x-this.size*0.1,
this.y-this.size*0.5);
vertex(
this.x-this.size*0.4,
this.y-this.size*0.5);
vertex(
this.x-this.size*0.4,
this.y-this.size*0.8);
vertex(
this.x-this.size*0.25,
this.y-this.size*0.8);
vertex(
this.x-this.size*0.25,
this.y-this.size*0.65);
vertex(
this.x-this.size*0.1,
this.y-this.size*0.65);
vertex(
this.x-this.size*0.1,
this.y-this.size*1.0);
vertex(
this.x+this.size*0.1,
this.y-this.size*1.0);
vertex(
this.x+this.size*0.1,
this.y-this.size*0.5);
vertex(
this.x+this.size*0.2,
this.y-this.size*0.5);
vertex(
this.x+this.size*0.2,
this.y-this.size*0.75);
vertex(
this.x+this.size*0.35,
this.y-this.size*0.75);
vertex(
this.x+this.size*0.35,
this.y-this.size*0.35);
vertex(
this.x+this.size*0.1,
this.y-this.size*0.35);
vertex(
this.x+this.size*0.1,
this.y);
endShape(CLOSE);
}
/////////////////////////////////
// EVENTS
/////////////////////////////////
/////////////////////////////////
// RUN!
/////////////////////////////////
function setup() {
// INIT VARS
//canvas
canvasWidth = 600;
canvasHeight = 400;
//artwork
horizon = 250;
//cactus vars
minCactusSize = 5;
maxCactusSize = 100;
minCactusSpeed = 1;
maxCactusSpeed = 4;
minCactusY = horizon+5;
maxCactusY = canvasHeight+10;
//colors
cactusStroke = 255;
skyColor1 = color(218, 165, 32);
skyColor2 = color(72, 61, 139);
skyColor = skyColor1;
skyFaderVal = 0;
skyFaderSpeed = .1;
// CANVAS SETUP
createCanvas(canvasWidth, canvasHeight);
//INIT OBJECTS
//far mountains
makeLandscape(50, 100, 0.0001, .01, color(0), color(255));
//close mountains
makeLandscape(125, 75, 0.0002, .009, color(0), color(255));
//ground
makeLandscape(horizon, 10, 0.0003, .005, color(0), color(255));
//test cactus
makeCactus(random(0,canvasHeight-horizon));
}
function draw() {
background(skyColor); //update background
//UPDATE MODEL
// new cactus 2% of the time
if (random(0,1) < 0.02) {
//new cactus with random distance from bottom of canvas
makeCactus(random(0,1));
}
//move cacti
moveCacti();
//remove
removeCactiOutOfView();
//sort cacti by distance from view
sortArray(cacti);
//update sky color
updateSkyColor();
//DRAW THE LANDSCAPE
drawMovingLandscape();
}
]]>//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Assignment-07-Project-Composition with Curves
/////////////////////////////////
// DECLARE VARIABLES
/////////////////////////////////
//canvas vars
var canvasWidth, canvasHeight, nRows, nCols, cellSizeX, cellSizeY;
//event vars
var mx, my; //constrained mouseX and mouseY
//curve vars
var nPts, scaleFactor, falloffFactor;
//color
/////////////////////////////////
// HELPER FNS
/////////////////////////////////
function drawConchoidOfDeSluzeCurve(cx, cy, a) {
//Conchoid of de Sluze
//http://mathworld.wolfram.com/ConchoidofdeSluze.html
var x, y;
var scaleFactor = map(my, 0, canvasHeight, 1, 10);
noFill();
stroke(255);
beginShape();
for (var i = 0; i < nPts; i++) {
var t = map(i, 0, nPts, 0, TWO_PI);
// compute raw curve vals
x = (1/cos(t) + a*cos(t)) * cos(t); // x = (sec t + a cos t) cos t
y = (1/cos(t) + a*cos(t)) * sin(t); // x = (sec t + a cos t) sin t
// scale vals
x *= scaleFactor;
y *= scaleFactor;
// position in canvas
x += cx;
y += cy;
// create vertex
vertex(x, y);
}
endShape();
}
/////////////////////////////////
// EVENTS
/////////////////////////////////
/////////////////////////////////
// RUN!
/////////////////////////////////
function setup() {
// INIT VARS
nPts = 75;
falloffFactor = 0.6;
//canvas
canvasWidth = canvasHeight = 640;
nRows = 4;
nCols = 4;
cellSizeX = canvasWidth/nCols;
cellSizeY = canvasHeight/nRows;
// CANVAS SETUP
createCanvas(canvasWidth, canvasHeight);
}
function draw() {
background(0); //update background
//constrain mouse position to canvas
mx = constrain(mouseX, 0, canvasWidth);
my = constrain(mouseY, 0, canvasHeight);
//get a value of outer-most curve
var aMax = map(mx, 0, canvasWidth, -50, 50);
//create grid
for (row = 0; row < nRows; row++) {
for (col = 0; col < nCols; col++) {
var cx = col*cellSizeX + cellSizeX/2;
var cy = row*cellSizeY + cellSizeY/2;
//create nested Conchoid of de Sluze curves at grid pt
for (a = aMax; abs(a) > 1; a *= falloffFactor) {
//draw curve centered at pt
drawConchoidOfDeSluzeCurve(cx, cy, a);
}
}
}
}
I chose one of the curves off of the Wolfram web site and used the mouse position to manipulate the scale of the curve and the order, or “a” variable, of the curve. For loops is are used to create the nested curves of different orders, as well as to create the overall array of curves for the composition.
]]>//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Project-06-Abstract Clock
//In this clock, the hour is represented by the greyscale tone of the background,
//the minute is represented by the rotation of the line, and the second is
//represented by the inflection of the line which is a 6-bit binary representation
//of the integer value of the second.
/////////////////////////////////
// GLOBAL VARS
/////////////////////////////////
//canvas
var canvasWidth, canvasHeight;
//time
var h, m, s;
//geometry
var x = []; //x pts of line
var y = []; //y pts of line
var nBits, ptsPerBit, nPts, ptSpacing;
var bitVal, bit0Amplitude, bit1Amplitude;
var rotationAngle;
var seeTime;
var mBallSize, sBallSize, cBallSize;
//color
var cBackground, cStroke;
/////////////////////////////////
// HELPER FNS
/////////////////////////////////
function drawWave() {
for (var i = 0; i < x.length-1; i++) {
stroke(cStroke);
push(); //rotation for minute
translate(canvasWidth/2, canvasHeight/2); //translate to origin
rotate(rotationAngle); //rotate based on current minute
translate(-canvasWidth/2, -canvasHeight/2); //translate back to position.
line(x[i], y[i], x[i+1], y[i+1]); //draw line
pop();
}
}
function drawSBall() {
var armLength = canvasWidth/4-sBallSize/2;
var theta = s*(TWO_PI/60) - HALF_PI;
var cx = canvasWidth/2 + armLength*cos(theta);
var cy = canvasHeight/2 + armLength*sin(theta);
stroke(0);
noFill();
ellipse(cx, cy, sBallSize);
}
function drawMBall() {
var armLength = canvasWidth/2-mBallSize/2;
var theta = rotationAngle;
var cx = canvasWidth/2 + armLength*cos(theta);
var cy = canvasHeight/2 + armLength*sin(theta);
noStroke();
fill(0);
ellipse(cx, cy, mBallSize);
}
function drawCBall() {
noStroke();
fill(0);
ellipse(canvasWidth/2, canvasHeight/2, cBallSize);
}
function drawTime() {
var h = hour()%12;
var m = minute();
var s = second();
noStroke();
fill(cStroke);
text("" + nf(h,2,0) + ":" + nf(m,2,0) + ":" + nf(s,2,0), canvasWidth/2, 3*canvasHeight/4);
}
//adapted from fernandosavio's function on Stack Overflow
//http://stackoverflow.com/questions/9939760/how-do-i-convert-an-integer-to-binary-in-javascript
function getBinString(n){
//return binary string for input number with specified number of digits
return nf((n >>> 0).toString(2), nBits);
}
function updateTime() {
h = hour();
m = minute();
s = second();
}
function updatePts() {
//clean pts lists
x = [];
y = [];
//get binary string for seconds count
binString = getBinString(s); //convert second to binary, return string
//get x and y values for pts
for (i = 0; i < nBits; i++) { //for each bit
bitVal = int(binString[i]);
for (j = 0; j < ptsPerBit; j++) { //for each pt in bit
x.push(i*ptsPerBit*ptSpacing+j*ptSpacing); //x value
if (bitVal == 0) {
yMax = bit0Amplitude;
yMin = -bit0Amplitude;
}
else {
yMax = bit1Amplitude;
yMin = -bit1Amplitude;
}
y.push(canvasHeight/2 + random(yMax, yMin)) //y value
}
}
}
function updateRotation() {
rotationAngle = m*(TWO_PI/60) + HALF_PI;
}
function updateColors() {
cStroke = color(h*(255/24)); // greyscale tone, hr0 = 0, hr24 = 255
cBackground = color(255-h*(255/24)); //greyscale tone, hr0 = 255, hr24 = 0
}
function updateModel() {
updateTime();
updatePts();
updateRotation();
updateColors();
}
/////////////////////////////////
// EVENTS
/////////////////////////////////
function keyPressed() {
if (seeTime == false) seeTime = true;
else seeTime = false;
}
/////////////////////////////////
// RUN
/////////////////////////////////
function setup() {
// INIT VARS
//canvas
canvasWidth = canvasHeight = 400;
//time
h = hour();
m = minute();
s = second();
//geometry
nBits = 6; //max second count will be 59, which is 111011 in binary, 6 bits
ptsPerBit = 25; //density of "bits" represented graphically
nPts = nBits*ptsPerBit;
ptSpacing = canvasWidth/nPts;
bit0Amplitude = 5;
bit1Amplitude = 50;
seeTime = false;
mBallSize = 15;
sBallSize = 8;
cBallSize = 5;
//canvas setup
createCanvas(canvasWidth, canvasHeight);
ellipseMode(CENTER);
//initial list setup
// rotateMinuteHand();
updateModel();
}
function draw() {
//update model
updateModel();
//draw background
background(cBackground);
//draw wave
drawWave();
//draw minute ball
drawMBall();
//draw minute ball
drawSBall();
//draw center ball
// drawCBall();
//draw actual time
if (seeTime == true) {
drawTime();
}
}
The aesthetic of this clock was inspired by an exhibit by Martin Messier, which I had the chance to experience at Pittsburgh’s Wood Street Galleries over the summer. I wanted to explore creating a visual drama through the activation of a single line. I used randomness to generate “noise” in the line to give a sense of constant movement, time passing. The “bands” that form within the line change every second and are a 6-bit binary representation of the second count, from 0 to 59; the bands with the larger amplitude represent 1’s, while the bands with the smaller amplitude represent 0’s. The minute is complete when the line returns to flat, then the binary counting begins again. The minute is represented by the rotation of the line and the ball at the end, while the hour is represented by the grey-scale tone change throughout the 24-hour period–white at 00:00, black at 23:00.
]]>//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Assignment-05-project
/////////////////////////////////
// INIT VARIABLES
/////////////////////////////////
var canvasWidth, canvasHeight, margin;
//grid vars
var nRows, nCols, cellSize, ellipseSize, gridLeft, gridTop, cx, cy;
//color
var colorYellow, colorBrown, colorGreen;
/////////////////////////////////
// HELPER FNS
/////////////////////////////////
function drawBanana(x, y) {
drawLeftCurve(x,y);
drawRightCurve(x,y);
drawLowerCap(x,y);
drawUpperCap(x,y);
}
function drawLeftCurve(x,y) {
//set shape pts...pts determined using guess-and-check
var ax = x + 20; var ay = y + 40;
var bx = x - 20; var by = y + 20;
var cx = x - 25; var cy = y - 20;
var dx = x - 10; var dy = y - 40;
//draw curve
noFill();
stroke(colorBrown);
bezier(ax, ay, bx, by, cx, cy, dx, dy);
// //vis pts
// noStroke();
// fill(0,255,0);
// ellipse(x, y, 10);
// fill(255,0,0);
// ellipse(ax, ay, 5);
// ellipse(bx, by, 5);
// ellipse(cx, cy, 5);
// ellipse(dx, dy, 5);
}
function drawRightCurve(x,y) {
//get bezier pts...pts determined using guess-and-check
var ax = x + 25; var ay = y + 35;
var bx = x; var by = y + 15;
var cx = x - 7; var cy = y - 20;
var dx = x - 5; var dy = y - 40;
//draw curve
noFill();
stroke(colorBrown);
bezier(ax, ay, bx, by, cx, cy, dx, dy);
// //vis pts
// noStroke();
// fill(0,255,0);
// ellipse(x, y, 10);
// fill(255,0,0);
// ellipse(ax, ay, 5);
// ellipse(bx, by, 5);
// ellipse(cx, cy, 5);
// ellipse(dx, dy, 5);
}
function drawLowerCap(x,y) {
//set shape pts...pts determined using guess-and-check
var ax = x + 20; var ay = y + 40;
var bx = x + 25; var by = y + 35;
var cx = x + 27; var cy = y + 37;
var dx = x + 22; var dy = y + 42;
//draw shape
fill(colorBrown);
stroke(colorBrown);
quad(ax, ay, bx, by, cx, cy, dx, dy);
}
function drawUpperCap(x,y) {
//set shape pts...pts determined using guess-and-check
var ax = x - 10; var ay = y - 40;
var bx = x - 5; var by = y - 40;
var cx = x - 1; var cy = y - 49;
var dx = x - 7; var dy = y - 51;
//draw shape
fill(colorBrown);
stroke(colorBrown);
quad(ax, ay, bx, by, cx, cy, dx, dy);
}
function drawVine(x, y) {
//set curve pts...pts determined using guess-and-check
var ax = x - 4; var ay = y - 50;
var bx = x +10; var by = y - 150;
var cx = x + 50; var cy = y - 170;
var dx = x + 60; var dy = y - 155;
//draw curve
noFill();
stroke(colorGreen);
bezier(ax, ay, bx, by, cx, cy, dx, dy);
}
/////////////////////////////////
// RUN!
/////////////////////////////////
function setup() {
// INIT VARS
//canvas
canvasWidth = 800;
canvasHeight = 800;
margin = 25;
//grid vars
nRows = 6;
nCols = 7;
cellSize = (canvasHeight-margin*2)/nRows;
ellipseSize = 35;
gridLeft = margin + ellipseSize/2;
gridTop = (canvasHeight - ((nRows-1)*sqrt(3)/2*cellSize))/2;
//color
colorYellow = color(255, 211, 0);
colorBrown = color(51, 34, 9);
colorGreen = color(0, 82, 1);
// CANVAS SETUP
createCanvas(canvasWidth, canvasHeight);
background(colorYellow);
noLoop();
}
function draw() {
//generate hex grid
for (row = 0; row < nRows; row++) {
for (col = 0; col < nCols; col++) {
//get x,y center for pattern cells
if (row % 2 == 0) { cx = gridLeft + col*cellSize; } //x alignment for even rows
else { cx = gridLeft + col*cellSize + cellSize/2; } //x alignment for odd rows
cy = gridTop + row*(sqrt(3)/2)*cellSize; //y alignment for all rows
//draw pattern, with subtle randomness
if ((row == 2) & (col == 4)) { continue; }
else if ((row == 3) & (col == 3)) { drawBanana(cx, cy);}
else {
drawBanana(cx, cy); //draw banana
drawVine(cx, cy); //draw arc
}
}
}
}
I wanted to spin off of the historic catalog of wallpapers and create a pattern that was more fun and cheesy. A banana became my basic element. Most of the time was spent creating a set of helper functions to draw the banana. No fun. Then I simply applied the banana graphic to a version of the hexagonal grid from assignment 5b, and used a couple conditional statements add some visual interest by removing a single cell in the pattern.
//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Project-04
/////////////////////////////
// DECLARE VARS
/////////////////////////////
//canvas
var canvasWidth, canvasHeight, cx, cy, margin
//mouse
var xm, xy; //constrained mouseX and mouseY\
//color
var bgColor, nColor, hColor; //background, normal, and highlight colors
//Spiral
var r0, theta0; //initial values for recursive spiral
/////////////////////////////
// HELPER FNS
/////////////////////////////
function drawSpiralArt(r, theta) {
//mouseX controls amount by which spiral lines are decimating
//in length with each recursive call
var reductionFactor = map(xm,0,canvasWidth,0,1);
//mouseY controls rotational increment between strings in the
//spiral. Varying this by numbers which are not factors of 2*pi
//leads to different types of spirals.
var angleIncrement = int(map(xy, 0, canvasHeight, 1, 10));
//get spiral points per call
var p1x = cx + r*sin(theta);
var p1y = cy + r*cos(theta);
var p2x = cx - r*sin(theta);
var p2y = cy - r*cos(theta);
//draw grey strings
stroke(nColor);
line(0, 0, p1x, p1y); //top left to p1
line(canvasWidth, 0, p1x, p1y); //top right to p1
line(0, canvasHeight, p1x, p1y); //bottom left to p1
line(canvasWidth, canvasHeight, p1x, p1y); //bottom right to p1
line(0, 0, p2x, p2y); //top left to p2
line(canvasWidth, 0, p2x, p2y); //top right to p2
line(0, canvasHeight, p2x, p2y); //bottom left to p2
line(canvasWidth, canvasHeight, p2x, p2y); //bottom right to p2
//draw main strings;
stroke(hColor);
line(p1x, p1y, p2x, p2y); //through center (core of spiral)
//recursive call until base case is met
if (r > 1) {
drawSpiralArt(r*reductionFactor, theta + angleIncrement);
}
}
/////////////////////////////
// RUN!
/////////////////////////////
function setup() {
//init canvas vars
canvasWidth = 640;
canvasHeight = 480;
cx = canvasWidth/2;
cy = canvasHeight/2;
margin = 30;
//init color vars
bgColor = color(255); //white
nColor = color(136,136,136,35); //grey, semitransparent
hColor = color(255,0,136); //fucia
//init spiral vars
r0 = canvasWidth/2
theta0 = HALF_PI;
//init canvas
createCanvas(canvasWidth, canvasHeight);
}
function draw() {
background(bgColor);
//recalculate mouseX, mouseY with constraints
xm = constrain(mouseX, margin, canvasWidth-margin);
xy = constrain(mouseY, margin, canvasHeight-margin);
//draw spiral
drawSpiralArt(r0, theta0);
}
I wanted to use trigonometry to create a series of points predicated on circular geometry and then connect the points to various other points across the canvas. I ended up using recursion to generate a central spiral. I then used the mouse position to control the degree to which the length of strings in the spiral decimates (the rate at which the strings spiral towards the center) and to control the rotational rate of the strings in the spiral, so that spirals of various character could be created.
Later sketch focusing on spiral series generated from recursive rules.
]]>
//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Project-03
/////////////////////////////
//GLOBAL VARS
/////////////////////////////
//colors
var positive, negative, hold;
// triangles
var triangle1LegLength, triangle1P1X, triangle1P1Y, triangle1P2X, triangle1P2Y,
triangle1P3X, triangle1P3Y;
// lines
var linesPerQuadrant, lineSpacingX, lineSpacingY, x1, y1, x2, y2;
/////////////////////////////
//HELPER FNS
/////////////////////////////
function mousePressed() {
hold = positive;
positive = negative;
negative = hold;
}
/////////////////////////////
//RUN!
/////////////////////////////
function setup() {
//canvas setup
createCanvas(640, 480);
textFont("Courier New");
textAlign(CENTER);
//init vars
positive = 255;
negative = 0;
linesPerQuadrant = 10;
lineSpacingX = (width/2)/linesPerQuadrant;
lineSpacingY = (height/2)/linesPerQuadrant;
}
function draw() {
//clear drawing
background(negative);
// draw triangles
fill(positive);
triangle1LegLength = height-mouseY;
triangle1P1X = mouseX;
triangle1P1Y = height-triangle1LegLength;
triangle1P2X = mouseX - triangle1LegLength;
triangle1P2Y = height;
triangle1P3X = mouseX + triangle1LegLength;
triangle1P3Y = height;
triangle(triangle1P1X, triangle1P1Y,
triangle1P2X, triangle1P2Y,
triangle1P3X, triangle1P3Y);
triangle2LegLength = mouseY;
triangle2P1X = mouseX;
triangle2P1Y = triangle2LegLength;
triangle2P2X = mouseX - triangle2LegLength;
triangle2P2Y = 0;
triangle2P3X = mouseX + triangle2LegLength;
triangle2P3Y = 0;
triangle(triangle2P1X, triangle2P1Y,
triangle2P2X, triangle2P2Y,
triangle2P3X, triangle2P3Y);
stroke(positive);
//vary stroke weight based on mouse distance from center
strokeWeight(dist(mouseX,mouseY,width/2,height/2)/height);
// draw lines
for (var i = 0; i < linesPerQuadrant + 1; i++) {
x1 = width/2;
y1 = height/2 - i*lineSpacingY;
x2 = width/2 - (mouseX/lineSpacingX/2-i)*lineSpacingX;
y2 = height/2;
line(x1,y1,x2,y2); //quadrant 1
line(x1,y1,width-x2,y2); //quadrant 2
line(x1,height-y1,x2,height-y2); //quadrant 3
line(x1,height-y1,width-x2,y2); //quadrant 4
};
// text
fill(negative);
push(); //isolate transformations for upper text
translate(mouseX, mouseY-height/2);
rotate((mouseX/width)*TWO_PI);
text("click to invert", 0, 0);
pop();
push(); //isolate transformations for lower text
translate(mouseX, mouseY+height/2);
rotate((mouseX/width)*TWO_PI);
text("click to invert", 0, 0);
pop();
}
After viewing some examples of dynamic drawings online, I decided that I wanted to use strictly black and white artwork and use the geometry to create dynamism in the drawing. I did a did some scratch-work in my sketchbook to try to figure out some of the math behind the geometry I wanted to create; however, to be honest, I was not able to get the “string art” to transition exactly as I wanted it to. After spending a good chunk of time trying to figure it out, I decided to just roll with it. Cheers.
Participatory art is a form of analog art that exploits the aggregate power of the public to create a work of art. In a way, participatory art is form of generative art; though, rather than relying on a digital algorithm, it is predicated on a set of physical constraints and rules and the autonomy of the participants. Indeed, good participatory art involves a clear framework or set of physical parameters that participants operate within, together, to ultimately generate an unexpected, authentic piece of art. The physical medium acts as the rule set; the participants act as the autonomous motors.
This past weekend, I had the opportunity to visit an exhibit at the Philadelphia Museum of Art titled The Architecture of Francis Kere: Building for Community. Francis Kere is a German-trained architect from a small town in Burkina Faso (West Africa) called Gando. During his youth, he was the only child to be given the opportunity to leave his village to become educated. Since his training in Germany, he has committed his career to reinvesting his architectural knowledge into his home town of Gando. By including the community in the design and construction process, he is empowering them with the knowledge, skills, and optimism to modify their built environment and create more livable spaces for themselves.
Part of Kere’s exhibit included a participatory art piece used to demonstrate the power of participation and collaboration in the creation of art and architecture. The piece was predicated on a simple framework consisting of plastic straws and a plastic cellulated wall structure. Participants were able to grab a straw from a bucket and stick it into the cellulated structure. They were able to select the color of their choice, the location of their straw in the wall, as well as the way in which their straw met the structure. The beauty to this system is that the basic parameters (i.e. the physical properties of the straws and the physical properties of the wall) do not lead to singular results. The straws can be bent or twisted or formed into loops, straws can be combined to form larger straws, colors can be clustered or patterned, straws can pass entirely through the structure and appear on both sides. Each of these conditions was evident in the piece when I visited it, and more conditions were continuing to be developed by participants while I was there. In the end, this type of participatory art leads to unique, unexpected results that are dependent on both a set of base constraints as well as the participants’ interpretation and creativity.
For more information about the work of Francis Kere, visit http://www.kere-architecture.com/.
]]>//Garrett Rauck
//Section C
//grauck@andrew.cmu.edu
//Project-02
///////////////////////////////////
//INIT VARS
///////////////////////////////////
//Layout parameters
var canvasWidth = 600;
var canvasHeight = 600;
var cx = canvasWidth/2;
var cy = canvasHeight/2;
var padding = 15;
var textY = canvasHeight-5
//Shape attributes
var normalStrokeWeight = 2;
//Face parameters
var faceWidth, faceHeight; //face
var chinWidth, chinHeight; //chin
var mouthType, mouthWidth, mouthHeight, mouthY; //mouth
var eyesType, eyesSpacing, eyesWidth, eyesHeight, eyesY; //eyes
var r, g, b;
//Names
var name;
var names = ["Mark","Steve","Kevin","Meredith","Kyle","Sharon",
"Danny","John","Rachel","Sam","Nate","Will","Ashwath",
"Gerry","Bob","Michael","Chris","Isaac","Alex","Gary",
"Chloe","Anne","Maria","Sophia","Kelsey","Monica",
"Phoebe","Marcos","Amy","Candace","Garrett","Sean",
"Judy","Nico","Shannon","Alison","Margaret","Fuad",
"Peter","Dave","Mary","Lisa","Susan"];
///////////////////////////////////
//HELPER FNS
///////////////////////////////////
function randomizeVars() {
//face
faceWidth = random(125,275);
faceHeight = random(125,275);
//chin
chinWidth = random(125,275);
chinHeight = random(125,275);
//eyes
eyesType = int(random(0,3));
eyesSpacing = 100;
eyesWidth = random(5,25);
eyesHeight = random(5,25);
eyesY = random(cy-eyesHeight/2,cy-faceHeight+eyesHeight/2);
//mouth
mouthType = int(random(0,4));
mouthWidth = random(50, chinWidth-padding*2);
mouthHeight = random(50, chinHeight-padding*2);
mouthY = random(cy+mouthHeight/2,cy+chinHeight-mouthHeight/2);
//skin color
r = random(10,245);
g = random(10,245);
b = random(10,245);
//name
name = random(names);
}
function drawMouth() {
if (mouthType == 0) {drawMouth0();}
else if (mouthType == 1) {drawMouth1();}
else if (mouthType == 2) {drawMouth2();}
else if (mouthType == 3) {drawMouth3();}
}
function drawMouth0() { //toothy grin
noStroke();
fill(0);
rect(cx, mouthY, mouthWidth, mouthHeight);
fill(255);
rect(cx, mouthY-mouthHeight/3, mouthWidth, mouthHeight/3); //teeth
fill(127);
rect(cx, mouthY+mouthHeight/3, mouthWidth, mouthHeight/3); //tongue
stroke(0);
noFill();
strokeWeight(normalStrokeWeight);
rect(cx, mouthY, mouthWidth, mouthHeight); //outline
line(cx, mouthY+mouthHeight/6, cx, mouthY+mouthHeight/3);
}
function drawMouth1() { //basic line mouth
stroke(0);
strokeWeight(normalStrokeWeight);
line(cx-mouthWidth/2, mouthY, cx+mouthWidth/2, mouthY);
}
function drawMouth2() { //basic circle mouth
stroke(0);
fill(25);
strokeWeight(normalStrokeWeight);
ellipse(cx, mouthY, mouthWidth, mouthHeight);
}
function drawMouth3() { //basic frown
stroke(0);
noFill();
strokeWeight(normalStrokeWeight);
arc(cx, mouthY, mouthWidth, mouthHeight, PI+PI/8, -PI/8);
}
function drawEyes() {
if (eyesType == 0) {drawEyes0();}
else if (eyesType == 1) {drawEyes1();}
else if (eyesType == 2) {drawEyes2();}
}
function drawEyes0() {
noStroke();
fill(0);
strokeWeight(normalStrokeWeight);
ellipse(cx-eyesSpacing/2, eyesY, eyesWidth, eyesHeight); //left
ellipse(cx+eyesSpacing/2, eyesY, eyesWidth, eyesHeight); //right
}
function drawEyes1() {
noStroke();
fill(255);
ellipse(cx-eyesSpacing/2, eyesY, eyesWidth, eyesHeight); //left
ellipse(cx+eyesSpacing/2, eyesY, eyesWidth, eyesHeight); //right
fill(0);
ellipse(cx-eyesSpacing/2, eyesY, eyesWidth/6, eyesHeight/6);
ellipse(cx+eyesSpacing/2, eyesY, eyesWidth/6, eyesHeight/6);
}
function drawEyes2() {
stroke(0);
strokeWeight(normalStrokeWeight);
line(cx-eyesSpacing/2-eyesWidth/2, eyesY, cx-eyesSpacing/2+eyesWidth/2, eyesY);
line(cx+eyesSpacing/2-eyesWidth/2, eyesY, cx+eyesSpacing/2+eyesWidth/2, eyesY);
}
///////////////////////////////////
//RUN
///////////////////////////////////
function setup() {
createCanvas(canvasWidth, canvasHeight);
background(255);
ellipseMode(CENTER);
rectMode(CENTER);
noStroke();
randomizeVars();
}
function draw() {
background(255);
//face
fill(r,g,b);
rect(cx, cy-faceHeight/2, faceWidth, faceHeight);
//chin
fill(r,g,b);
rect(cx, cy+chinHeight/2, chinWidth, chinHeight);
//eyes
drawEyes();
//mouth
drawMouth();
//name
noStroke();
fill(0);
textAlign(CENTER);
textFont("Courier New");
text(name,cx,textY);
}
function mousePressed() {
randomizeVars();
}
For this exercise, I thought it was critical to define certain parameters that would allow each of the faces to be compared very easily. The face was simplified to an upper “face” rectangle and a lower “chin” rectangle, each of which was randomized with each generation creating unique but comparable facial shapes. Rather than changing the dimensions or inflection of facial features like the mouth and eyes, I thought it would be more interesting to have multiple “types” of eyes and mouths to suggest different expressions. These types would then respond to the dimensions of the chin and face, creating even more variety. Finally, I decided to add a name to each face. I created a list of possible names, hoping that viewers can relate to at least a few of them and have a laugh at what faces pair with what names. The randomization of color helps to bring the characters to life a bit and truly make them believable as individual personas.
]]>