GarrettRauck-Final Project

sketch

//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:

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();
}

GarrettRauck-project-11 (Click to add turtles)

sketch

//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:

  1. When a turtle reaches a pixel that is brighter than a certain value, spawn two child turtles to the left and to the right.
  2. When a turtle reaches a pixel that is darker than a certain value, that turtle “dies”.
  3. If a turtle reaches a pixel that has already been visited by another turtle, or if the turtle runs off the canvas, that turtle respawns to another random location.

What I was able to achieve creates an interesting composition; however, I don’t think it is reacting to the image as I intended.

GarrettRauck-Project-10

sketch

//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();
}

GarrettRauck-Project-07

sketch

//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.

GarrettRauck-Project-6

sketch

//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.

GarrettRauck-Project-5

sketch

//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.

img_2042
Sketches to help determine coordinates for the banana element.

GarrettRauck-Project-4

sketch

//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.

img_1987
Early sketches showing ideas relating to circular arrays of points.

 

img_1986

Later sketch focusing on spiral series generated from recursive rules.

 

GarrettRauck-Project-3

sketch

//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.

img_1614
Scratch-work done in my sketchbook as I attempted to work out some of the math behind the graphic.

GarrettRauck-LookingOutwards-2

img_1105
Participatory art installation at The Architecture of Francis Kere: Building for Community at the Philadelphia Museum of Art.

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.

img_1115
Wall piece at the Francis Kere exhibit talking about the primary philosophy behind Kere’s work: participation.

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.

img_1107
Detail view looking through the cellulated structure of the piece.

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/.

GarrettRauck-Project-2

sketch

//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.