hizlik- LastProject

Update: As of 2020, an updated documentation for this project is now on my website at hiz.al/togetheragain.

Together Again

Click here to play. Click here to see the GitHub repo.

Do you ever wish you could turn back time? Fix what you’ve broken, see those you’ve lost? Together Again is a simple game with a unique mechanic; the goal is to reunite the square and the circle, make them one and the same. Simply use your mouse to tap or bounce the falling square towards the circle. The faster you hit the square, the harder the hit and faster the motion of the square. As the levels progress, gravity becomes stronger, and it gets harder and harder to be together again. Therefore, your wish has come true and you can travel back in time to fix your mistakes, plan out your actions through trial and error, and hopefully succeed. Your past attempts show as faded versions in the background, and the more you fail, the more crowded and distracting and harder it will get to succeed. There is no game over. There is just time.

This game is related to my previous project, hizlik-Book (Year One) in that this game is about the unexpected and unbelievable event that is a breakup that occurred during the time of the printing of the book. However, like the book, the correlation of my projects to my personal life is left ambiguous and often unnoticed. Specifically, the square represents a male figure, the circle a female figure, and the color green is her favorite color.

The game was created in p5.js, with the code provided below.


var c; // canvas
var cwidth = window.innerWidth;
var cheight = window.innerHeight;
var nervous; var biko; // fonts

var gravity = 0.3;
var mouseBuffer = -3;
var bounce = -0.6;
var p2mouse = [];
var boxSize = 50;

var gameState = "menu";
var vizState = "static";
var transitionVal = 0;
var level = 1;
var boxState = "forward";
var offscreen = false;
var offscreenCounter = 0;
var keyWasDown = false;
var gameCounter = 0;

var currBox = null;
var currCirc = null;
var ghosts = [];

function setup() {
	c = createCanvas(cwidth, cheight);
	background(255);
	frameRate(30);
	noCursor();
	nervous = loadFont("Nervous.ttf");
	biko = loadFont("Biko_Regular.otf");
}

window.onresize = function() { 
	cwidth = window.innerWidth;
	cheight = window.innerHeight;
	c.size(cwidth, cheight);
}

function draw() {
	background(255);

	// splash menu
	if(gameState == "menu") {
		noStroke();
		fill(121,151,73)
		textFont(nervous);
		textSize(min(cwidth,cheight)*.1);
		textAlign(CENTER,CENTER);
		text("Together Again", cwidth/2, cheight/2);

		fill(218,225,213);
		textFont(biko);
		textSize(min(cwidth,cheight)*.03);
		text("hold SPACE to go turn back time", cwidth/2, cheight-cheight/5);

		if(keyIsDown(32)) { vizState = "transition"; }
		if(keyIsDown(68)) { gravity = 1; }

		if(vizState == "transition") {
			transitionVal += 10;
			fill(255,255,255,transitionVal);
			rect(0,0,cwidth,cheight);
			if(transitionVal>255) {
				gameState = "game";
			}
		}
	}

	// actual game
	if(gameState == "game") {
		if(currBox == null) currBox = new Box(null);
		if(currCirc == null) currCirc = new Circle();

		if(vizState == "transition") {
			currBox.draw();
			currCirc.draw();
			transitionVal -= 10;
			fill(255,255,255,transitionVal);
			rect(0,0,cwidth,cheight);
			if(transitionVal < 0 && !keyIsDown(32)) {
				vizState = "static";
			}
		}
		else {
			// check if space is being pressed
			if(keyIsDown(32)) {
				boxState = "rewind"
				if(!keyWasDown) { 
					ghosts.push(currBox);
					keyWasDown = true;
				}
			}
			else if(keyWasDown) {
				keyWasDown = false;
				boxState = "forward";
				var prevCount = 1;
				var prev = null;
				while(prev == null) {
					// console.log(ghosts.length-prevCount);
					prev = ghosts[ghosts.length-prevCount].getCurrPos();
					prevCount++;
					if(prevCount > ghosts.length) {
						for(var i=0; i 255) {
					console.log("new level");
					level++;
					gravity = constrain(gravity+.2, 0, 3);
					currBox = new Box([random(cwidth-boxSize), random(cheight/2), 0, 0]);
					// currBox = new Box(null);
					currCirc = new Circle();
					ghosts = [];
					vizState = "transition";
					boxState = "forward"
					transitionVal = 255;
				}
				else if(transitionVal-150 > 150) {
					currBox.draw();
					currCirc.draw();
					fill(255,255,255,transitionVal-150);
					rect(0,0,cwidth,cheight);
				}
				else {
					currBox.draw();
					currCirc.draw();
				}
			}
			else if(boxState == "rewind") {
				gameCounter--;
				if(gameCounter>0) {
					for(var i=0; i= 0) { this.vy += constrain(vm, -40, 40); }
				else { this.vy *= bounce; }
				this.pos[1] = mouseY+mouseBuffer;
			}

			// ========== UPDATE HORIZONTAL ========== //
			var hpos = map(mouseX, this.pos[0], this.pos[0]+boxSize, -1, 1);
			this.vx += 10 * bounce * hpos;
		}

		// update horizontal bounce
		if (collision != null && (collision[2]=="left" || collision[2]=="right")) {
			var vm = (mouseX - pmouseX); // velocity of the mouse in x direction
			this.vx += constrain(vm, -40, 40);

			if(collision[2] == "left") {
				if(this.vx > 0) { this.pos[0] = mouseX; }
				else { this.vx *= bounce; }
				this.pos[0] = mouseX;
			}
			if(collision[2] == "right") {
				if(this.vx < 0) { this.pos[0] = mouseX-boxSize; }
				else { this.vx *= bounce; }
				this.pos[0] = mouseX-boxSize;
			}
		}

		// update position
		if(this.vx > 20*gravity || this.vx < -20*gravity) { this.vx *= 0.85; }
		if(this.vy > 35*gravity || this.vy < -35*gravity) { this.vy *= 0.85; }
		this.vy = constrain(this.vy + gravity, -30, 50);
		this.pos[0] += this.vx;
		this.pos[1] += this.vy;
		
		//debug
		if(this.pos[1]-boxSize>cheight || this.pos[0]+boxSize<0 || this.pos[0]>cwidth) {
			offscreen = true;
		}
		this.history.push([this.pos[0], this.pos[1], this.vx, this.vy]);
		this.currIndex++;
	}

	this.draw = function() {
		noStroke();
		fill(57,67,7);
		if(!this.active)
			fill(239,240,235);
		if(this.currIndex >= 0 && this.currIndex < this.history.length) {
			if(boxState == "reunited") { 
				this.corner = constrain(this.corner+1, 0, 18); 
				var r = map(this.corner, 0, 18, 57, 121);
				var g = map(this.corner, 0, 18, 67, 151);
				var b = map(this.corner, 0, 18, 7, 73);
				fill(r,g,b);
			}
			rect(this.history[this.currIndex][0], this.history[this.currIndex][1], boxSize, boxSize, this.corner);
		}
	}

	this.getMouseCollisionPoint = function() {
		var top = new Line(this.pos[0],this.pos[1],this.pos[0]+boxSize,this.pos[1])
		var left = new Line(this.pos[0],this.pos[1],this.pos[0],this.pos[1]+boxSize)
		var bottom = new Line(this.pos[0],this.pos[1]+boxSize,this.pos[0]+boxSize,this.pos[1]+boxSize)
		var right = new Line(this.pos[0]+boxSize,this.pos[1],this.pos[0]+boxSize,this.pos[1]+boxSize)
		var mouse = new Line(mouseX, mouseY+mouseBuffer, pmouseX, pmouseY+mouseBuffer);
		var coords = null;
		if(pmouseX <= mouseX) {
			var result = getMouseCollision(mouse, left);
			if(result != null) {
				result.push("left");
				return result;
			}
		}
		if(pmouseX >= mouseX) {
			var result = getMouseCollision(mouse, right);
			if(result != null) {
				result.push("right");
				return result;
			}
		}
		if(pmouseY <= mouseY) {
			var result = getMouseCollision(mouse, top);
			if(result != null) {
				result.push("top");
				return result;
			}
		}
		if(pmouseY >= mouseY){
			var result = getMouseCollision(mouse, bottom);
			if(result != null) {
				result.push("bottom");
				return result;
			}
		}
		if(this.vx < 0 && 
			mouseX >= this.pos[0] &&
			pmouseX < this.pos[0]-this.vx &&
			mouseY+mouseBuffer >= this.pos[1] && 
			mouseY+mouseBuffer <= this.pos[1]+boxSize) {
			return [mouseX, mouseY+mouseBuffer, "left"];
		}
		if(this.vx > 0 && 
			mouseX <= this.pos[0]+boxSize &&
			pmouseX > this.pos[0]+boxSize-this.vx &&
			mouseY+mouseBuffer >= this.pos[1] && 
			mouseY+mouseBuffer <= this.pos[1]+boxSize) {
			return [mouseX, mouseY+mouseBuffer, "right"];
		}
		if(this.vy < 0 && 
			mouseY+mouseBuffer >= this.pos[1] &&
			pmouseY+mouseBuffer < this.pos[1]+this.vy &&
			mouseX >= this.pos[0] && 
			mouseX <= this.pos[0]+boxSize) {
			return [mouseX, mouseY+mouseBuffer, "top"];
		}
		if(this.vy > 0 && 
			mouseY+mouseBuffer <= this.pos[1]+boxSize &&
			pmouseY+mouseBuffer >= this.pos[1]+boxSize-this.vy &&
			mouseX >= this.pos[0] && 
			mouseX <= this.pos[0]+boxSize) {
			return [mouseX, mouseY+mouseBuffer, "bottom"];
		}
		return null;
	}

	this.rewind = function() {
		this.currIndex--;
		this.active = false;
	}

	this.init(startVals);
}

function Circle() {
	this.pos = [];
	this.ring = 6;
	this.corner = 25; //18 mid-point

	this.init = function() {
		this.pos = [random(cwidth), random(cheight)];
	}

	this.pulse = function() {
		this.ring-= .2;
		if(this.ring<0.5 && boxState != "reunited") {
			this.ring = 6;
		}
		else if(this.ring<0 && boxState == "reunited") {
			this.ring = 0;
		}
	}

	this.draw = function() {
		this.pulse();
		fill(176, 196, 134);
		strokeWeight(this.ring);
		stroke(239,240,235);
		// ellipse(this.pos[0], this.pos[1], boxSize, boxSize);
		if(boxState == "reunited") { 
			noStroke();
			this.corner = constrain(this.corner-1, 18, 25); 
			var r = map(this.corner, 18, 25, 121, 176);
			var g = map(this.corner, 18, 25, 151, 196);
			var b = map(this.corner, 18, 25, 73, 235);
			fill(r,g,b);
		}
		rect(this.pos[0]-boxSize/2, this.pos[1]-boxSize/2, boxSize, boxSize, this.corner)
	}

	this.init();
}

function lostMessage() {
	noStroke();
	fill(218,225,213);
	textFont(nervous);
	textSize(min(cwidth,cheight)*.08);
	text("lost your way", cwidth/2, cheight/2);
	textFont(biko);
	var sec = "seconds";
	textSize(min(cwidth,cheight)*.05);
	if(round(offscreenCounter/30)==1) sec = "second";
	text("for "+round(offscreenCounter/30)+" "+sec, cwidth/2, cheight/2+cheight/10);
	textSize(min(cwidth,cheight)*.03);
	text("hold SPACE to go turn back time", cwidth/2, cheight-cheight/5);
}

function getMouseCollision(a, b) {
	var coord = null;
	var de = ((b.y2-b.y1)*(a.x2-a.x1))-((b.x2-b.x1)*(a.y2-a.y1));
	var ua = (((b.x2-b.x1)*(a.y1-b.y1))-((b.y2-b.y1)*(a.x1-b.x1))) / de;
	var ub = (((a.x2-a.x1)*(a.y1-b.y1))-((a.y2-a.y1)*(a.x1-b.x1))) / de;
	if((ua > 0) && (ua < 1) && (ub > 0) && (ub < 1)) {
		var x = a.x1 + (ua * (a.x2-a.x1));
		var y = a.y1 + (ua * (a.y2-a.y1));
		coord = [x, y];
	}
	return coord;
}

function Line(x1, y1, x2, y2) {
	this.x1 = x1;
	this.y1 = y1;
	this.x2 = x2;
	this.y2 = y2;
}

function areReunited(box, circle) {
	var distX = Math.abs(circle.pos[0] - box.pos[0] - boxSize / 2);
    var distY = Math.abs(circle.pos[1] - box.pos[1] - boxSize / 2);

    if (distX > (boxSize / 2 + boxSize/2)) {
        return false;
    }
    if (distY > (boxSize / 2 + boxSize/2)) {
        return false;
    }

    if (distX <= (boxSize / 2)) {
        return true;
    }
    if (distY <= (boxSize / 2)) {
        return true;
    }

    var dx = distX - boxSize / 2;
    var dy = distY - boxSize / 2;
    return (dx * dx + dy * dy <= (boxSize/2 * boxSize));
}

hizlik-object

CHOCO BOXO

For my physical computing project I decided to create a little box to protect my chocolates from warm weather. Once the environmental (outdoor) temperature reaches 65 degrees Fahrenheit or greater, the fans kick in until the temperature drops back below 65. Who wants melted chocolate during the summer? This will keep them nice and cool, but not hardened like putting them in a fridge would.

Using the littleBits was rather simple, as Golan described. Setting up my cloudBit was a no-brainer and linking it with IFTTT was also extremely simple. Wiring up my box also did not take too much time but the connections are a bit unreliable, due to their magnetic nature. Unfortunately if the box is jolted and the cloudBit loses power, it takes 15 seconds to reboot, and will not start the fans back up if they were already on before the “power outage.” This is because on start, the cloudBit awaits a trigger from IFTTT, which wont send a new “turn on fan” one until it goes above, then back below 65 degrees.

This project was created using littleBits and a cloudBit, as well as IFTTT. Here are the recipes:

recipes

And the diagram of the bits:

screen-shot-2016-11-15-at-10-21-09-am

hizlik-manifesto

I believe that among all the engineering tenets, #9 is the most important, relevant and (in my opinion) impactful. I say this because the code in anything defines how digital technology works, and digital technology is by far the biggest, most influential, life-impacting and ever-present form of technology, innovation and invention since the start of mankind.

Its very important to maintain a balance between human and machine interaction. If one confuses the other the balance will be broken. This tenet is saying how engineers should not write code purely for function but for the response and perception by people that interact with it. They should delve into psychological and social realms as the tenet says, and should work to create the most immersive digital experience possible without ruining the harmony with the physical world. There are a few large corporations that seek to create a seamless communication between the two, “perfecting” the experience, such as Apple (during the years Steve Jobs was in charge). The company has always aimed to create a simple-to use (to minimize frustration) yet powerful operating system experience on all of the devices they own, from mobile devices like the iPhone to desktops and laptops. Not only do they strive to minimize the friction between person and machine interaction, they have developed a tight and invisible connection between all of their own machines, creating a machine-to-machine environment that creates an even better experience as you switch from one to the other without problem.

hizlik-lookingoutward05

VRDoodler by Cindy Bishop

VRDoodler is a comprehensive in-browser 3D drawing tool that lets you draw and explore your drawings in 3D, with or without virtual reality gear. It is definitely an interesting, unique concept that would not have been possible before our generation’s available technologies. Unfortunately I could not stay during her lecture at Weird Reality very long because of volunteering commitments, however from the short amount of time I was present, I was able to understand a few things. One is that, it can be frustrating as it has a bit of a learning curve. You can often find yourself drawing at multiple depths/distances without realizing it until you spin your camera. One critique is that it is a bit iffy on a phone, laggy and not fluid. It is best on a computer, with a tablet.

You can visit the gallery to see some works created by users: http://vrdoodler.com/#gallery 

hizlik-lookingoutward08

Hiroshi Ishii – Materiable

This series of creations/projects by Hiroshi Ishii and the other team members of the group is perhaps the most famous, and possibly therefore most cliche Looking Outwards pick, by the Tangible Media Group. I personally connect to this project because I have seen the documentation long before this, I think possibly even before college years. I had never heard of physical computing, of interactive art or anything at the time. I just knew, when I saw it, that this was the future!

The Materiable “tables” are complex yet simple designs that allow you to “form” shapes using blocks on motors. As seen in the documentation, you can use programatic designs (such as 3D graphs), interactive reactions (such as motion sensing) and “moldable” forms, which react to direct physical actions like pushing down on the blocks. I really loved the visualizations of the 3d graphs, and the “real life” example of the phone that moves into view.

Some critiques on this would, I suppose, be that it is still a bit too static (limited by a square of area on a table). I would love to see a room-sized version of this where you can walk on it and interact with it that way, perhaps with a 2-3 foot height when each block is fully extended. And, of course, “resolution” also could be increased, although with each additional “pixel”/block resolution, the complexity would only increase. But maybe in time.

hizlik-mocap

screengrab

Created with Processing, project available on GitHub.

Creating this was an interesting process. I’m not sure if there is a real way to access the bone-by-bone data through the API, but I ended up modifying the BVH parser code directly, outside of my main Processing file. I used each bone element as a starting point to randomly draw lines to two other randomly picked bones in the body, and connect them to create a triangle (with a semi-opaque fill). I chose to have the triangles change position every frame rather than retain their assigned bones throughout, because I thought it was more abstract and exciting than a static triangle shape moving with the movement of the bones.

Some issues I ran into: I found it easier to access bone data (again, by modifying the parser library) compared to the three.js library, therefore my final project stayed in Processing. I also found it to be very glitchy with other found BVH/mocap recordings found online. I have a folder full of hundreds of recordings, my favorites being martial-arts based movements. However, even though these all work in three.js demo file, they did not work at all the Processing thing, and I’m unsure why. It was not a normal crash (array index out of bounds or some other error). Instead, it would just look weird, glitchy and move all over the place, with no actual code errors.

loop

I have no planning/sketches as I was creating this project experimentally rather than planned like some of my other projects.

hizlik-lookingoutward07

138 Years of Popular Science    Jeb Thorp

This was a project that caught my eye as I searched through the works of the several data-vis artists and designers listed on the lecture page. I personally have read many pop-sci magazine articles in the past and, along with Wired magazine, value their views, opinions and news regarding the technological and scientific world, past, present and future. Therefore I was interested in what parameters Jeb was considering as the data for his visuals (there are so many things you can compare with 140 years of magazine data). He decided to create a visualization based on terms used throughout the years (“Radio-Television” for example). Although I personally think this is an ok but not best choice, I can’t think of anything cooler. However, I really enjoyed his method of visualizing this massive amount of data. According to his detailed documentation, he wanted a DNA-structure for the decades, with clusters of years surrounding each decade, and within each year is a circle that represents a separate magazine article (and the circle color is dependent on the dominant color on the article cover).

I personally love the final structure, but am unsure about the colors still. Although it is a culmination of all the dominant cover colors over the decades, the overall pop-sci page feels very dull/muted. Perhaps a pure-white background would help pop the cover colors more, or using color in the metadata/term text surrounding the structure? Not sure. I do love his many forms of designing the final structure, including this one “diversion” as he calls it.

You can view more of this project at his blog post. I have been looking more at his other works too and really enjoy his visualizations. I could love to actually pick up and read this page from the Pop-Sci magazine though.

hizlik-lookingoutward06

Restroom Genderator by  & 

This Twitter-bot is a unique take on the conversation of gender equality, gender fluidity and gender identity that is taking over the political and social spectrum for last few years now. This bot generates and pairs random words to create unique, unheard of genders and pairs them with a symbol and a Braille translation. The result is formatted to look like a bathroom sign that you would hang in a corridor or bathroom door. The background colors, genders, symbols and braille are all generated randomly.

I personally love the completeness and absurdity of the project. In terms of completeness, I respect a project that goes to round out all the edges to an idea, so in this case he did not just generate genders, but a fully constructed sign with all the required parts to it. And the results look clean and good looking, all of them.


The absurdness comes from the sometimes non-existent link to human behavior or appearance (such as “minerals” in the gender name), the often un-relatable symbols to human form (ordinarily you see a generic man or woman stick figure, not an alien symbol), and the last of which is the fact that, in the end, this bot is sort of segregating genders even more. Instead of promoting a single bathroom for all to use, it is generating an infinite amount of segregated bathrooms, one for each gender. I believe whether you are for or against gender equality in bathrooms, Restroom Genderator is an enjoyable piece for all to enjoy and remark about.

hizlik-visualization

screen-shot-2016-11-03-at-6-21-05-pm

You can view the project live here. IT TAKES 20-30 SECONDS TO LOAD

Chart: It shows the amount of departures from stations per hour. Using this, you can kind of see how some stations have more people taking bikes in the mornings and some have more people taking them at nights (possibly to/from work?). Y axis = number of departures, X axis = station.

Making of: It was remarkably hard using d3, just like we were warned (that it takes a different kind of thinking). That said, I’m fairly happy with how it turned out, for a simple chart. I could not fix the x-axis names (I wanted them to be vertical) and could not get animations working. I also would have preferred to have a max-height that is consistent across all hours, rather than changing the scale of the chart. But I couldn’t figure that out either. I couldn’t get the axis titles to show up either, so I ended up not using them.

hizlik-book

Update: As of 2020, an updated documentation for this project is now on my website at hiz.al/yearone.

Year One is a book of cityscapes. Each city represents the daily volume of text messages that my girlfriend and I sent each other during our first year of dating. There are 366 total cities (I included both our first day, and first anniversary), and each building represents an hour in the day (24 total buildings, at most), in the manner of a bar chart.

cover_front

Each city is rendered with a watercolor/painted effect. The book was mainly generated using Processing, but other programming languages were used as well, which served various purposes in the creation of the imagery within the pages.

365

As described in the process video (below), I wanted to create a project based on the texts that my girlfriend sent to each other during our first year of dating, which was long-distance for most of the time. The other thing I wanted to do was create generative cityscapes (I have a fascination with cities). After being unable to pick between the two concepts, I put them together. Essentially, the total significance of all the texts per hour (so not the amount of texts, but the total content length) dictates the height of a building, while the number of images sent are represented by the various attachments to the buildings (antennas, water towers, balconies, “blocks/vents” on the roof, etc. I used multiple languages to create this project, detailed below. One of the significant issues for this was creating the painterly effect, which in the end I think turned out exactly as I had hoped it would. I took inspiration from artist Michael Tompsett for the artistic style of my generated imagery.

screen-shot-2016-10-28-at-1-48-53-am screen-shot-2016-10-28-at-1-49-04-am

The most time-consuming process was the creation of the variety of buildings and building parameters. Each building consists of differently modified versions of vertex-based shapes. I believe I succeeded in everything I intended to do, which surprises me because normally I either run out of time or am unable to achieve my vision. This was one of the more multi-step and complicated projects I’ve undertaken, so I was surprised I was able to not only achieve the exact image I had in my head, but also within record time (I started Wednesday night, finished by Saturday afternoon). I love how the buildings look, am pleased with their variety (although it could always use more) and love the way the watercolor effect looks. If I had to change anything, I would change the % possibility for some types of more unique buildings (to a lower chance), and also add more brush types and more “floors” the buildings rest on, for more variety. (You can see some repetition if you pay attention).

The numbers at the bottom of each page represent the total number of text messages, and total volume of characters that we exchanged. Golan expressed surprise that we sent each other (in some cases) hundreds of text messages per day. It’s true.

Here’s a video of Golan flipping through the book:

FILES

Short excerpt of the book (8MB PDF): hizlik_book_short.pdf

You can download this PDF and all the source code from my GitHub repo.

CODE

cover_back


Shell/Bash Script: Export text messages from Apple Messages SQLite database

if [ $# -lt 1 ]; then
    echo "Enter a iMessage account (email of phone number i.e +33616.....) "
fi
login=$1
    
sqlite3 ~/Library/Messages/chat.db "
select '[str][' as 'ph1',datetime(date + strftime('%s', '2001-01-01 00:00:00'), 'unixepoch', 'localtime') as date,']' as 'ph2','[' as 'ph2',is_from_me,']' as 'ph2',NULL as 'filename',text from message where handle_id=(
select handle_id from chat_handle_join where chat_id=(
select ROWID from chat where guid='iMessage;-;$1')
)
union all
select '[att][' as 'ph1',datetime(date + strftime('%s', '2001-01-01 00:00:00'), 'unixepoch', 'localtime') as date,']' as 'ph2','[' as 'ph3',is_from_me,']' as 'ph2',NULL as 'text',filename from attachment,message_attachment_join,message where attachment.rowid = message_attachment_join.attachment_id and message_attachment_join.message_id = message.rowid and message.cache_has_attachments=1 and message.handle_id=(
select handle_id from chat_handle_join where chat_id=(
select ROWID from chat where guid='iMessage;-;$1')
) order by date" | sed 's/\|1\|/H/g;s/\|0\|/N/g;s/\|//g' > iMessages$1.txt

Python: Analyze texts for number, total characters, and amount of attachments sent (per hour)

# -*- coding: utf-8 -*-
import glob
import os
import re
import string

from datetime import datetime,timedelta

filenames = []
texts = []
data = {}

metadata = {
	'total_N_str': 0,
	'total_N_att': 0,
	'total_N_str_len': 0,
	'max_N_len': 0,
	'total_H_str': 0,
	'total_H_att': 0,
	'total_H_str_len': 0,
	'max_H_len': 0
}

def getLogs():
	cwd = os.getcwd()
	os.system("cd '" + cwd + """'
./imbfull.sh PHONE
./imbfull.sh EMAIL""")

def getFileNames():
	for filename in glob.glob('*.txt'):
		if "iMessage" in filename: filenames.append(filename)

def displayFileList():
	print len(filenames),"files found:"
	print '\n'.join(filenames)

def openFile(s):
	print "Openning file...",
	with open(s, 'r') as f: 
		readList = f.readlines()
	print "			File successfully open."
	return readList

def parseText(text):
	# search for [type][YYYY-MM-DD HH:MM:SS][FROM]
	pattern = re.compile('^\[[a-z]{3}\]\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]\[[A-Z]\]')

	meta = re.search(pattern, text).group()
	t_type = meta[1:4]
	t_from = meta[-2:-1]
	t_date = datetime.strptime(meta[6:25], '%Y-%m-%d %H:%M:%S')
	t_str = text.replace(meta,"").rstrip()

	return t_type, t_from, t_date, t_str

def listTexts(file, filename):
	print "Listing messages...",

	# search for [type][YYYY-MM-DD HH:MM:SS][FROM]
	pattern = re.compile('^\[[a-z]{3}\]\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]\[[A-Z]\]')

	for line in file:
		line = line.rstrip()
		if re.match(pattern, line):
			texts.append(line)
		else:
			texts[-1] += line

	print "		Messages successfully listed."

def runMessageExtractor(s):
	print "\nExtracting text IDs on %s:"%(s)
	listTexts(openFile(s), s)
	# H_att,N_att,H_txt,N_txt = countTexts(readList,s)
	# texts.append((H_att,N_att,H_txt,N_txt))

def analyze():
	global total_N_str, total_N_att, total_H_str, total_H_att

	print "Analyzing messages...",
	#initialize time range
	start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
	while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): 
		data[start_date.strftime('%Y-%m-%d %H')] = { 
			'H': { 'txt_count':0, 'len':0, 'att_count':0 },
			'N': { 'txt_count':0, 'len':0, 'att_count':0 }
		}
		start_date += timedelta(hours=1)
	#populate data
	for text in texts:
		t_type, t_from, t_date, t_str = parseText(text)
		if t_type == "str":
			if t_from == "H":
				metadata['total_H_str'] += 1
				metadata['total_H_str_len'] += len(t_str)
				metadata['max_H_len'] = max(metadata['max_H_len'],len(t_str))
			else:
				metadata['total_N_str'] += 1
				metadata['total_N_str_len'] += len(t_str)
				metadata['max_N_len'] = max(metadata['max_N_len'],len(t_str))
		else:
			if t_from == "H":
				metadata['total_H_att'] += 1
			else:
				metadata['total_N_att'] += 1
		if t_date.strftime('%Y-%m-%d %H') in data:
			data[t_date.strftime('%Y-%m-%d %H')][t_from]['txt_count'] += 1
			if t_type == "att":
				data[t_date.strftime('%Y-%m-%d %H')][t_from]['att_count'] += 1
			else:
				data[t_date.strftime('%Y-%m-%d %H')][t_from]['len'] += len(t_str)
	print "		Analysis complete."
def writeData():
	print "Writing data...",
	with open("h_data.txt", 'w') as f:
		start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
		prev_day = -1
		while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): if prev_day != start_date.day: if prev_day >= 0:
					f.write("\n")
				f.write(start_date.strftime("%B %d").lstrip("0").replace(" 0", " "))
				prev_day = start_date.day
			txt_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['txt_count']
			t_len = data[start_date.strftime('%Y-%m-%d %H')]['H']['len']
			att_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['att_count']
			f.write("-%d&%d&%d"%(txt_count, t_len, att_count))
			start_date += timedelta(hours=1)
		f.close()
	with open("n_data.txt", 'w') as f:
		start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
		prev_day = -1
		while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): if prev_day != start_date.day: if prev_day >= 0:
					f.write("\n")
				f.write(start_date.strftime("%B %d").lstrip("0").replace(" 0", " "))
				prev_day = start_date.day
			txt_count = data[start_date.strftime('%Y-%m-%d %H')]['N']['txt_count']
			t_len = data[start_date.strftime('%Y-%m-%d %H')]['N']['len']
			att_count = data[start_date.strftime('%Y-%m-%d %H')]['N']['att_count']
			f.write("-%d&%d&%d"%(txt_count, t_len, att_count))
			start_date += timedelta(hours=1)
		f.close()
	print "			Write complete."

def writeDataTogether():
	print "Writing data...",
	with open("data.txt", 'w') as f:
		start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
		prev_day = -1
		while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): if prev_day != start_date.day: if prev_day >= 0:
					f.write("\n")
				f.write(start_date.strftime("%B %d, %Y").lstrip("0").replace(" 0", " "))
				prev_day = start_date.day
			txt_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['txt_count'] + data[start_date.strftime('%Y-%m-%d %H')]['N']['txt_count']
			t_len = data[start_date.strftime('%Y-%m-%d %H')]['H']['len'] + data[start_date.strftime('%Y-%m-%d %H')]['N']['len']
			att_count = data[start_date.strftime('%Y-%m-%d %H')]['H']['att_count'] + data[start_date.strftime('%Y-%m-%d %H')]['N']['att_count']
			f.write("-%d&%d&%d"%(txt_count, t_len, att_count))
			start_date += timedelta(hours=1)
		f.close()
	print "			Write complete."

def runTextCounter():
	getLogs()
	getFileNames()
	displayFileList()
	print "\nBeginning chatlog indexing..."
	for chatlog in filenames:
		runMessageExtractor(chatlog)
	print ""

	analyze()
	writeDataTogether()

	# only within time range
	non0len_H = 0
	non0count_H = 0
	maxlen_H = 0
	non0len_N = 0
	non0count_N = 0
	maxlen_N = 0

	start_date = datetime.strptime("2014-05-02", '%Y-%m-%d')
	while start_date < datetime.strptime("2015-05-03", '%Y-%m-%d'): t_len = data[start_date.strftime('%Y-%m-%d %H')]['H']['len'] if t_len > 0:
			non0len_H += t_len
			non0count_H += 1
			maxlen_H = max(maxlen_H, t_len)
		t_len = data[start_date.strftime('%Y-%m-%d %H')]['N']['len']
		if t_len > 0:
			non0len_N += t_len
			non0count_N += 1
			maxlen_N = max(maxlen_N, t_len)
		start_date += timedelta(hours=1)

	print "\nChatlog indexing is complete.\n"
	print "%d total messages: %d by H and %d by N."%(metadata['total_H_str']+metadata['total_N_str'], metadata['total_H_str'], metadata['total_N_str'])
	print "%d total attachments: %d by H and %d by N."%(metadata['total_H_att']+metadata['total_N_att'], metadata['total_H_att'], metadata['total_N_att'])
	print "%d ave characters: %d for H (max %d) and %d for N (max %d)"%((metadata['total_H_str_len']+metadata['total_N_str_len'])*1.0/(metadata['total_H_str']+metadata['total_N_str']), metadata['total_H_str_len']*1.0/metadata['total_H_str'], metadata['max_H_len'], metadata['total_N_str_len']*1.0/metadata['total_N_str'], metadata['max_N_len'])
	print "%d ave characters per hour: %d for H (max %d) and %d for N (max %d)"%((non0len_H + non0len_N)*1.0/(non0count_H + non0count_N), non0len_H*1.0/non0count_H, maxlen_H, non0len_N*1.0/non0count_N, maxlen_N)
	

runTextCounter()


Processing: Generating cityscape masks using brush strokes and random buildings based on text analysis

import java.util.*; 
boolean debug = false;

ArrayList<String[][]> texts = new ArrayList<String[][]>();
int index = 0;
int hour = 0;
boolean var3used = false;
boolean var4used = false;
boolean thinused = false;

// VARIATIONS
float overlap = 10;
float ignore = 10;
float xscap = 25;
float smallcap = 50;
float medcap = 200;
float tallcap = 500;

// CITY VARIABLES
float citystartx;
float citystarty;
float building_width;
ArrayList trees = new ArrayList();

// PAINT
ArrayList floors = new ArrayList();
ArrayList lefts = new ArrayList();
ArrayList rights = new ArrayList();
ArrayList mids = new ArrayList();

void setup() {
  size(1700,1700); //start 350 in from 1700
  String[] fileLines = loadStrings("../txtcounter/data.txt");
  for(int i=0; i<fileLines.length; i++)
    texts.add(parseData(fileLines[i]));
  
  // trees
  for(int i=0; i<12; i++)
    trees.add(loadImage("greenery/s_treeTop"+i+".png"));
  
  // floors
  for(int i=0; i<10; i++)
    floors.add(loadImage("brushes/floors/floor"+i+".png"));
  
  // lefts
  for(int i=0; i<12; i++)
    lefts.add(loadImage("brushes/lefts/left"+i+".png"));
  
  // rights
  for(int i=0; i<12; i++)
    rights.add(loadImage("brushes/rights/right"+i+".png"));
  
  // mids
  for(int i=0; i<46; i++) mids.add(loadImage("brushes/mids/mid"+i+".png")); citystartx = 350; citystarty = (height-350)-(height-700)/3.0; building_width = (width-700)/24.0; background(255); //buildCity(texts.get(index)); println(getMetadata(texts.get(index))); } void draw() { // draw background if(hour == 0) { int floor = int(random(floors.size())); image(floors.get(floor),0,-2); int left = int(random(lefts.size())); if(round(random(2))>0)
      image(lefts.get(left),0,0);
    int right = int(random(rights.size()));
    if(round(random(2))>0)
      image(rights.get(right),0,0);
    for(int i=0; i<round(random(4,6)); i++) { int mid = int(random(mids.size())); image(mids.get(mid),random(-100,100),0); } hour ++; } // draw template String[][] data = texts.get(index); if(hour>0 && hour<data.length) {
    buildCity(data, hour);
    hour++;
  }
}

void reset() {
  hour = 0;
  background(255);
  var3used = false;
  var4used = false;
  thinused = false;
}

void keyPressed() {
  if (key == 'b' || key == 'B') {
    index--;
    if(index<0) index = texts.size()-1; println(getMetadata(texts.get(index))); reset(); } if (key == 'n' || key == 'N') { index++; if(index >= texts.size()) index = 0;
    println(getMetadata(texts.get(index)));
    reset();
  }
  if (key == 's' || key == 'S') {
    String filename = "drawings/"+index+" "+texts.get(index)[0][0]+".png";
    saveFrame(filename);
  }
  if (key == 'r' || key == 'R') {
    reset();
  }
}

String getMetadata(String[][] data){
  String info = data[0][0]+" ";
  int texts = 0;
  int att = 0;
  for(int i=1; i<data.length; i++) {
    texts += parseInt(data[i][0]);
    att += parseInt(data[i][2]);
  }
  info += "("+texts+" texts, "+att+" attachments)";
  return info;
}

void buildCity(String[][] data, int i) {
  overlap = random(10);
  float limitter = 8000; //ave 755
  float hval = map(parseInt(data[i][1]), 0, limitter, 0, height/2);
  if(hval < smallcap) { //is small fill(255,0,0,128); } else if(hval >= smallcap && hval < medcap) { //is med fill(0,255,0,128); } else if(hval >= medcap && hval < tallcap) { //is tall
    fill(0,0,255,128);
  } 
  else { //is super tall
    fill(128,128,128,128);
  }
  if(debug) {
    noStroke();
    rect(citystartx+building_width*(i-1)-overlap, citystarty, building_width+overlap, random(-100,-200));
  }
  if(hval != 0) {
    if(hval <= ignore && parseInt(data[i][2])>0)
      attGroup0(citystartx+building_width*(i-1)-overlap, citystarty, building_width+overlap, parseInt(data[i][2]));
    else
      randomBuilding(citystartx+building_width*(i-1)-overlap, citystarty, building_width+overlap, hval, parseInt(data[i][2]));
  }
}

void randomBuilding(float startx, float starty, float w, float h, int numAtt) {
  // is too small
  if(h <= ignore) {
    var11(startx, starty, w, h, numAtt);
  }
  
  // is xs
  else if(h < xscap) {
    if(round(random(1)) == 0)
      var11(startx, starty, w, h, numAtt);
    else
      var13(startx, starty, w, h, numAtt);
  }
  
  // is small
  else if(h < smallcap) {
    int choice = int(random(100));
    if(choice < 30)
      var9(startx, starty, w, h, numAtt);
    else if(choice < 60)
      oldRoof(startx, starty, w, h, numAtt);
    else if(choice < 90)
      slantRoof(startx, starty, w, h, numAtt);
    else
      basic(startx, starty, w, h, numAtt);
  }
  
  // is medium
  else if(h < medcap) { 
    int choice = int(random(8));
    if(choice == 0)
      oldRoof(startx, starty, w, h, numAtt);
    else if(choice == 1) {
      if(thinused)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        thin(startx, starty, w, h, numAtt);
    }
    else if(choice == 2)
      slantRoof(startx, starty, w, h, numAtt);
    else if(choice == 3)
      basic(startx, starty, w, h, numAtt);
    else if(choice == 4)
      stairstep(startx, starty, w, h, numAtt);
    else if(choice == 5)
      angled(startx, starty, w, h, numAtt);
    else if(choice == 6)
      bevel(startx, starty, w, h, numAtt);
    else
      roundRoof(startx, starty, w, h, numAtt);
  }
  
  // is tall
  else if(h < tallcap) { 
    int choice = int(random(14));
    if(choice == 0)
      var1(startx, starty, w, h, numAtt);
    else if(choice == 1) {
      if(var3used)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        var3(startx, starty, w, h, numAtt);
    }
    else if(choice == 2) {
      if(var4used)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        var4(startx, starty, w, h, numAtt);
    }
    else if(choice == 3)
      var8(startx, starty, w, h, numAtt);
    else if(choice == 4)
      slant(startx, starty, w, h, numAtt);
    else if(choice == 5)
      stairstep(startx, starty, w, h, numAtt);
    else if(choice == 6)
      bevel(startx, starty, w, h, numAtt);
    else if(choice == 7)
      slice(startx, starty, w, h, numAtt);
    else if(choice == 8)
      angled(startx, starty, w, h, numAtt);
    else if(choice == 9)
      blockRoof(startx, starty, w, h, numAtt);
    else if(choice == 10)
      triangleRoof(startx, starty, w, h, numAtt);
    else if(choice == 11)
      basic(startx, starty, w, h, numAtt);
    else if(choice == 12) {
      if(thinused)
        randomBuilding(startx, starty, w, h, numAtt);
      else
        thin(startx, starty, w, h, numAtt);
    }
    else
      var2(startx, starty, w, h, numAtt);
  }
  
  // is super tall
  else { 
    int choice = int(random(100));
    if(choice < 20)
      var1(startx, starty, w, h, numAtt);
    else if(choice < 40)
      var5(startx, starty, w, h, numAtt);
    else if(choice < 60)
      var7(startx, starty, w, h, numAtt);
    else if(choice < 80)
      stairstep(startx, starty, w, h, numAtt);
    else
      var8(startx, starty, w, h, numAtt);
  }
}

String[][] parseData(String texts) { 
  String[][] hourlyData = new String[25][3];
  String[] hours = texts.split("-");
  hourlyData[0] = new String[1];
  hourlyData[0][0] = hours[0];
  for(int i=1; i<hours.length; i++) { hourlyData[i] = hours[i].split("&"); } return hourlyData; } // ******************** VARIATION DESIGNS ********************* // void basic(float startx, float starty, float w, float h, int numAtt) { noStroke(); fill(0); rect(startx, starty, w, -1*h); if(numAtt>0)
    attGroup1(startx, starty, w, h, numAtt);
}

void slant(float startx, float starty, float w, float h, int numAtt) {
  int both = round(random(2));
  int left = round(random(1));
  float xr = random(w/6, w/3);
  float xl = random(w/6, w/3);
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(both==1) {
    vertex(startx+xl, starty-h);
    vertex(startx+w-xr, starty-h);
  }
  else {
    if(left==1) {
      vertex(startx+xl, starty-h);
      vertex(startx+w, starty-h);
    }
    else {
      vertex(startx, starty-h);
      vertex(startx+w-xr, starty-h);
    }
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt > 0) {
    if(both == 1)
      attGroup1(startx+xl, starty, w-(xr+xl), h, numAtt);
    else if(left == 1)
      attGroup1(startx+xl, starty, w-xl, h, numAtt);
    else
      attGroup1(startx, starty, w-xr, h, numAtt);
  }
}

void bevel(float startx, float starty, float w, float h, int numAtt) {
  int both = round(random(2));
  int left = round(random(1));
  float x1 = random(w/6, w/3); // width of slant
  float y1 = random(h/5); // height of slant
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(both==1) {
    vertex(startx, starty-h+y1);
    vertex(startx+x1, starty-h);
    vertex(startx+w-x1, starty-h);
    vertex(startx+w, starty-h+y1);
  }
  else {
    if(left==1) {
      vertex(startx, starty-h+y1);
      vertex(startx+x1, starty-h);
      vertex(startx+w, starty-h);
    }
    else {
      vertex(startx, starty-h);
      vertex(startx+w-x1, starty-h);
      vertex(startx+w, starty-h+y1);
    }
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt > 0) {
    if(both == 1)
      attGroup1(startx+x1, starty, w-x1*2, h, numAtt);
    else if(left == 1)
      attGroup1(startx+x1, starty, w-x1, h, numAtt);
    else
      attGroup1(startx, starty, w-x1, h, numAtt);
  }
}

void slice(float startx, float starty, float w, float h, int numAtt) {
  int left = round(random(1));
  float y1 = random(h/8, h/3); // height of slant
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(left==1) {
    vertex(startx, starty-h);
    vertex(startx+w, starty-h+y1);
  }
  else {
    vertex(startx, starty-h+y1);
    vertex(startx+w, starty-h);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+10+random(w-20), starty-h+y1, w, random(h/6, h/4), startx, starty, numAtt);
}

void angled(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/4, w-w/4); // corner point
  float yl = constrain(random(h/3),0,10); // height of left
  float yr = constrain(random(h/3),0,10); // height of right
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+yl);
  vertex(startx+x1, starty-h);
  vertex(startx+w, starty-h+yr);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+10+random(w-20), starty-h+max(yl,yr), w, random(h/8, h/5), startx, starty, numAtt);
}

void stairstep(float startx, float starty, float w, float h, int numAtt) {
  int single = round(random(3));
  int left = round(random(1));
  float x1; //width of left
  float y1; //height of left
  if(single == 0) { // is both
    x1 = random(w/5, w/2);
    y1 = random(h/8, h/3);
  }
  else {
    x1 = random(w/4, w-w/4);
    y1 = random(h/8, h/3);
  }
  
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(single==0) {
    stairLeft(startx, starty, w, h, x1, y1);
    stairRight(startx, starty, w, h, x1, y1);
  }
  else if(left == 1) {
    stairLeft(startx, starty, w, h, x1, y1);
    vertex(startx+w, starty-h);
  }
  else {
    vertex(startx, starty-h);
    stairRight(startx, starty, w, h, x1, y1);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  numAtt = attGroup3(startx, starty, w, h-y1, numAtt);
  if(numAtt > 0) {
    if(single==0)
      attGroup0(startx, starty, w, numAtt);
    else if(left == 1)
      attGroup1(startx+x1, starty, w-x1, h, numAtt);
    else
      attGroup1(startx, starty, w-x1, h, numAtt);
  }
}

void stairLeft(float startx, float starty, float w, float h, float x1, float y1) {
  int steps = round(random(4,6));
  for(int i=0; i<=steps; i++) {
    float x = startx + ((x1/steps)*i);
    float y = (starty-h+y1) - ((y1/steps)*i);
    vertex(x,y);
    vertex(x+(x1/steps),y);
  }
}

void stairRight(float startx, float starty, float w, float h, float x2, float y2) {
  int steps = round(random(4,6));
  for(int i=0; i<steps; i++) {
    float x = startx + (w-x2) + ((x2/steps)*i);
    float y = (starty - h) + ((y2/steps)*i);
    vertex(x,y);
    vertex(x+(x2/steps),y);
  }
}

void blockRoof(float startx, float starty, float w, float h, int numAtt) {
  float y = random(h/2); // height of block
  if(y<20) y = 20; float space = random(5,13); //spacing between steps noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-h+y); vertex(startx+(w/space), starty-h+y); vertex(startx+(w/space), starty-h+y/2); vertex(startx+((w/space)*2), starty-h+y/2); vertex(startx+((w/space)*2), starty-h); //mid vertex(startx+w-((w/space)*2), starty-h); vertex(startx+w-((w/space)*2), starty-h+y/2); vertex(startx+w-(w/space), starty-h+y/2); vertex(startx+w-(w/space), starty-h+y); vertex(startx+w, starty-h+y); vertex(startx+w, starty); endShape(CLOSE); numAtt = attGroup3(startx, starty, w, h-y, numAtt); if(numAtt>0)
    attGroup2(startx+((w/space)*2)+5+random((startx+w-((w/space)*2))-(startx+((w/space)*2))-10), starty-h, w, random(h/6, h/4), startx, starty, numAtt);
}

void triangleRoof(float startx, float starty, float w, float h, int numAtt) {
  float y = random(h/15,h/5);
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y);
  vertex(startx+w/2, starty-h);
  vertex(startx+w, starty-h+y);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+w/2, starty, w, h+random(h/6, h/4), startx, starty, numAtt);
}

void roundRoof(float startx, float starty, float w, float h, int numAtt) {
  float y = random(h/5);
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y);
  bezierVertex(startx, starty-h, startx+w/2, starty-h, startx+w/2, starty-h);
  bezierVertex(startx+w, starty-h, startx+w, starty-h+y, startx+w, starty-h+y);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+5+random(w-10), starty, w, h+random(h/8, h/5), startx, starty, numAtt);
}

void oldRoof(float startx, float starty, float w, float h, int numAtt) {
  float x1 = overlap*0.75; //distance from edge
  float y1 = smallcap/4; //height
  float y2 = 3; //edge height
  if(h<=smallcap) { h = map(h, 0, smallcap, smallcap-smallcap/3, smallcap); float div = random(0.8, 1.8); startx += overlap*div; w -= (overlap*div)*2; x1 /= 3; y1 /= 3; y2 /= 3; } noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-h+y1-y2); vertex(startx+y2, starty-h+y1-y2); vertex(startx+y2, starty-h+y1); vertex(startx+x1, starty-h+y1); vertex(startx+x1, starty-h+y2); vertex(startx+x1-y2, starty-h); //mid vertex(startx+w-x1+y2, starty-h); vertex(startx+w-x1, starty-h+y2); vertex(startx+w-x1, starty-h+y1); vertex(startx+w-y2, starty-h+y1); vertex(startx+w-y2, starty-h+y1-y2); vertex(startx+w, starty-h+y1-y2); vertex(startx+w, starty); endShape(CLOSE); numAtt = attGroup3(startx, starty, w, h-y1, numAtt); if(numAtt > 0)
    attGroup4(startx, starty, w, h, numAtt);
}

void slantRoof(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(3,8); //horizontal size
  float y1 = random(3,15); //vertical size
  if(h<=smallcap) { h = map(h, 0, smallcap, smallcap-smallcap/3, smallcap); float div = random(0.8, 1.8); startx += overlap*div; w -= (overlap*div)*2; x1 /= 3; y1 /= 3; } noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-h+y1); vertex(startx-x1, starty-h); vertex(startx+w+x1, starty-h); vertex(startx+w, starty-h+y1); vertex(startx+w, starty); endShape(CLOSE); numAtt = attGroup3(startx, starty, w, h-y1, numAtt); } void thin(float startx, float starty, float w, float h, int numAtt) { thinused = true; float x1 = random(w/10, w/3); // start point of thin float x2 = random(w/2, w*.75); // width of thin float y1 = random(h/5, h/3); // height of first float y2 = random(h/4, h/2); // height of second noStroke(); fill(0); randomBuilding(startx+x1, starty, x2, h, numAtt); randomBuilding(startx-overlap/2, starty, overlap/2+x1+x2/2, y1, 0); randomBuilding(startx+x1+x2/2, starty, w-(x1+x2/2)+overlap/2, y2, 0); } // ******************** FULL DESIGNS ********************* // void var1(float startx, float starty, float w, float h, int numAtt) { startx -= overlap/3; w += (overlap/3)*2; float x1 = random(w/6, w/4); //slant width float y1 = random(h/4, h-h/4); //left slant start float y2 = random(20); //slant height float y1r = random(h/4,h-h/4); //right slant start int uneven = round(random(5)); noStroke(); fill(0); beginShape(); vertex(startx, starty); vertex(startx, starty-y1); vertex(startx+x1, starty-y1-y2); vertex(startx+x1, starty-h); vertex(startx+w-x1, starty-h); if(uneven > 0) {
    vertex(startx+w-x1, starty-y1r-y2);
    vertex(startx+w, starty-y1r);
  }
  else {
    vertex(startx+w-x1, starty-y1-y2);
    vertex(startx+w, starty-y1);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  attGroup1(startx+x1, starty, w-x1*2, h, numAtt);
}

void var2(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/6, w/4); //slant width
  float y1 = random(h-h/3); //bottom left slant start
  float y2 = random(20); //slant height
  float y1r = random(h-h/4); //bottom right slant start
  float y3 = random(h/3); //top height
  y1=constrain(y1,h/5,h-y3);
  y1r=constrain(y1,h/5,h-y3);
  int uneven = round(random(1));
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-y1);
  vertex(startx+x1, starty-y1-y2);
  vertex(startx+x1, starty-(h-y3-y2*2));
  vertex(startx+w/4, starty-(h-y3-y2));
  vertex(startx+w/4, starty-(h-y2));
    //mid
  vertex(startx+w/2, starty-h);
  vertex(startx+w/2+w/4, starty-(h-y2));
  vertex(startx+w/2+w/4, starty-(h-y3-y2));
  vertex(startx+w-x1, starty-(h-y3-y2*2));
  if(uneven == 1) {
    vertex(startx+w-x1, starty-y1r-y2);
    vertex(startx+w, starty-y1r);
  }
  else {
    vertex(startx+w-x1, starty-y1-y2);
    vertex(startx+w, starty-y1);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup2(startx+w/2, starty-h, w, random(h/7, h/4), startx, starty, numAtt);
}

void var3(float startx, float starty, float w, float h, int numAtt) {
  var3used = true;
  startx -= overlap/2;
  w += overlap;
  float y1 = random(h/5, h/2); // start of curve
  float x1 = random(5,w/4); // gap
  int bridges = round(random(1,5));
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y1);
  bezierVertex(startx, starty-h, startx+(w-x1)/2, starty-h, startx+(w-x1)/2, starty-h);
  vertex(startx+(w-x1)/2, starty);
  endShape(CLOSE);
  beginShape();
  vertex(startx+w, starty);
  vertex(startx+w, starty-h+y1);
  bezierVertex(startx+w, starty-h, (startx+w)-(w-x1)/2, starty-h, (startx+w)-(w-x1)/2, starty-h);
  vertex((startx+w)-(w-x1)/2, starty);
  endShape(CLOSE);
  boolean top = true;
  for(int i=0; i<bridges; i++) { float y2 = random(h/2); //height on building float x3 = random(3,8); //thickness if(top) { y2 = h-y2; top = false; } else top = true; rect(startx+(w-x1)/2,starty-y2,x1,x3); } if(numAtt>0)
    attGroup0(startx, starty, w, numAtt);
}

void var4(float startx, float starty, float w, float h, int numAtt) {
  var4used = true;
  float left = round(random(1));
  float xl = random(w/4, w/2); // left "waist" amount
  float xr = random(w/4, w/2); // right "waist" amount
  noStroke();  
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(left == 1)
    vertex(startx+xl, starty-h/2);
  vertex(startx, starty-h);
  vertex(startx+w, starty-h);
  if(left == 0)
    vertex((startx+w)-xr, starty-h/2);
  vertex(startx+w, starty);   
  endShape(CLOSE);
  float weight = random(1.5,5);
  strokeWeight(weight);
  stroke(0);
  if(left==1)
    line(startx+weight, starty-weight, startx+weight, starty-h+weight);
  else
    line(startx+w-weight, starty-weight, startx+w-weight, starty-h+weight);
  
  if(numAtt>0)
    attGroup1(startx, starty, w, h, numAtt);
}

void var5(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/3, w*.75); //width of curve
  int left = round(random(1));
  w += x1/2; // *2?
  noStroke();
  fill(0);
  beginShape();
  if(left == 1) {
    startx -= x1/2;
    vertex(startx, starty);
    bezierVertex(startx+x1, starty, startx+x1, starty-h, startx+x1, starty-h);
    vertex(startx+w, starty-h);
    vertex(startx+w, starty);
  }
  else {
    vertex(startx+w, starty);
    bezierVertex(startx+w-x1, starty, startx+w-x1, starty-h, startx+w-x1, starty-h);
    vertex(startx, starty-h);
    vertex(startx, starty);
  }
  endShape(CLOSE);
  
  if(numAtt>0) {
    if(left == 1)
      attGroup1(startx+x1, starty, w-x1, h, numAtt);
    else
      attGroup1(startx, starty, w-x1, h, numAtt);
  }
}

void var7(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/10, w/3); // width of side
  float x2 = random(w/10, w/3);
  float y1 = random(h); // height of side
  float y2 = random(h);
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y1);
  vertex(startx+x1, starty-h);
  vertex(startx+w-x2, starty-h);
  vertex(startx+w, starty-h+y2);
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup1(startx+x1, starty, w-x1-x2, h, numAtt);
}

void var8(float startx, float starty, float w, float h, int numAtt) {
  float y1 = random(h-h/4, h); // height of angle corner
  float y2 = random(h/3, h/2); // height of start angle
  int left = round(random(1));
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  if(left == 1) {
    vertex(startx, starty-y2);
    vertex(startx+w/3, starty-y1);
    vertex(startx+w, starty-h);
  }
  else {
    vertex(startx, starty-h);
    vertex(startx+w-w/3, starty-y1);
    vertex(startx+w, starty-y2);
  }
  vertex(startx+w, starty);
  endShape(CLOSE);
  
  if(numAtt>0) {
    if(left == 1)
      attGroup2(startx+5+w/3+random(w-w/3-5), starty-y1, w, random(h/5, h/3), startx, starty, numAtt);
    else
      attGroup2(startx+random(w-w/3-5), starty-y1, w, random(h/5, h/3), startx, starty, numAtt);
  }
}

void var9(float startx, float starty, float w, float h, int numAtt) {
  float origW = w;
  w = random(w-w/4, w);
  float y1 = random(8); // side slant height
  float y2 = 8; // top slant height
  float y3 = 10; // tower base height
  float x1 = w/3; // inward amount
  constrain(h, y1+y2+y3+5, smallcap);
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty);
  vertex(startx, starty-h+y2+y3+y1);
  vertex(startx+x1, starty-h+y2+y3);
  vertex(startx+x1, starty-h+y2);
  vertex(startx+w/2, starty-h);
    //mid
  vertex(startx+w-w/2, starty-h);
  vertex(startx+w-x1, starty-h+y2);
  vertex(startx+w-x1, starty-h+y2+y3);
  vertex(startx+w, starty-h+y2+y3+y1);
  vertex(startx+w, starty);
  endShape(CLOSE);
  fill(255);
  if(round(random(2)) > 0)
    ellipse(startx+w/2, starty-h+y2+y3/2, x1/3, x1/3);
    
  if(numAtt>0)
    attGroup0(startx, starty, origW, numAtt);
}

void var11(float startx, float starty, float w, float h, int numAtt) {
  float x1 = w/4; // width
  float x2 = random(3); // "wing"
  noStroke();
  fill(0);
  beginShape();
  vertex(startx+w/2-x1/2, starty);
  vertex(startx+w/2-x1/2, starty-h);
  vertex(startx+w/2-x1/2-x2, starty-h);
  vertex(startx+w/2, starty-h-random(4,10));
  vertex(startx+w/2+x1/2+x2, starty-h);
  vertex(startx+w/2+x1/2, starty-h);
  vertex(startx+w/2+x1/2, starty);
  endShape(CLOSE);
  
  if(numAtt>0)
    attGroup0(startx, starty, w, numAtt);
}

void var13(float startx, float starty, float w, float h, int numAtt) {
  float x1 = random(w/4,w); // width
  float x2 = random(3); // "wing"
  float y1 = random(4, 10);
  float start = startx + w/2;
  noStroke();
  fill(0);
  beginShape();
  vertex(start - x1/2, starty);
  vertex(start - x1/2, starty-h);
  vertex(start - x1/2 - x2, starty-h);
  vertex(start - x1/2 + x2*2, starty-h-y1);
  vertex(start + x1/2 - x2*2, starty-h-y1);
  vertex(start + x1/2 + x2, starty-h);
  vertex(start + x1/2, starty-h);
  vertex(start + x1/2, starty);
  endShape(CLOSE);
  
  if(round(random(1)) == 1) {
    beginShape();
    vertex(start - x2, starty-h-y1);
    vertex(start, starty-h-y1-y1/2);
    vertex(start + x2, starty-h-y1);
    endShape(CLOSE);
  }
  
  if(numAtt>0)
    attGroup0(startx, starty, w, numAtt);
}

// ******************** ATTACHMENT DESIGNS ********************* //

// limits to greenery
void attGroup0(float startx, float starty, float w, int numAtt) {
  while(numAtt>0) {
    tree(startx+random(w), starty);
    numAtt -= 10;
  }
}

// limits to blocks, antennas, greenery
void attGroup1(float startx, float starty, float w, float h, int numAtt) { //h = total height
  boolean antennaDone = false;
  while(numAtt > 0) {
    int doAntenna = round(random(1));
    int doSimple = round(random(1));
    if(numAtt>0 && doAntenna==1 && doSimple==0 && !antennaDone) { //two levels
      complexAntenna(constrain(startx+random(w),startx+10, (startx+w)-10), starty-h, random(h/7, h/4));
      numAtt -= 10;
      antennaDone = true;
    }
    if(numAtt>0 && doAntenna==1 && !antennaDone) {
      antenna(startx+5+random(w-10), starty-h, random(h/9, h/5));
      numAtt -= 10;
      antennaDone = true;
    }
    if(numAtt>0) {
      block(startx, startx+w, starty-h);
      numAtt -= 10;
    }
    //draw greenery, last resort
  }
}

// limits to single antenna starting at various heights (calculated beforehand), then greenery
void attGroup2(float startx, float starty, float w, float h, float treeStartx, float treeStarty, int numAtt) {
  antenna(startx, starty, h);
  numAtt -= 10;
  if(numAtt>0)
    attGroup0(treeStartx, treeStarty, w, numAtt);
}

// draws balconies alongside the two vertical edges, returns adjusted numAtt value
int attGroup3(float startx, float starty, float w, float h, int numAtt) {
  if(numAtt > 10) {
    balcony(startx, starty, h);
    balcony(startx+w, starty, h);
    return numAtt - 20;
  }
  return numAtt;
}

// limits to blocks, dishes, watertowers, and greenery
void attGroup4(float startx, float starty, float w, float h, int numAtt) { //h = total height
  boolean towerDone = false;
  boolean dishDone = false;
  while(numAtt > 0) {
    int doTower = round(random(5));
    int doDish = round(random(5));
    if(!towerDone && doTower == 1) {
      watertower(startx+5+random(w-25), starty-h, constrain(map(h,0,medcap,.1,.9), .1, .9));
      numAtt -= 10;
      towerDone = true;
    }
    if(!dishDone && doDish == 1 && numAtt > 0) {
      dish(startx+15+random(w-30), starty-h, constrain(map(h,0,medcap,.5,1), .5, 1));
      numAtt -= 10;
      dishDone = true;
    }
    if(numAtt>0) {
      block(startx+5, startx+w-10, starty-h);
      numAtt -= 10;
    }
    //draw greenery, last resort
  }
}

void antenna(float startx, float starty, float h) {
  strokeWeight(random(1.5,3));
  stroke(0);
  line(startx, starty, startx, starty-h);
}

void complexAntenna(float startx, float starty, float h) {
  float x1 = random(3,8); // distance between beams
  float y1 = random(h/7, h-h/4); // height of secondary beam
  float y2 = random(y1); //connector beam heights
  float y3 = random(y1);
  if(round(random(1))==1) x1 *= -1;
  strokeWeight(random(1.5,3));
  stroke(0);
  line(startx, starty, startx, starty-h);
  strokeWeight(random(1.5,3));
  line(startx+x1, starty, startx+x1, starty-y1);
  line(startx, starty-y2, startx+x1, starty-y2);
  line(startx, starty-y3, startx+x1, starty-y3);
}

void block(float startx, float endx, float starty) {
  float w = random(8, (endx-startx)-(endx-startx)/4); //width
  float h = -1*random(4, w/2); //height
  float x1 = random(startx, endx-w); //start position
  noStroke();
  fill(0);
  rect(x1, starty, w, h);
}

void balcony(float startx, float starty, float hval) {
  int amount = int(map(hval, 0, tallcap, 0, 40));
  float w = 8;
  float h = w/2;
  float x1 = random(w/3,w/2);
  noStroke();
  fill(0);
  for(int i=0; i<amount; i++) {
    rect(startx-x1, starty-(hval/amount)*i-h, w, h);
  }
}

void watertower(float startx, float starty, float scale) {
  float h = 22*scale;
  float w = 15*scale;
  float y1 = h/5.5; // barrel start
  float y2 = h/10; // barrel roof height
  float x1 = w*.8; // barrel width
  noStroke();
  fill(0);
  beginShape();
  vertex(startx, starty-y1);
  vertex(startx, starty-h+y2);
  bezierVertex(startx, starty-h,startx+x1/2, starty-h,startx+x1/2, starty-h);
  bezierVertex(startx+x1, starty-h,startx+x1, starty-h+y2,startx+x1, starty-h+y2);
  vertex(startx+x1, starty-y1);
  endShape(CLOSE);
  strokeWeight(2*scale);
  stroke(0);
  line(startx, starty, startx+w*.1, starty-y1);
  line(startx+x1, starty, (startx+x1)-w*.1, starty-y1);
  line(startx+x1/2, starty, startx+x1/2, starty-y1);
  strokeWeight(1.5*scale);
  line(startx+w, starty, startx+w, starty-h+y2);
  line(startx+w, starty-h+y2*2, startx+w-w*.3, starty-h+y2*2);
}

void dish(float startx, float starty, float scale) {
  float side = 25*scale;
  noFill();
  strokeWeight(2*scale);
  stroke(0);
  if(round(random(1)) == 1) {
    arc(startx, starty-side/2-5, side, side, PI/2, PI);
    line(startx-side*.35, starty, startx-side*.35, starty-side*.35);
    line(startx-side*.35, starty-side*.35, startx-side*.35+side*.2, starty-side*.35-side*.2);
  }
  else {
    arc(startx, starty-side/2-5, side, side, 0, PI-PI/2);
    line(startx+side*.35, starty, startx+side*.35, starty-side*.35);
    strokeWeight(1.5*scale);
    line(startx+side*.35, starty-side*.35, startx+side*.35-side*.2, starty-side*.35-side*.2);
  }
}

void tree(float startx, float starty) {
  int treeIndex = int(random(trees.size()));
  //int y = round(random(25, 50));
  PImage tree = trees.get(treeIndex);
  //tree.resize(0,y);
  image(tree, startx-tree.width/2, starty-tree.height+1);
}

Processing: Modified version of Justin Livi’s code for generating watercolor paintings

/*
 * WatercolorSediment
 * May, 2011
 *
 * Copyright 2011  Justin Livi
 * justinlivi.net
 * Written in Processing
 *
 */

int seedcount = 10; // default = 50
float h, maxh, start; // overall distance from center
float vspeed, rspeed;
float theta = 0;
float speed = 1;
float rot;
boolean up = false;
float[] hm; // hue array
float[] sm; // saturation array
float[] lm; // lightness array
float[] dhm; // delta hue array
float[] dsm; // delta saturation array
float[] dlm; // delta lightness array
float[] dm; // distance array
float[] stm; // streakiness array
HSL hsl = new HSL();

int count = 0;
boolean a = true;
boolean pause;

void setup() {
  size(1700, 1700);
  smooth();
  noStroke();
  background(255);
  if(width > height)
    maxh = height;
  else
    maxh = width;
  hm = new float[seedcount];
  sm = new float[seedcount];
  lm = new float[seedcount];
  dhm = new float[seedcount];
  dsm = new float[seedcount];
  dlm = new float[seedcount];
  dm = new float[seedcount];
  stm = new float[seedcount];
  reset();
}

void draw() {
  if(pause) { return; }
  if (theta < width) {
    pushMatrix();
      translate(theta, 0);
      rotate(PI/2);
      generate();
    popMatrix();
    theta++;
  }
  else if(count<400) {
    if(a) {
      saveFrame("paintings/painting-"+count+"_a.png");
      a = false;
    }
    else {
      saveFrame("paintings/painting-"+count+"_b.png");
      a = true;
      count++;
    }
    reset();
  }
}

void mousePressed() {
  pause = !pause;
  //background(255);
  //reset();
}

void reset() {
  seedcount = (int)random(8, seedcount); 
  float centerhue = random(0, 360);
  float variance = random(10, 40);
  for(int count = 0; count < seedcount; count++) {
    hm[count] = random(centerhue-variance, centerhue+variance);
    sm[count] = random(45, 70);
    lm[count] = random(20, 90);
    dhm[count] = hm[count];
    dsm[count] = sm[count];
    dlm[count] = lm[count];
    dm[count] = random(maxh);
    stm[count] = random(.1, 1);
  }
  dm[0] = 0;
  dm[seedcount-1] = height;
  dm = sort(dm, seedcount);
  h = 0;
  start = h;
  theta = -20;
  vspeed = random(1, 9);
  rspeed = random(1, 4);
}

void generate() {
  for(int count = 0; count < seedcount; count++) { changeDm(count); fill(hsl.toRGB(dhm[count],dsm[count],dlm[count],100)); blend(count, dm[count]); } dm = sort(dm, seedcount); } // ---------------------- POSITION! ------------------------------------ // void changeDm(int count) { int prev = 0; int next = seedcount-1; if(count > 0)
    prev = count-1;
  if(count < seedcount-1)
    next = count+1;
  if(count == seedcount-1)
    prev = seedcount-1;
  if(count == 0)
    next = 0;
  dm[count] = constrain(dm[count]+random(-1, 1), 0, height);
  if (count == 0)
    dm[count] = 0;
  else if (count == seedcount-1)
    dm[count] = height;
}


// ---------------------- BLEND! ------------------------------------ // 

void blend(int count, float distance) {
  int prev = seedcount-1;
  if(count < seedcount-1)
    prev = count+1;
  float formax = abs(dm[prev]-distance);
  changeColor(count);
  for(int count2 = 0; count2 < formax; count2++) {
    for(int count3 = 0; count3 < (10/3.0*stm[count]+5/3.0); count3++) {
      float hi = (dhm[prev]-dhm[count])/formax;
      float si = (dsm[prev]-dsm[count])/formax;
      float li = (dlm[prev]-dlm[count])/formax;
      fill(hsl.toRGB(dhm[count]+random(-1,1)+(count2*hi),
                    dsm[count]+random(-.5,.5)+(count2*si),
                    dlm[count]+random(-.5,.5)+(count2*li), random(2, 10)));
      pushMatrix();
        translate(distance+count2+random(-1,1), random(-stm[count]*10,stm[count]*10));
        rotate(random(PI*2));
        ellipse(0, 0, random(2, 10+5*stm[count]), random(2, 10+5*stm[count]));
      popMatrix();
    }
  }
}

void changeColor(int count) {
  stm[count] += random(-.01, .01);
  stm[count] = constrain(stm[count], .1, 1);
  dhm[count] += random(-stm[count], stm[count]);
  dhm[count] = constrain(dhm[count], hm[count]-20, hm[count]+20);
  dsm[count] += random(-stm[count], stm[count]);
  dsm[count] = constrain(dsm[count], sm[count]-20, sm[count]+20);
  dlm[count] += random(-stm[count], stm[count]);
  dlm[count] = constrain(dlm[count], lm[count]-20, lm[count]+20);
}

class HSL {
  color toRGB(float H, float S, float L) {
    float R = 0, G = 0, B = 0;
    H /= 360;
    S /= 100;
    L /= 100;
    float temp1 = 0, temp2 = 0, Rtemp3 = 0, Gtemp3 = 0, Btemp3 = 0;
    if (S == 0) {
      R = L;
      G = L;
      B = L;
    }
    else {
      if (L < 0.5) temp2 = L*(1.0+S); else if (L >= 0.5)
        temp2 = L+S-L*S;
      temp1 = 2.0*L-temp2;
      Rtemp3 = H+1.0/3.0;
      
      if (Rtemp3 < 0) Rtemp3 = Rtemp3 + 1.0; if (Rtemp3 > 1)
        Rtemp3 = Rtemp3 - 1.0;
        
      Gtemp3 = H;
      if (Gtemp3 < 0) Gtemp3 = Gtemp3 + 1.0; if (Gtemp3 > 1)
        Gtemp3 = Gtemp3 - 1.0;
        
      Btemp3 = H-1.0/3.0;
      if (Btemp3 < 0) Btemp3 = Btemp3 + 1.0; if (Btemp3 > 1)
        Btemp3 = Btemp3 - 1.0;
        
      if (6.0*Rtemp3 < 1)
        R = temp1+(temp2-temp1)*6.0*Rtemp3;
      else if (2.0*Rtemp3 < 1)
        R = temp2;
      else if (3.0*Rtemp3 < 2)
        R = temp1+(temp2-temp1)*((2.0/3.0)-Rtemp3)*6.0;
      else
        R = temp1;
        
      if (6.0*Gtemp3 < 1)
        G = temp1+(temp2-temp1)*6.0*Gtemp3;
      else if (2.0*Gtemp3 < 1)
        G = temp2;
      else if (3.0*Gtemp3 < 2)
        G = temp1+(temp2-temp1)*((2.0/3.0)-Gtemp3)*6.0;
      else
        G = temp1;
        
      if (6.0*Btemp3 < 1)
        B = temp1+(temp2-temp1)*6.0*Btemp3;
      else if (2.0*Btemp3 < 1)
        B = temp2;
      else if (3.0*Btemp3 < 2)
        B = temp1+(temp2-temp1)*((2.0/3.0)-Btemp3)*6.0;
      else
        B = temp1;
    }
      
    R *= 255;
    B *= 255;
    G *= 255;
    
    return color((int)R, (int)G, (int)B);
  }
  
  color toRGB(float H, float S, float L, float A) {
    float R = 0, G = 0, B = 0;
    H /= 360;
    S /= 100;
    L /= 100;
    float temp1 = 0, temp2 = 0, Rtemp3 = 0, Gtemp3 = 0, Btemp3 = 0;
    if (S == 0) {
      R = L;
      G = L;
      B = L;
    }
    else {
      if (L < 0.5) temp2 = L*(1.0+S); else if (L >= 0.5)
        temp2 = L+S-L*S;
      temp1 = 2.0*L-temp2;
      Rtemp3 = H+1.0/3.0;
      
      if (Rtemp3 < 0) Rtemp3 = Rtemp3 + 1.0; if (Rtemp3 > 1)
        Rtemp3 = Rtemp3 - 1.0;
        
      Gtemp3 = H;
      if (Gtemp3 < 0) Gtemp3 = Gtemp3 + 1.0; if (Gtemp3 > 1)
        Gtemp3 = Gtemp3 - 1.0;
        
      Btemp3 = H-1.0/3.0;
      if (Btemp3 < 0) Btemp3 = Btemp3 + 1.0; if (Btemp3 > 1)
        Btemp3 = Btemp3 - 1.0;
        
      if (6.0*Rtemp3 < 1)
        R = temp1+(temp2-temp1)*6.0*Rtemp3;
      else if (2.0*Rtemp3 < 1)
        R = temp2;
      else if (3.0*Rtemp3 < 2)
        R = temp1+(temp2-temp1)*((2.0/3.0)-Rtemp3)*6.0;
      else
        R = temp1;
        
      if (6.0*Gtemp3 < 1)
        G = temp1+(temp2-temp1)*6.0*Gtemp3;
      else if (2.0*Gtemp3 < 1)
        G = temp2;
      else if (3.0*Gtemp3 < 2)
        G = temp1+(temp2-temp1)*((2.0/3.0)-Gtemp3)*6.0;
      else
        G = temp1;
        
      if (6.0*Btemp3 < 1)
        B = temp1+(temp2-temp1)*6.0*Btemp3;
      else if (2.0*Btemp3 < 1)
        B = temp2;
      else if (3.0*Btemp3 < 2)
        B = temp1+(temp2-temp1)*((2.0/3.0)-Btemp3)*6.0;
      else
        B = temp1;
    }
      
    R *= 255;
    B *= 255;
    G *= 255;
    
    return color((int)R, (int)G, (int)B, (int)A);
  }
}

Processing: Combining city masks and paintings to create final images

PImage painting;
PImage drawing;
int index = 17;
int savedIndex = -1;
String file = "../Watercolor_JustinLivi/paintings/painting-"+int(random(400))+"_a.png";

ArrayList used = new ArrayList();

void setup() {
  size(1700,1700);
  reload();
}

void draw() {
}

void keyPressed() {
  if (key == 'b' || key == 'B') {
    index--;
    if(index<0) index = 365; reload(); } if (key == 'n' || key == 'N') { index++; if(index >= 366) index = 0;
    reload();
  }
  if (key == 's' || key == 'S') {
    String filename = "renders/render"+index+".png";
    saveFrame(filename);
    if(savedIndex == index)
      used.remove(index);
    used.add(index, file);
    savedIndex = index;
  }
  if (key == 'r' || key == 'R') {
    reload();
  }
}

void reload() {
  background(255);
  String prevFile = file;
  while(used.contains(file) || prevFile.equals(file)) {
    if(round(random(1)) == 1)
      file = "../Watercolor_JustinLivi/paintings/painting-"+int(random(400))+"_a.png";
    else 
      file = "../Watercolor_JustinLivi/paintings/painting-"+int(random(400))+"_b.png";
  }
  println("drawing"+index+".png + "+ file);
  painting = loadImage(file);
  drawing = loadImage("../city_generator/drawings/drawing"+index+".png");
  drawing.filter(INVERT);
  painting.mask(drawing);
  image(painting, 0, 0);
}

Javascript/Basil.js: Create final Indesign/PDF file

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";

var StringData = b.loadString("data.txt");
var data = b.loadString('data.txt').split("\n");

var side = 7.5*72;
var img;

function parseData(texts) { 
  var hourlyData = [];
  var hours = texts.split("-");
  hourlyData.push([hours[0]]);
  for(var i=1; i<hours.length; i++)
    hourlyData.push(hours[i].split("&"));
  return hourlyData;
}

function getMetaData(hourlyData){
  var info = [];
  info.push(hourlyData[0][0]);
  var texts = 0;
  var chars = 0;
  var atts = 0;
  for(var i=1; i<hourlyData.length; i++) {
    texts += parseInt(hourlyData[i][0]);
    chars += parseInt(hourlyData[i][1]);
    atts += parseInt(hourlyData[i][2]);
  }
  info.push(texts);
  info.push(chars);
  info.push(atts);
  return info;
}

function setup() {

  b.clear (b.doc());
  
  b.noStroke(); 
  img = b.image("dedication_s.png", 0, 0, side, side);
  b.fill(255,255,255);
  b.textSize(12);
  b.textFont("Avenir","Light"); 
  b.textAlign(Justification.CENTER_ALIGN); 
  b.text("for nicole", 0, side/2-8, side, 16);

  // b.fill(175,175,175);
  // b.textSize(13);
  // b.text("special thanks to", 0, side/2-58, side, 16);
  // b.textSize(10);
  // b.text("golan levin", 0, side/2-8, side, 16);
  // b.text("justin livi", 0, side/2+12, side, 16);
  // b.text("adam knuckey", 0, side/2+32, side, 16);

  return

  for(var i=0; i<data.length; i++) {
    b.addPage();

    var date = getMetaData(parseData(data[i]))[0].split(",")[0].split(" ");
    var date_space = 10;
    
    b.fill(175,175,175);
    var tbh = 30;
    b.textSize(tbh-5);
    b.textFont("Avenir","Light"); 
    b.textAlign(Justification.RIGHT_ALIGN);

    b.text(date[0].toUpperCase().substring(0,3), 0, side/2-tbh/2, side/2-date_space/2, tbh);
    b.textFont("Avenir","Black"); 
    b.textAlign(Justification.LEFT_ALIGN);
    b.text(date[1], side/2+date_space/2, side/2-tbh/2, side/2, tbh);

    var y = side-72;
    var iconHeight = 15;
    var chatWidth = iconHeight*1.6;
    var leftSpace = 5;
    var typeX = chatWidth+leftSpace+chatWidth*1.5;
    var clipX = typeX+chatWidth+leftSpace+chatWidth*2;
    var totalWidth = clipX + chatWidth*0.55 + leftSpace + chatWidth*2;
    var x = side/2-totalWidth/2;

    b.textSize(10);
    b.fill(220,220,220);
    b.textFont("Avenir","Black"); 

    img = b.image("chat.png", x, y, chatWidth, iconHeight);
    b.text(getMetaData(parseData(data[i]))[1], x+chatWidth+leftSpace, y+1.5, chatWidth*1.5, 15);
    img = b.image("type.png", x+ typeX, y+1.5, chatWidth, iconHeight*.8);
    b.text(getMetaData(parseData(data[i]))[2], x+typeX+chatWidth+leftSpace, y+1.5, chatWidth*2, 15);
    img = b.image("clip.png", x+ clipX, y+1.5, chatWidth*0.55, iconHeight*.8);
    b.text(getMetaData(parseData(data[i]))[3], x+clipX+chatWidth*0.55+leftSpace, y+1.5, chatWidth*2, 15);


    b.addPage();

    img = b.image("renders/render"+i+".png", 0, 0, side, side);
  }
}

b.go(); 

hizlik- faceosc

Update: As of 2020, an updated documentation for this project is now on my website at hiz.al/lightspeed.

wallpaper

infinite

I really enjoyed making this interactive work, as it is both very pleasing for me to look at and a fun experience to be able to manipulate an imagery that has long since been static, in movies and games. Using your head, you can adjust the point of view in this virtual world, looking around the “corner” and moving side-to-side. One thing I would change if I had the time would be to spread the stars out amongst the space and have a true sense of filled space. Right now, at times, it’s evident that they’re all coming from this once central point, almost creating a tunnel. I tried to add depth by making some thin/small lights with more transparency but it’s not as good. I’m sure I’ll work on it some more this weekend.

loop

screen-shot-2016-10-14-at-12-03-05-am screen-shot-2016-10-14-at-12-02-55-am screen-shot-2016-10-14-at-12-02-47-am

 

import oscP5.*;
OscP5 oscP5;

int found;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();
float[] rawArray;
boolean showArray = false;

Coord center;
Coord new_center;
Coord bz_center;
Coord new_bz_center;
float speed = 0.02;
float ease = 0.1;
ArrayList lines = new ArrayList();
color[] colors = new color[4];

void setup() {
  //size(1280, 720, OPENGL);
  //size(640, 360, OPENGL);
  fullScreen(OPENGL);
  smooth(); 
  background(0);
  //frameRate(30);

  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "rawData", "/raw");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  
  colors[0] = color(169,241,230,200);
  colors[1] = color(80,214,207,200);
  colors[2] = color(11,168,159,200);
  colors[3] = color(255,255,255,200);
  
  center = new Coord(width/2, height/2);
  new_center = new Coord(width/2, height/2);
  bz_center = new Coord(width/2, height/2);
  new_bz_center = new Coord(width/2, height/2);
  for(int i=0; i<500; i++) {
    lines.add(new BezierLine()); 
  }
}

void draw() {  
  // blur effect
  noStroke();
  fill(0,0, 0, 80);
  rect(0, 0, width, height);
  //background(0);
  
  if(found > 0) {
    new_bz_center.x= width-map(poseOrientation.y, -0.25, 0.25, 0, width);
    new_bz_center.y= height-map(poseOrientation.x, -0.25, 0.25, 0, height);
    new_center.x = map(posePosition.x, 100, 600, 0, width);
    new_center.y = height-map(posePosition.y, 100, 400, 0, height);
  }
  else {
    new_bz_center.x=mouseX;
    new_bz_center.y=mouseY;
    bz_center.x=mouseX;
    bz_center.y=mouseY;
    if(mousePressed) {
      new_center.x = mouseX;
      new_center.y = mouseY;
    }
  }
  
  move();
  
  fill(255);
  for(int i=0; i 	 	 1) {
       center.x += d_cx * ease;
       center.y += d_cy * ease;
   }
   if (bz_distance > 1) {
       bz_center.x += d_bzx * ease;
       bz_center.y += d_bzy * ease;
   }
}

class BezierLine {
  private Coord end;
  private float distance;
  private int delay;
  private Coord vary;
  private color c;
  
  public BezierLine() {
    delay = int(random(100));
    int vary_amount = 5;
    vary = new Coord(random(-1*vary_amount,vary_amount), random(-1*vary_amount,vary_amount));
    resetEndpoint();
  }
  
  private void resetEndpoint() {
    float angle = random(360);
    float radius = sqrt((width*width)+(height*height))/2;
    float x = width/2 + cos(angle)*radius;
    float y = height/2 + sin(angle)*radius;
    end = new Coord(x,y);
    distance = constrain(random(-1,speed), 0, speed);
    int picker = (int)random(0,colors.length);
    c = colors[picker];
    println(picker);
  }
  
  public void draw() {
    if(delay > 0) {
      delay --;
      return;
    }
    float x1 = center.x + vary.x;
    float y1 = center.y + vary.y;
    float x2 = center.x + vary.x;
    float y2 = center.y + vary.y;
    float x3 = bz_center.x;
    float y3 = bz_center.y;
    float x4 = end.x;
    float y4 = end.y;
    
    noFill();
    stroke(255);
    //bezier(center.x, center.y, center.x, center.y, bz_center.x, bz_center.y, end.x, end.y);
    float len = map(distance, 0, 1, 0, random(.05));
    float thickness = map(distance, 0, 1, .5, random(1.5, 4));
    float tx1 = bezierPoint(x1, x2, x3, x4, distance);
    float ty1 = bezierPoint(y1, y2, y3, y4, distance);
    float tx2 = bezierPoint(x1, x2, x3, x4, constrain(distance+len, 0, 1));
    float ty2 = bezierPoint(y1, y2, y3, y4, constrain(distance+len, 0, 1));
    stroke(c);
    strokeWeight(thickness*2);
    line(tx1, ty1, tx2, ty2);
    stroke(255,255,255,128);
    strokeWeight(thickness);
    line(tx1, ty1, tx2, ty2);
    distance+=speed;
    if(distance > 1) {
      resetEndpoint();
    }
  }
}

class Coord {
  public float x;
  public float y;
  
  public Coord(float new_x, float new_y) {
    this.x = new_x;
    this.y = new_y;
  }
}

// OSC CALLBACK FUNCTIONS

public void found(int i) {
  //println("found: " + i);
  found = i;
}

public void rawData(float[] raw) {
  rawArray = raw; // stash data in array
}

public void posePosition(float x, float y) {
  //println("pose position\tX: " + x + " Y: " + y );
  //println(center.x + ", " + center.y);
  posePosition.set(x, y, 0);
}

public void poseOrientation(float x, float y, float z) {
  //println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}

hizlik-LookingOutwards04

When I had seen this a few years ago (my friend had sent me a link) I couldn’t stop laughing. Why? Because in all the times I played Minecraft I’d forgotten just how ridiculous the idea of hitting (or punching) a block to gather resources was. To do anything in the survival mode, first thing you need is wood, which is achieved by punching trees. Need dirt to build temporary walls? Punch dirt (or hit it with anything). Seeing this in “real life” just made those silly actions become a reality, and for me this was just an emphasis of that feeling. The artist, Ben Purdy, made 3 of these videos (I originally only saw one) but it’s a shame he hasn’t done anything with it. This has great potential to be an public or education-based interactive artwork or exhibition. I can see this being used in very lower-aged groups, such as elementary kids (perhaps ages so young they haven’t even played Minecraft as a sort of digital/interactive-art building block or learning method.

Unfortunately I don’t find this particularly inspiring for my own work, however it is an enjoyable piece of interactive work that has obviously required some special thinking and meticulous work (such as managing to perfectly project onto the sides of the box using one(?) projector… I believe in the 3rd video he uses multiple projectors for even-sided projection.

hizlik-Plot

Update: As of 2020, an updated documentation for this project is now on my website at hiz.al/lineplotter.

img_7208

img_7210

This project doesn’t have sketches because I began my process with an intent on experimenting with my previous project, Loops. And my experiment was so successful that I never had reason to try another idea. Instead of drawing the cycloid circles in the Loop project, I tried plotting their center points throughout their movement in a circle (within a circle, within a circle, etc). This worked out beautifully, creating an intricate, delicate looking pattern. I then found an optimal variable to modify in as many ways as I could think of, changing the constants and variables with different mathematical operations, negative values, and additional constants and value changes. I named each work after it’s mathematical expression, which also helped in recreating it later if I needed to revisit it.

ezgif-com-gif-makerimg_7498

This is the original loop project, followed by an example of my modified visuals for this project drawing themselves:

bwdrawdemo

I created over 20 unique designs, a few of which are outlined below:

0-5xi 2i div1-5-0-5i div3-0-5xi div3-0-1xi

collage

hizlik-Clock-Feedback

Update: As of 2020, an updated documentation for this project is now on my website at hiz.al/tree.

A majority of my feedback had two main themes. One was that a lot of people found my clock to be pleasant to watch, even calling it ‘meditative.’ A particular point of interest was the minute-to-minute transition (with the blossoms flying off). The other, more critical feedback was that my clock is hard to read, especially without some directions regarding what everything means. While I understand that, I figured that would be the issue when making a more “realistic” representation of nature through time-based changes, so I am not too worried. There are generally two types of clock artworks anyway, ones that represent time and ones that use time to change some aspects of it. This is somewhere in between but I guess leaning more towards the “using time to make a visual” side. Something that I don’t like but cannot change is how the tree looks at early hours (with few branches). Something that I don’t like but could change is the logic in the generation of branches, mainly involivng starting points to prevent odd-shaped trees.

Also, to the one person that mentioned seeing the actual timestamp in the top corner occasionally: if you press ‘d’ it enters ‘debug mode’ which shows time.

hizlik-Interruptions

sketch (link on GitHub)

This was an interesting project to work on- upon quick observation I knew the tactic I would use to recreate the lines (having a grid of center points that lines of certain length would randomly pivot on) and place them within a padded canvas (to prevent lines from going off-screen). The challenging part for me was the missing lines, and how to achieve them. My approach was to use multiple levels of randomGaussian() and random() functions. I would have a random amount of “blank spots” that had a random amount of “spread” (size of spot), which had a random number of lines within that spread hide or remain showing. While it looks similar to the original artworks, my rendering doesn’t have the same sense of “closeness” some of the original artworks portray in their blank spots. Like tight “tubes” of white, mine are more like soft areas/circles of blank.

hizlik-Reading03

A-  I don’t mean to be repetitive here, but my LookingOutwards03 assignment is very relevant to this situation. Tom Beddard’s Aurullia is I think a very good example of effective complexity- it was generated from an algorithm (a fractal formula) which is a very ordered method, but the final product is way more difficult to establish a category for (in terms of complexity as a random or ordered state). I would argue it is closer to ordered (you can expect a great many of things from the unknown parts of this “world”, such as there are no floating objects, buildings will be gray, there is a certain maximum height and minimum height, etc. The reason why I say it is not complete order, is because it is not repetitive and entirely predictable, in terms of anything from shapes of buildings to patterns amongst the “streets” and cracks.

B- “Problem of Intent”  I personally find myself using random or generative procedures as a sort of computer-self-exploration, where the computer tests, experiences, and explores designs and patterns and I benefit from it’s findings. Or i use random factors for times when I don’t want to control it, or don’t have any idea how to control it, and would rather have the computer do it for me. Often I am pleased with the results and work off of it rather than leave it as the artwork itself. However I do believe an artist should have a meaning for using random factors, and a purpose like mine (don’t know what to do if it weren’t random) doesn’t seem like a good enough reason.

hizlik-LookingOutwards03

Click here to check out the website!

This project, Aurulia by Tom Beddard, and the technology he uses, is amazing! Like I mentioned in my LookingOutwards02 assignment, I love large-scale, 3d cityscapes and renderings, so when I saw a thumbnail that look looked like a city, I immediately clicked on it but was blown away once I started reading into it. Tom interprets Mandalay’s fractal formula in this cityscape way, somehow creating these scenes that don’t quite look repetitive like fractal algorithms generally look like. I absolutely love the detail in it, the realism yet abstraction of this impossible cityscape, and the fact that it was algorithmically generated. I am quite speechless really. The flowing streets and interesting outcropping of buildings, odd round “holes” or “dents” in the mass of flat buildings that almost create a “floor” like Coruscant from Star Wars… it’s all mesmerizing.

I do not know much about Mandalay’s fractal formula, but I do know what fractals are and how they’ve been used for 2D artworks and designs. I have never seen them in 3D before, and this video included in the site also blew me away with how easy it is to create them! It’s almost like cheating, like i could use any screenshot as an artwork but it looks like it actually had very minimal effort to create it. This project has a nice balance between order and disorder- it looks, feels and acts like a real city, but it’s actually created using the randomnes, disorderness yet orderliness of a fractal formula. Like I mentioned above, he managed to break away from the orderly repetitiveness of a fractal formula to create this somewhat organic and varying landscape.

hizlik-AnimatedLoop

Update: As of 2020, an updated documentation for this project is now on my website at hiz.al/lineplotter.

bw

I am not too proud of this project- I’m not sure why but there are some tasks that I just have no imagination for- I was mesmerized by all the great things I saw but had zero ideas for anything good. So I just coded two different types of loops out of experimentation and the first looked better (the second was based on squares). I guess I do better with more conceptual or themed projects. I also made a second version with different colors:

green

Here are some notes on the project:

img_7472

You can view the code on github, or see it below:

float x;
float y;
int count = 0;

void setup() {
  size(480, 480);
  background(0);
  noStroke();
  fill(255);
  ellipse(width/2, height/2, min(width, height), min(width, height));
  fill(255,0,0);
  
  float ballR = min(width, height)/2;
  float pathR = min(width, height)/2 - ballR/2;
  float mDiv = 1000.0;
  float t = millis()/mDiv;
  float x = width/2+pathR*cos(t);
  float y = height/2+pathR*sin(t);
  ellipse(x,y,ballR,ballR);
  
  for(int i=0; i<5; i++) {
    fill(200-(i*50), 0,0);
    mDiv /= 2;
    t = millis()/mDiv;
    ballR /= 2;
    pathR = pathR/2;
    x = x+pathR*cos(t);
    y = y+pathR*sin(t);
    ellipse(x,y,ballR,ballR);
  }
}

void draw() {
  count++;
  background(255);
  
  fill(0);
  ellipse(width/2, height/2, min(width, height), min(width, height));
  fill(255);
  float ballR = min(width, height)/2;
  float pathR = min(width, height)/2 - ballR/2;
  float mDiv = 1000.0;
  float t = millis()/mDiv;
  float x = width/2+pathR*cos(t);
  float y = height/2+pathR*sin(t);
  ellipse(x,y,ballR,ballR);
 
  boolean fillB = true;
  
  for(int i=0; i<5; i++) {
    noStroke();
    if(fillB) fill(0);
    else fill(255);
    fillB = !fillB;
    //fill(200-(i*50), 0,0);
    mDiv /= 2;
    t = millis()/mDiv;
    ballR /= 2;
    pathR = pathR/2;
    x = x+pathR*cos(t);
    y = y+pathR*sin(t);
    ellipse(x,y,ballR,ballR);
  }
  saveFrame();
}  

hizlik- LookingOutwards02

[Sorry for being late]

ovalwalker

I’ve always been inspired by large-scale projects and artworks, especially in the realm of video games/3D models. For example, I am fascinated by sprawling Minecraft creations and super-detailed, vast landscapes and cityscapes created for games like Grand Theft Auto. In movies like Star Wars, detailed 3D renderings of cities that go as far as the eye can see (like Coruscant) have also inspired me (which just resulted in using them as computer wallpapers). I’ve also in the past dabbled in Google Earth model-ripping software that allowed you to export real-world 3D cities into 3DS Max to work with.

The latest in the world of large-scale projects is perhaps the largest of them all, No Mans Sky. It is a space exploration game where you can visit planets, starships, and space stations by flying around in a ship, collecting minerals and materials to trade and craft with. What makes this game so unique is the complex mathematical algorithms and logic used to randomly generate planets, creatures, plant life, atmosphere, and “properties” (gravity, toxicity, etc). The team developed this in-house, with the help of a team of mathematicians and a graphic designer. Although randomly generated biology existed before, applying them to such a variety of 3D model applications on such a large-scale, and public, project has never been done before.

Details about the game’s creation, the methods used and about the team are available in this interview on Kotaku.

HIZLIK-Clock

Update: As of 2020, an updated documentation for this project is now on my website at hiz.al/tree.

How to read: #branches = hours, length of current hour/branch = minutes, #blossom clusters = seconds.

This project was quite fun to do- although the initial idea of using a recursive-style (but not really) generative tree seemed hard, the most time consuming part of this project was the more subtle things, such as how many branches were allowed to come out of a parent branch, the artistic design (strokeWeight, colors, etc), other “rules” regarding positioning and working on animations.

Although it seemed optional, I felt it was quite important to have the ability to re-grow your tree at any point (by clicking), and as an aesthetic design, having the branches “grow” into place quickly to catch up to the current time. I also felt adding the “floating” death animation for the blossoms was a nice touch, something better than just disappearing.

The hardest part of this project was actually debugging. As I added tools to help me develop (such as the ability to speed up time or force a specific time of day), I realized more and more how many issues there were that I couldn’t notice real-time, and figuring out what was wrong was quite an adventure.

24-hr-gif

screen-shot-2016-09-16-at-12-22-15-am screen-shot-2016-09-16-at-12-22-21-am

hizlik – FirstWordLastWord

After reading this, I’m kind of surprised I had never thought of dividing significant works of art, or moments in time, based on if it was “groundbreaking” or “best ever” of a particular subject, medium or substance. And I was taken aback again by the same feeling once i got to the end and once again, something I hadn’t though of (both first word art and last word art).

I can sometimes relate to the things Naimark mentions, and feel I’m more sort of a last-word-art(ist). I thoroughly enjoy making a preexisting idea or refining a preexisting piece to perfection, rather than starting from scratch and thinking of something no one has thought of. I often do try to think of things that are first word art, pieces that haven’t been made ever, software that’s never been thought of, designs that haven’t been invented yet. But most of the time I end up running in circles in my mind, only to give up, be inspired by something I see on the web, and build/work off of that to create newer, different yet still somewhat familiar projects. Most specifically I can relate to my PowerPoint artworks, games and projects, all of which use a preexisting platform (MS Office) and tools/shapes but I end up creating something no one has thought of (that I’ve met in person, so far).

hizlik- LookingOutwards01

See more on her website.

Anouk Wipprecht is a fashion designer who incorporates technology, electronics, and modern designs into her clothing (often dresses). For example, creating an Audi-inspired, headlight-infused, parking-sensitive dress. I really enjoyed seeing how she used her skills, her views and her method of thinking outside the box to send a message, or create a vision, that is more striking and memorable than, say, generic models for events like car shows or concerts. Going back to the Audi example, how she “hacked” a car into a dress. Or how she repurposed football shoulder pads for a (female) singer in a halftime show at the Super Bowl.

She presents her work very straightforward, almost too quickly, but effectively (with plenty of documentation and little stories to keep us interested). I love her wording sometimes, like “Hacking a car”.

“For me, this is the poetics of technology,” she said, which I’ve never heard someone talk about technology like that. I like how she uses all kinds of technology, from interactive (sensors) to constructive (3D printing, mechanical parts) to sensory (sounds, lights, atmosphere). She combines them or uses them individually to make powerful pieces. I’d love to see her works (in action) in person! It’s a shame all of her works are temporary (since they are to be worn once).