Antar-book

OKAY BABE

Summary:

  • A French fold book with typography wallpapers, with 70’s porn between the pages
  • The content is from a random selection of the 100 most recent tweets that contained the phrase “okay babe”
  • The tweets were then cleaned up to only contain the text that was proper English. I also took out the URLs, @mentions, acronyms, sounds and also the phrase “okay babe” itself.
  • Each page contains one of the cleaned tweets, rendered with a basil.js script
  • There were three scripts:
    • Growth: each character of the tweet grows in point size linearly
    • Sine: each character of the tweet grows in point size sinusoidally
    • DoubleSine: the same as sine, but additionally the baseline shift of each character grows sinusoidally
  • Scanned pages from Playboy’s March 1970 issue are the inside page images

Here’s a video of Professor Levin flipping through my OKAY BABE book:

I have been taking this class as an opportunity to explore my own style and creative voice, and this project was a particularly fun way to do some discovering. At the beginning of sophomore year, I conceived the phrase OKAY BABE, and it has since made appearances in my work, and has also become my signature on my Instagram. I use OKAY BABE as a filler text, it’s my lorum ipsum, that can carry very different typographic voices depending on the visual context that it is in.

I wanted to use this project to see how other people use “okay babe”, and what voices they are using.  I also wanted to have a very type heavy book and explore the power of basil.js. I first thought of going to NPR or NYT to search for articles that contained “okay babe”. This was, probably obviously, pretty unsuccessful. The phrase is so casual and colloquial that it wasn’t really used in more formal conversations that happen during NPR stations or articles in the Times. This is when I decided to turn to twitter, where I had a new, opposite, challenge. The language of twitter can be so causal that some tweets only contained phrases like, “@Tayatyy hahahahah yaa wtvr okay babe!!!”. I still wanted my content to be sophisticated language, but said in a casual context.

As a communication designer I really felt in my realm here. I love book design and typography, and just learned how to bind books by hand. In terms of a visual context I wanted to use this program to make adjustments to type that wouldn’t be really possible by hand. A lot of my work plays with repetition and pattern, so I saw this as an opportunity to mix this with my love for type. However, pages of repetitive patterns are great, but they are repetitive. I wanted to incorporate images without obstructing the type patterns. After learning about book binding this semester in my communications studio, I thought that this project would be a great opportunity to use french fold perfect binding. This is where the outside edge of the pages are not cut, allowing the reader to peak in between two pages. This is where I thought that I could create a nice narrative for the book: While the pages look like repetitive (perhaps dull) wallpapers, between the pages (behind the walls), reveals risqué images. This allowed for the perfect opportunity to borrow my friend’s vintage 1970’s March issue of Playboy magazine. In the 70’s the pornography industry was significantly more conservative than it is now. The girls are almost never completely nude, and most of the time they are fairly covered.

screen-shot-2016-10-31-at-12-47-14-pmscreen-shot-2016-10-31-at-12-47-43-pmscreen-shot-2016-10-31-at-12-49-36-pmscreen-shot-2016-10-31-at-12-50-04-pmscreen-shot-2016-10-31-at-12-50-44-pmscreen-shot-2016-10-31-at-12-50-54-pmscreen-shot-2016-10-31-at-12-51-02-pmscreen-shot-2016-10-31-at-12-45-00-pmscreen-shot-2016-10-31-at-12-45-14-pm screen-shot-2016-10-31-at-12-45-37-pm

screen-shot-2016-10-31-at-12-41-59-pm screen-shot-2016-10-31-at-12-42-18-pm screen-shot-2016-10-31-at-12-42-26-pm screen-shot-2016-10-31-at-12-42-37-pm screen-shot-2016-10-31-at-12-43-37-pm

Written by Comments Off on Antar-book Posted in Book

Kelc-Book

bookcover

kelc

Book Versions:

Words to Live By takes fairly well-known quotes from poets and writers and mashes them with lines from current-day rappers. Below are some excerpts:

exp8 exp7

exp1

Process & Code

Python Program – pulls lyrics from given artists from LyricsNMatch.com and saves .txt file using Unirest.

Processing Code – reads from .txt files produced above and sews together quotes + names.

Conceptually my aim was to work on blending two different vernaculars into one text to create an odd and unexpected twist on phrases, sayings, and lines many people recognize. To start I sifted through the various song lyrics API’s available to me. I ended up using the “LyricsNMusic” API from marketplace.mashape.com. It allowed search inputs to range from artist and song name to keywords and lyric excerpts. The biggest issue I ran into with this project was simply working with java; I knew most of the manipulation I would be doing would be in rita.js but attempting to download every installer, package and library and required to even begin using this API (and then over-Google every error message I received) became very taxing on both me and my laptop. So I opted for Python.

I first ran my Python program, which produced lyric .txt files of whatever artist name is put in, on an array of about 37 rappers I could come up with. Then in Processing I iterated through those documents as well as another “50 Famous Quotes from Poets” .txt file and sewed different lines together through a common word like “in”, “and”, or “woman”. The output wasn’t as seamless and consistent as I originally intended but it worked surprisingly similar to a more efficient maneuver I would’ve used in rita.js. All in all, sometimes the less than desirable method still gets you a result you are aiming for.


Here’s a video of the professor, flipping through my book:

Written by Comments Off on Kelc-Book Posted in Book

Krawleb-Book

by any other nameexperiments in synonymous corruption

PDF (600 kb): ./wp-content/uploads/2016/10/krawleb-book.pdf

baon1

baon2

baon3

My book is a collection of 14 pieces of writing that have been transformed and corrupted through a synonym-swapping algorithm. The source text comes from monologues, song lyrics, and poems, each printed in its original form, then recursively synonym-swapped 5 times. The resulting text is a twisted, often out of context, but somewhat related derivative of the original concepts. Inspired by the famous Shakespeare passage “a rose by any other name would smell as sweet”, I explored what happened when we use ‘other names’ for things to the point of destruction.

The software pipeline for this project begins with a Python for Processing program that parses .txt files of both the original source content and a large .txt thesaurus. The program looks at each word, identifies words with synonyms in the thesaurus, and then replaces them. This data is then written to CSV, tagging each word that has been swapped each iteration, as well as tagging ends of lines to preserve the line breaks.

These CSVs are then fed into a Basil.js program that iterates over an array of CSV files, printing the text of each iteration on a new page, and italicizing each swapped word in each recursive iteration.

Ultimately, this project was an interesting experiment, but was a bit unpolished and buggy as I began to run out of time as the deadline approached. Some words are swapped into non-english synonyms, and some synonyms appear nonsensical or unrelated. However, in there chaos there are moments of beauty. Sometimes the text is morphed into new and humorous or beautiful new ideas through the process.

Additionally, the visual form is very minimal, which I would have liked to develop more, but the Basil.js workflow is rather unfriendly to rapid iteration, so I kept things as clean as possible for the final. That being said, I do like the austerity of the small undecorated text on otherwise blank pages.

Github here

PDFs of cover and spreads


Code below:

import random
from random import randrange
import re
import csv
import sys

textName = 'loveCallsUs'

source = open('texts/'+textName+'.txt', 'r')
ts = open('texts/synAntNoHyph.txt', 'r')
ts2 = open('texts/MobyTs.txt', 'r')

export = open('export.txt', 'w')
csvExport = open('exports/'+textName+'.csv', 'wb')

def setup():
    size(100, 100)

    rawTs = ts.readlines()
    rawTs2 = ts2.readlines()
    
    rawlines = source.readlines()
    lines = []
    iterations = []
    numIterations = 5 

    for raw in range(len(rawlines)):
        aLine = rawlines[raw].split()
        lines.append(aLine)
        
    #initialize csv with source
    writer = csv.writer(csvExport)
    writer.writerow( ('word', 'isNew', 'iteration', 'endLine') )
    print lines
    for x in range(len(lines)):
        isEnd = 0 #is it the end of a line?
        for y in range(len(lines[x])):
            word = lines[x][y]
            if y == len(lines[x])-1: #adjusted for index
                isEnd = 1
            writer.writerow( (word, '0' , '0' , isEnd ) )
        
    #first, print original text
    for line in lines:
        joined = ' '.join(line)
        print>>export, joined
    print>>export, '\n'

    for z in range(numIterations):
        # Find Synonyms
        for i in range(len(lines)):  # loop over lines
            for j in range(len(lines[i])):  # loop through words
                
                currentWord = re.sub(r'[^\w\s]','',lines[i][j]).title() #remove punctuation
                # print currentWord
                
                found = False #reset found (in first thesaurus)
                
                for k in range(len(rawTs)):  # loop through ts (first thesaurus)
                    if random.random() > 0.0:
                        if rawTs[k].startswith(currentWord+'.'): #if it's a period, grab the word right after
                                # print rawTs[k]
                                index = random.randint(1,len(rawTs[k].split())-1)
                                synonym = rawTs[k].split()[index]
                                synonym = str('_'+synonym)
                                found = True
                        if rawTs[k].startswith(currentWord+','): #if it's a comma, grab the second word
                                # print rawTs[k]
                                if len(rawTs[k].split())-1 >= 2:
                                    index = random.randint(2,len(rawTs[k].split())-1)
                                else:
                                    index = 1
                                synonym = rawTs[k].split()[index] 
                                synonym = str('_'+synonym)
                                found = True
    
                if found == True:
                    if j > 0:
                        synonym = synonym.lower()
                        synonym = re.sub(r'[^\w\s]','',synonym)
                        synonym = synonym.strip(';')
                    lines[i][j] = synonym
         
        #write to CSV 
        print lines                                     
        for x in range(len(lines)):
            for y in range(len(lines[x])):
                isSyn = 0 #is it a synonym?
                isEnd = 0 #is it the end of a line?
                if lines[x][y].startswith('_'): #detect if its a synonym
                    lines[x][y] = lines[x][y][1:] #strip synonym identifier
                    isSyn = 1
                if y == len(lines[x])-1: #adjusted for index
                    isEnd = 1
                word = lines[x][y]
                writer.writerow( (word, isSyn , (z+1) , isEnd) )
                # print word
                    
            # print str(lines[x])
            joined = ' '.join(lines[x])  
            print>>export, joined
        print>>export, '\n'
            
        iterations.append(lines)
    noStroke()
    fill(0,255,0)
    ellipse(width/2,height/2,80,80)
    
    csvExport.close()

Basil/InDesign code:

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

var csvArray = [
'rose.csv',
'hammy.csv',
'desire.csv',
'leisure.csv',
'enigmas.csv',
'israfil.csv',
'tooLate.csv',
'aRitual.csv',
'folkMeta.csv',
'sasquach.csv',
'souvenir.csv',
'gazzillion.csv',
'loveCallsUs.csv',
'spottieOttie.csv' ];

var csvData;

var genCover = false;
var reset = true;

//--------------------------------------------------------
function setup() {

  // Clear the document at the very start. 
  if (reset == true){
    b.clear (b.doc());
  }

  if (genCover == true){

  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Helvetica","Light"); 
  b.textAlign(Justification.LEFT_ALIGN); 
  b.text("By Any Other Name", 72,72,360,36);
  b.text("Kaleb Crawford, Fall 2016", 72,108,360,36);

  } //end Gen Cover

  //72 points in an inch
  var margin = 72

  var titleX = 72; 
  var titleY = 72;
  var titleW = 72;
  var titleH = 72;

  var passageX = margin + 18;
  var passageY = margin * 2;
  var passageW = margin * 3;
  var passageH = b.height-margin*2;

  var innerText;
  var mainFrame;
  var tempFrame;

  //////////////////////CSV Array Loop//////////////////////
  for (var texts = 0; texts < csvArray.length; texts++){

  var csvString = b.loadString(csvArray[texts]);
  csvData = b.CSV.decode( csvString );
  b.println("Number of elements in CSV: "+csvData.length);

  var totalIterations = parseInt(csvData[csvData.length-1].iteration);
  var currentIteration = 0;

  ////////////////////// Iterations Loop////////////////////
  for (var i = 0; i <= totalIterations; i++) {

    b.addPage(); // Create the next page.

    //Create the frame
    b.fill(0);
    b.textSize(8);
    b.textFont("Garamond","Regular"); 
    b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN );
    mainFrame = b.text("", passageX, passageY, passageW, passageH);

    currentIteration = i;
    innerText = ''; //initialize inner text

    ////////////////////Word Loop///////////////////
    for (var l = 0; l < csvData.length ; l ++ ){
      if (csvData[l].iteration == currentIteration){

        innerText = csvData[l].word+" ";

        if (csvData[l].isNew == 1){
          b.textFont("Garamond","Italic");
        }
        else{ b.textFont("Garamond","Regular"); }

        if (csvData[l].endLine == 1){
          innerText += "\n";
        }

        tempFrame = b.text(innerText, 0, 0, 100, 100);

        // mainFrame.contents += tempFrame.contents;

        tempFrame.paragraphs[0].move(LocationOptions.after,mainFrame.insertionPoints[-1]);

        tempFrame.remove();

      } //end if currentIteration check
    } // end line loop
    b.println('text '+(texts+1)+" iteration "+currentIteration);
  } //end iteration loop
}//end csvArray loop
} // end setup

// This makes it all happen:
b.go(); 
Written by Comments Off on Krawleb-Book Posted in Book

Lumar-LookingBotwards06

This bot takes sentences out of the New York Times completely out of context. And it happens to be a riot. Plenty of people seem to agree – the bot has a loyal following of 131k!

https://twitter.com/nytminuscontext

screen-shot-2016-10-28-at-7-33-41-am

This is just so incredibly fun. I wonder how the Bot was programmed to have such humor or whether the humor is largely self-prescribed knowing that it is a segmented taken from the NY times? Is it inherently funny? Or just funny because we think it’s from the NYT?

screen-shot-2016-10-28-at-7-35-26-am

 

Some are just outrageous and I absolutely cannot imagine under what circumstance/context they would make sense under in  the NYT:

screen-shot-2016-10-28-at-7-38-23-am

Perhaps that is part of the allure? Wondering HOW ON EARTH DID THIS COME TO BE?!?!??!?!

I’m so curious – this bot could totally promote articles way better than any ad. I’d want to read this article if only to find out the context behind this outrageous statement.

Xastol – LookingOutwards06

Urine

lo6_xastol_00

Juncture

lo6_xastol_01

Vitality

lo6_xastol_02

A twitter bot that I enjoyed was reverseocr. The bot selects a random word and then draws until the ocr library recognizes it as the given word. This process occurs four times a day.

The algorithm used is intriguing to me because it’s based on the probability of the cursor drawing shapes that are similar to letters; the algorithm is based on randomness. I’m interested in learning more about generativity and applying it to my own work (particularly film). I find this algorithm to be a good starting point at developing new, randomly genereated aesthetics/filters that can be applied to the video form.

reverseocr – https://twitter.com/reverseocr

hizlik-book

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 +19084212107
./imbfull.sh nicole.hladick@gmail.com""")

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 Hizal and %d by Nicole."%(metadata['total_H_str']+metadata['total_N_str'], metadata['total_H_str'], metadata['total_N_str'])
	print "%d total attachments: %d by Hizal and %d by Nicole."%(metadata['total_H_att']+metadata['total_N_att'], metadata['total_H_att'], metadata['total_N_att'])
	print "%d ave characters: %d for Hizal (max %d) and %d for Nicole (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 Hizal (max %d) and %d for Nicole (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(); 

Written by Comments Off on hizlik-book Posted in Book

Catlu – Book

MythMash is a whimsical mashup of the styles of major world mythologies. It pulls from a list of 9 Mythologies: African, Arabian, Asian, Celtic, Classical, Egyptian, Native American, Icelandic, and Polynesian. Each page contains a small myth generated using Markov chains and 2 random mythologies. The idea is to think about the place of stories and mythology in the world. The different mythologies all have many similarities, while also retaining individuality and cultural flavor. I encourage the reader to think about this while enjoying fun and entertaining newly generated stories.

20161028_004932

The idea of this book came to me as I was thinking about how to incorporate something I really loved and cared about into this generative book project. I think mythology is fascinating, beautiful, and amazing in scope. As I was thinking about it, I thought about how certain stories carry across cultures and regions: hero stories, creation myths, god figures, etc. This made me think about how storytelling is universal in the world, and is a vessel for creativity and empathy. Eventually, I decided to mash mythology styles together to show these similarities in an interesting and whimsical way, while hoping they still retained enough individual flavor to make them recognizable within the whole. Starting this book, I read a bit on Markov code. Eventually I decided to use RiTa.js’ Markov functions. For each mythology, I went through online sources and our school library to find texts of myths, and compiled about 60 pages in MS Word for each. I made it so that my program would randomly select two different mythologies for each page and mash them together. For pictures to accompany the words, at first I thought I could use patterns (also universal), but Golan suggested this could be cliche and I agreed. He then suggested that I choose random images that had to do vaguely with my myths, and that this might add a humorous and mysterious element. I liked the idea so Golan gave my a file with 1 million Flickr photos and their captions. With these pictures, I picked a random noun from the first 15 or so words, and looked for it in the captions that Golan provided me, saving everything to a JSON. I had a lot of problems with the million image file, which took 2 hours to transfer every time I had to move it. In the million images, a small percentage of them were missing (and way more missing photos got pulled than I really expected), and I had to go in and manually add existent images in when I was importing them into inDesign. Eventually, as I was checking through, I found out that some of the caption numbers were offset by 1, while others were not, and that this was completely random. Very frustrated at this point, I really looked at the pictures and decided they weren’t relevant enough to really be all that funny or merit being in the book. In the end, it turned out to be just the text. I was very excited about this project to be begin with, and wish it had turned out a little better. I couldn’t find how to make my own Markov code so had to use RiTa’s with little choice of personalization. Dan Shiffman released videos explaining how to write Markov when I was rushing and struggling to get the pictures to work with the Markov I already had. As for the pictures, I spent maybe 25-30 hours trying to transfer the files several times(8 hours), figure out the code, and work out all the little insidious bugs and how to deal with all the shifting zeros in the 1000 folders of 1000 pictures in the million file. In the end, I was very disappointed that I didn’t love the effect of the pictures as much as I would have liked, and that I spent so long on them and didn’t use them. I definitely really like the idea of the generative book, and how the myths came out, but I may revisit this project again when I have more time to really invest in getting everything right. The content of this book is important to me, and in the future I definitely plan on coming back and making the book every bit as good as it can be, writing the Markov myself and thinking through the right way to illustrate the myths.

MythMash PDF:

mythMash2.pdf

The code can be found in the below Github links:
Github Code:

Processing:

https://github.com/catlu4416/60-212/blob/master/markov4.pde

import processing.pdf.*;
import rita.*;


String captions[];

int rand1;
int rand2;
String myth1;
String myth2;

String mythsTitlePart1;
String mythsTitlePart2; 
String mythsTitle;

//lexicon initialization
RiLexicon lexicon = new RiLexicon();

//markov initialization
RiMarkov markov;
String mythText = "click to (re)generate!";
int x = 160, y = 240;

//array of myth types
String[] mythTypes = {"africanMyths.txt", "nativeAmericanMyths.txt", "asianMyths.txt", "arabianMyths.txt", 
  "celticMyths.txt", "egyptianMyths.txt", "norseIcelandicMyths.txt", "polynesianMyths.txt", "classicalMyths.txt"};

String[] mythTypesText = {"African", "Native American", "Asian", "Arabian", 
  "Celtic", "Egyptian", "Icelandic", "Polynesian", "Classical"};


//caption stuff
String captionPull;
//caption stuff try 2
String mythCaption1;
String mythCaption2;

String searchNoun;

int foundCount = 0;
IntList picNumbers;

//JSON stuff
JSONArray bookStuff;
String jsonFinal;


void setup()
{
  size(500, 800);

  beginRecord(PDF, "everything.pdf");

  fill(0);
  textFont(createFont("times", 16));
  bookStuff = new JSONArray();
}

void captions() {
  //caption stuff//Golan

  picNumbers = new IntList();

  String captionsFilename = "SBU_captioned_photo_dataset_captions.txt"; 
  String captions[] = loadStrings(captionsFilename); 

  int answerCount = 0; 
  String wordsIWant[] = { searchNoun }; 
  for (int i=0; i

Basil.js JavaScript for InDesign (.jsx):

https://github.com/catlu4416/60-212/blob/master/myBook.jsx

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

// Load a data file containing your book's content. This is expected
// to be located in the "data" folder adjacent to your .indd and .jsx. 
var jsonString = b.loadString("book.json");
var jsonData;
var imageFolder;
var anImageFilename;
var anImage;

//--------------------------------------------------------
function setup() {

  // Clear the document at the very start. 
  b.clear (b.doc());
  
  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Garamond"); 
  b.textAlign(Justification.CENTER_ALIGN); 
  b.text("MythMash", 72,72,360,36);
  b.textSize(16);
  b.text("Is a journey into the many mythologies of our world; a whimsical examination of their similarities and differences through generative mashup myth.", 72,108,360,300);

  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);
  b.println(jsonData);


  // Initialize some variables for element placement positions.
  // Remember that the units are "points", 72 points = 1 inch.
  var titleX = 320; 
  var titleY = 415;
  var titleW = 150;
  var titleH = 50;

  var captionX = 54; 
  var captionY = 54;
  var captionW = 396;
  var captionH =396;

  var imageX = 72; 
  var imageY = 72-30; 
  var imageW = 72*3; 
  var imageH = 72*3;


  // Loop over every element of the book content array
  // (Here assumed to be separate pages)
  for (var i = 0; i < jsonData.length; i++) {

    // Create the next page. 
    b.addPage();

    // Load an image from the "images" folder inside the data folder;
    // Display the image in a large frame, resize it as necessary. 
    b.noStroke();  // no border around image, please.
    
    //6 digits (hundreds of thousands)
    if (b.floor((jsonData[i].image/1000000)) == 0) {
        imageFolder = "00" + b.floor((jsonData[i].image/1000));
    }
    //5 digits (tens of thousands)
    if (b.floor((jsonData[i].image/100000)) == 0) {
        imageFolder = "000" + b.floor((jsonData[i].image/1000));
    }
    //4 digits (thousands)
    if (b.floor((jsonData[i].image/10000)) == 0) {
        imageFolder = "0000" + b.floor((jsonData[i].image/1000));
    }
    //first 1000
    if (b.floor((jsonData[i].image/1000)) == 0) {
        imageFolder = "0000" + b.floor((jsonData[i].image/10000));
    }

    
    
    var anImageFilename = "images/" + imageFolder + "/" + jsonData[i].image + ".jpg";
    var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
    anImage.fit(FitOptions.PROPORTIONALLY);
   
    

    // Create textframes for the "title" field.
    // Draw an ellipse with a random color behind the title letter.
    //b.noStroke(); 
    //b.fill(b.random(180,220),b.random(180,220),b.random(180,220)); 
    //b.ellipseMode(b.CORNER);
    //b.ellipse (titleX,titleY,titleW,titleH);
    
    b.addPage();
    
    b.fill(0);
    b.textSize(10);
    b.textFont("Garamond"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].title, titleX,titleY,titleW,titleH);
    //b.println(jsonData[i].title);

    // Create textframes for the "caption" fields
    b.fill(0);
    b.textSize(12);
    b.textFont("Garamond"); 
    b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN );
    b.text(jsonData[i].caption, captionX,captionY,captionW,captionH);

  };
}

// This makes it all happen:
b.go(); 


Here's a video of Golan flipping through my book:

Written by Comments Off on Catlu – Book Posted in Book

Keali-Book

cover

****[Eng Sub] by HorribleSubs (but actually though…) is a collection of randomly selected anime episode screencaps mismatched with arbitrarily generated, nonsensical, and irrelevant subtitles created through Markov Chains. A light-hearted and comedic production, the book pays homage to the dear memories of classic Japanese animated shows around which my adolescent life revolved; the collection of such franchises was a big influence on me wanting to become an artist. The title is reminiscent of the general title formats of the videos, as non-Japanese speakers attentively sought for the episodes that had the [eng sub] tag in the search results. *HorribleSubs is a popular provider of such subtitled episodes for numerous seasons.

Process:
My initial idea was entirely different–in retrospect, it’s amusing that my ideas throughout the process and the final resulting product are both appealing aesthetics to me, though visually and thematically different. I originally intended to make my book using APIs regarding nature astronomy–I thus researched Google APIs, particularly those affiliated with Google Maps and Google Earth (which favorably extended to data of oceans, landmarks, and stars). I wanted to randomly generate latitude and longitude locations on the planet, and then visually represent the state of the sky, and possibly the surroundings, at that location using designs and typography. I sketched out some visuals, having different fonts and sizes for stars and planets being laid onto the page depending on the angle point of view and the distance of the celestial bodies and Earth. This plan eventually fell farther from my grasp as I failed to find the relevant APIs, and also discovered that many of the astronomy-related APIs were no longer updated or accessible.

It was then when I noticed that plenty of the generative book type arts I had seen up to that point were comedic, meant to be a light and comical read, and I thought perhaps I could attempt this as well being an illustrator that usually does not work on comedy-related artworks. As such came my anime idea, where I just had the most arbitrary thought of pairing up random screencaps of episodes to nonsensical, maybe gibberish, captions. I laughed at the thought of the most ordinary images captioned with the most irrelevant subtitles underneath. This also meant a lot to me personally as someone who decided to pursue art because of the cartoons and games with which I grew up. It was refreshing to, in a way, revisit and work on a project that displayed this field of art which, to my own experience, has been such a taboo at this institution for reasons I cannot comprehend. (Please take cartoons seriously…) I feel like it has been so long since I’ve made something this potentially funny and light-hearted.

The main key in getting started was familiarizing myself with Rita–something completely new: I needed to learn about Markov Chains. This meant Dan Shiffman tutorials, dissecting references, and downloading and analyzing the example code that generated random sentences whenever the user clicked. Essentially, I modified the example code to feed in my own text files (collected anime subtitles), and to output the JSON objects as an array with {“subtitle”:} types.

I then wrote a script in Python to take and save random screenshots per all the downloaded episode files, and to save and condense all the subtitles per video into .txt files, organized by series. I brought all the series’ subtitles into the Processing file to generate sentences using Markov from them. I then ran the program and clicked as many times as I wanted to collect plenty randomly generated, nonsense subtitles that would be placed in my book.

Bringing the code into basil was more bearable with the sample book which Golan provided; the necessary lines and files were replaced with my own, and from there on the priority was to mimic the aesthetic of an adolescent’s exposure to anime: I wanted to the book to be unrefined, simple, informal. The screencaps were aligned per page in the center, as if it was directly captured from the video itself, and the subtitles were positioned to be at the bottom of the screen, also center-aligned and not past the video borders. The subtitle font was purposely chosen to be of the ones typically used to sub episodes, with easy readability, reasonable size, and a white or pale yellow fill color. I wanted to represent the online streaming environment as much as possible to fulfill the vision of the book (as direct representations of a subbed episode on some browser video player).

Streaming the episodes of a foreign cartoon from the states actually felt risky at times; if you couldn’t find the episode you wanted on youtube, you had to rely on google, and I personally was always wary with which sites could be trusted/which one had reliable subtitles (haha)/which one had good quality/which one may give my computer a virus, etc… I also factored this “experience” into the overall visual aspect of the book: once again, childish, innocent, perhaps even ratchet–the outcome is simple with minimal elements, but I feel like it fulfilled the task of essentially representing the necessary attributes. The final result made me, and others, laugh–which I am more than thankful for. I would also say the simple outcome is deceptively simple from all the generativity behind it…(personal benchmark: first time writing a script…!)

process

picmonkey-collage

flipbookgif

Here’s a video of the professor, flipping through the book:

final pdf: kearniebook5

img_4015

//GitHub repository

Python script:

import subprocess
import shutil
import shlex
import re
import ass 
import os
def takeSnapshots(fileName,ep):
    amountPerEp = 10
    episodeTime = 20*60
    ignoreTime = 60*3
    interval = int((episodeTime - 2*ignoreTime)/amountPerEp - 0.01)
    base = "kearnie/screencaps/"
    extractTimes = [i for i in range(ignoreTime,episodeTime-ignoreTime,interval)]
    for i in range(len(extractTimes)):
        time = extractTimes[i]
        args = ["mpv","-ao","null","-sid","no","-ss",str(int(time)),"-frames","1","-vo","image",
        "--vo-image-format=png", fileName]
        try:
          subprocess.run(args)
          shutil.move("00000001.png",base + "%d.png" % (ep*amountPerEp+i))
        except:
          print("fal")
trackRegex = re.compile("mkvextract:\s(\d)")
removeBrackets = regex = re.compile(".*?\((.*?)\}")
def getSubtitleTracks(fileName):
    output = subprocess.check_output(["mkvinfo",fileName],universal_newlines=True).splitlines()
    currentTrack = None
    sub_tracks = []
    for line in output:
        if "Track number:" in line:
            trackNumber = trackRegex.search(line).group(1)
            currentTrack = trackNumber
        if "S_TEXT/ASS" in line:
            sub_tracks.append(currentTrack)
    return sub_tracks
def exportSRT(fileName, track):
    srtName = fileName + "-%s.srt" % track
    args = ["mkvextract", "tracks",fileName, "%s:%s" % (track,srtName)]
    subprocess.run(args)
    return srtName
def cleanLine(line):
    newLine = ""
    inBracket = False
    lastBackSlash = False
    for c in line:
        if c == "{":
            inBracket = True
        elif c == "}":
            inBracket == False
        elif not inBracket:
            if c == "\\":
                lastBackSlash = True
            elif c != "N" or not lastBackSlash:
                newLine += c
                lastBackSlash = False
    return newLine

def extractTextFromSubtitles(fileName):
    tracks = getSubtitleTracks(fileName)
    output = ""
    for track in tracks:
        srtName = exportSRT(fileName, track)
        lines = []
        with open(srtName,"r") as f:
            doc = ass.parse(f)
            for event in doc.events:
                lines.append(cleanLine(event.text))
        combined = "\n".join(lines)
        if "in" in combined or "to" in combined or "for" in combined:
            output += combined
    return output

def extractFromFile(fileName,ep):
    os.makedirs("kearnie/screencaps/",exist_ok=True)
    text = extractTextFromSubtitles(fileName)
    with open("kearnie/subs.txt","a") as f:
        f.write(text)
    takeSnapshots(fileName,ep)
def extractSeries():
    ep = 0
    for filename in os.listdir("."):
        if filename.endswith(".mkv"):
            extractFromFile(filename,ep)
            ep += 1

extractSeries()

Processing code:

import rita.*;
import java.util.*;

//RiTa Markov Chain example template
//Does not contain JSON attributes yet 🙁 

JSONArray subtitles;
int count = 0;

RiMarkov markov;
String line = "click to (re)generate!";
String[] files = { "../data/durarara.txt", 
                   "../data/fmab.txt",
                   "../data/geass.txt",
                   "../data/naruto.txt",
                   "../data/wata.txt" };
int x = 160, y = 240;

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

  fill(0);
  textFont(createFont("times", 16));

  // create a markov model w' n=2 from the files
  markov = new RiMarkov(2);
  markov.loadFrom(files, this);
  subtitles = new JSONArray();
}

void draw()
{
  background(250);
  text(line, x, y, 400, 400);
}

void mouseClicked()
{
  if (!markov.ready()) return;
  x = y = 50;
  String[] lines = markov.generateSentences(1);
  line = RiTa.join(lines, " ");
  
  JSONObject subtitle = new JSONObject();
  subtitle.setString("subtitle", lines[0]);
  subtitles.setJSONObject(count, subtitle);
  println(lines[0]);
  if (lines[0] != null) {
    saveJSONArray(subtitles, count + ".json");
  count++;
  }
}

Basil.js code:

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

// Load a data file containing your book's content. This is expected
// to be located in the "data" folder adjacent to your .indd and .jsx. 
var jsonString = b.loadString("finalsubs.json");
var jsonData;


//--------------------------------------------------------
function setup() {
  var pageFlip = 0;
  // Clear the document at the very start. 
  b.clear (b.doc());

  // Make a title page. 
  // b.fill(0,0,0);
  // b.textSize(24);
  // b.textFont("Calibri"); 
  // b.textAlign(Justification.LEFT_ALIGN); 
  // b.text("A Basil.js Alphabet Book", 72,72,360,36);
  // b.text("Golan Levin, Fall 2016", 72,108,360,36);

  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);


  // Initialize some variables for element placement positions.
  // Remember that the units are "points", 72 points = 1 inch.
  var titleX = 72; 
  var titleY = 72;
  var titleW = 72;
  var titleH = 72;

  var captionX = 38; 
  var captionY = b.height - 115;
  var captionW = 500;
  var captionH = 36;

  var imageX = 38; 
  var imageY = 72; 
  var imageW = 500; 
  var imageH = 288; 


  // Loop over every element of the book content array
  // (Here assumed to be separate pages)
  for (var i = 0; i < jsonData.length; i++) {

    // Create the next page. 
    b.addPage();
    b.println(pageFlip);
    if (pageFlip == 0) {
        b.println("*******");
        // Load an image from the "images" folder inside the data folder;
        // Display the image in a large frame, resize it as necessary. 
        b.noStroke();  // no border around image, please.
        var anImageFilename = "images/" + b.floor(b.random(0,40)) + ".png"
        var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
        anImage.fit(FitOptions.PROPORTIONALLY);
        pageFlip = 1;
        b.println("*")
    }
    
    if (pageFlip == 1) {
    // Create textframes for the "title" field.
    // Draw an ellipse with a random color behind the title letter.
    /*b.noStroke(); 
    b.fill(b.random(180,220),b.random(180,220),b.random(180,220)); 
    b.ellipseMode(b.CORNER);
    b.ellipse (titleX,titleY,titleW,titleH);

    b.fill(255);
    b.textSize(56);
    b.textFont("Calibri"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].title, titleX,titleY,titleW,titleH);*/

    // Create textframes for the "caption" fields
    b.stroke(0);
    b.fill(255);
    b.textSize(20);
    b.textFont("Calibri"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
    b.text(jsonData[i].caption, captionX,captionY,captionW,captionH);
    
    pageFlip = 0;
    
    }
  };
}

// This makes it all happen:
b.go(); 
Written by Comments Off on Keali-Book Posted in Book

Xastol – Book

rip_00

rip_00_xastol

rip_01_xastol
 

#RIP is a generative book that showcases the use of the hashtag “#rip” on Twitter for October 21st, 2016. Using Temboo to gain easy access to the Twitter AI for collecting data, random tweets with the given hashtag are accessed and placed over 8-bit tombstones. In correlation with the randomly selected tweets are randomly generated backgrounds. The tombstones, on which the tweets are placed, are the same size and shape and flip horizontally every page (even pages vs. odd pages). Additionally, the background colors are randomly generated with dark color range to give off a somber feel that contrasts with with the general sarcastic mood of the tweets.

sketches_book_rip_xastol

The idea for my book actually sprang up while in the studio. After coming up with many over-complicated ideas and trying to figure out how realistic those ideas were while facing the learning curve of basil.js, I was stumped. That’s when a friend (takos) went into detail about us suffering and how we were going to probably spend the entire night in the studio, I responded with “rip” and then got myself thinking about how the term is used in slang. Initially, the term comes from “R.I.P.” or “Rest In Peace”, generally used when expressing ones condolences to the end of another’s life. However, the term has evolved into the singular word “rip”, and is often used to express sympathy with minor incidents of negative connotation.

I.E.

Person 1: “I just stubbed my pinkie toe and it hurts.”

Person 2: “rip.”

To help this come to life, I created two programs: one that would scrape data from Twitter and one that would generate the visuals. After being introduced to the website Temboo, gaining access to tweets on Twitter became trivial, as the website provides code for accessing the Twitter AI and Search features. This was completed in Processing.

I wanted to create visuals that would connect the traditional and modern usage of the term and found that Processing would also be useful for this. To connect the usages, I decided to create an 8-bit tombstone against a dark background (modern visuals against traditional feel).

Lastly, I converted my information into .json files (also done through Processing), so that data would be easily transmitted into InDesign (using basil.js).

In terms of my final product, I am satisfied, but not fully pleased. I am satisfied with the outcome of my project, given my limited knowledge on formatting print media and limited window to work on the project due to other class projects. Mainly, I think it’s the aesthetics of the book that lack, compared to the concept itself. I think if given more time to work with basil.js and print media, I’ll be able to create a much more aesthetically pleasing and content full piece.

 

Book Link: http://www.blurb.com/bookstore/invited/6594146/096a0b83792047730ec84f6dc02ab9d8dcb10012

PDF Link: rip-xapostol

Github Link: https://github.com/xapostol/60-212/tree/master/GenBook-xastol

 

TWITTER DATA COLLECTION CODE

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

// Create a session using your Temboo account application details
TembooSession session = new TembooSession("xastol", "myFirstApp", "WHATEVER YOURS IS");

void setup() {
  // Run the Tweets Choreo function
  runTweetsChoreo();
}

void runTweetsChoreo() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);

  // Set inputs
  String myQuery  = "rip";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken("YOUR STUFF");
  tweetsChoreo.setConsumerKey("THEIR STUFF");
  tweetsChoreo.setConsumerSecret("SECRET THEIR STUFF");
  tweetsChoreo.setAccessTokenSecret("SECRET YOUR STUFF");

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();
  
  // Print results
  println(tweetsResults.getLimit());
  println(tweetsResults.getRemaining());
  println(tweetsResults.getReset());
  //println(tweetsResults.getResponse());

  String[] result = {tweetsResults.getResponse()};
  println(result);
  saveStrings(myQuery+".json",result);
}

 

VISUALS CODE

// Template Provided by Golan
// Xavier Apostol (Xastol)
// 60-212 (8:30 - 11:20am & 1:30 - 4:20pm)
// xapostol@andrew.cmu.edu
// Random Character Generation

import processing.pdf.*;
boolean bRecordingPDF;
int pdfOutputCount = 1; 

float count = 1;
float changeFac = 40;
 
void setup() {
  size(750, 750);
  bRecordingPDF = true;
  strokeWeight(1);
}
 
void keyPressed() {
  // When you press a key, it will initiate a PDF export
  bRecordingPDF = true;
  count += 1;
  
  if (count % 2 == 1) {
    changeFac = 40; 
  } else {
    changeFac = -40; 
  }
}
 
void draw() {
  if (bRecordingPDF) {
    float backGCol = 255;
    float ranR = random(50);
    float ranG = random(50);
    float ranB = random(50);
    background(255); // this should come BEFORE beginRecord()
    beginRecord(PDF, "tombstone" + pdfOutputCount + ".pdf");
 
//--------------------------------------------------------------
    //Make all drawings here.
    noFill(); 
    beginShape();
    float tombX = width/2;
    float tombY = height/2;
    float tombW = tombX*1.15;
    float tombH = tombY*1.5;
    
    
    rectMode(CENTER);
    noStroke();
    /*
    // Tombstone
    fill(100,100,100);
    rect(tombX,tombY, tombW,tombH);
    rect(tombX,tombY, tombW-20,tombH+20);
    rect(tombX,tombY, tombW-40,tombH+40);
    rect(tombX,tombY, tombW-60,tombH+60);
    
    fill(150,150,175);
    rect(tombX+changeFac,tombY, tombW,tombH);
    rect(tombX+changeFac,tombY, tombW-20,tombH+20);
    rect(tombX+changeFac,tombY, tombW-40,tombH+40);
    rect(tombX+changeFac,tombY, tombW-60,tombH+60);
    */
    fill(ranR,ranG,ranB);
    rect(width/2,height/2, width,height);
    
    endShape();
//--------------------------------------------------------------
    endRecord();
    bRecordingPDF = false;
    pdfOutputCount++;
  }
}

 

INDESIGN CODE

#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
var jsonString = b.loadString("clean_tweets_sensored.json");
var jsonData;
//--------------------------------------------------------
function setup() {
// Clear the document at the very start.
b.clear (b.doc());
// Initialize some variables for element placement positions.
// Remember that the units are "points", 72 points = 1 inch.
var titleX;
var titleY = 72;
var titleW = 1080;
var titleH = 72;
var captionX;
var captionY = b.height/2;
var captionW = b.width - b.width/2;
var captionH = 180;
var imageX = 6;
var imageY = 10;
var imageW = 72*7;
var imageH = 72*7;
var coverFileName = "images/tomb1.png";
var coverImage = b.image(coverFileName, imageX, imageY, imageW, imageH);
// Make a title page.
coverImage.fit(FitOptions.PROPORTIONALLY);
b.fill(0);
b.textSize(200);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN);
b.text("#RIP", 12,90,480,360);
b.textSize(30);
b.text("Xavier Apostol", 153,339,360,72);
b.text("Fall 2016", 153,369,360,72);

// Make a info page.
b.addPage();
b.textSize(36);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN);
b.text("A Generative Book Using Twitter Hashtags", 72,162,360,180);

b.addPage();
b.textSize(36);
b.text("For Friday October 21st, 2016", 72,191,360,121);
// Parse the JSON file into the jsonData array
jsonData = b.JSON.decode( jsonString );
b.println("Number of elements in JSON: " + jsonData.length);
// Loop over every element of the book content array
// (Here assumed to be separate pages)
for (var i = 0; i < jsonData.length; i++) {

// Create the next page.
b.addPage();

// Load an image from the "images" folder inside the data folder;
// Display the image in a large frame, resize it as necessary.
b.noStroke(); // no border around image, please.
var anImageBFilename = "background/" + jsonData[i].image;
var anImageFilename;
if (i % 2 == 0) {
anImageFilename = "images/tomb1.png";
titleX = -b.width/2 - 10;
captionX = b.width/2 - b.width/5;

} else {
anImageFilename = "images/tomb2.png";
titleX = -b.width/2 - 40;
captionX = b.width/2 - b.width/4 - 10;
}

var anImageBack = b.image(anImageBFilename, imageX - 6, imageY-10, imageW+1, imageH);
var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
anImageBack.fit(FitOptions.PROPORTIONALLY);
anImage.fit(FitOptions.PROPORTIONALLY);

// Create textframes for the "screenName" field.
b.fill(0);
b.textSize(30);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
b.text(jsonData[i].screenName, titleX,titleY,titleW,titleH);

// Create textframes for the "date" fields
b.textSize(20);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
b.text(jsonData[i].date, captionX,titleY+108,captionW,captionH);

// Create textframes for the "text" fields
b.textSize(20);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
b.text(jsonData[i].text, captionX,captionY,captionW,captionH);

};
// For even amount of pages.
b.addPage();
var endPageFileName = "background/tombstone1-page-001.jpg";
var endPage = b.image(endPageFileName, imageX - 6, imageY-10, imageW+1, imageH);
endPage.fit(FitOptions.PROPORTIONALLY);
}

// This makes it all happen:
b.go();

Video of the professor flipping through my book:

Written by Comments Off on Xastol – Book Posted in Book

Ngdon-Book

Book of Illustrations of Imaginary Creatures

snip20161021_28

snip20161021_17

aasdfawelfhd

A PDF iteration of my book

View on Blurb

My book is a book of procedurally generated illustrations of imaginary creatures. Every generated book is different, detailing different creatures from different islands. Each of them is titled “Fauna of <Island Name>”, and has a map of that island and illustrations of 50 creatures living on that island, along with their Latin names.

Inspiration

My main inspiration is the book Classic of Mountains and Seas from 4th century BC China. The book contains detailed description of hundreds of weird animals that probably didn’t exist, such as snake headed bird, and tiger with nine heads, and headless humanoid with nipples as its eyes and belly button as its mouth, etc. This book also has geographic description of mountains and rivers, and together they rendered a fantastic world of prehistoric China.

 

 

 

 

 

 

 

 

Another inspiration is zoological illustrations. They are usually very carefully drawn, detailing every hair and every feather. However, creatures in these illustrations usually have a stiff, deadpan and lifeless look. This contrast gives them a very peculiar aesthetic that I found interesting.

Techniques

Skeleton
snip20161027_2

I started by making a general skeleton for my creatures. The skeleton consists of bones, and each bone object has only the following attributes:

  1. A pointer to its parent bone
  2. An array of pointer(s) to its child bone(s)
  3. Relative rotation to its parent bone
  4. Length of the bone

Thus the absolute position, rotation and other information of a bone can be calculated recursively. This proved to be a very versatile and robust data structure.

Flesh

snip20161027_4

For every bone I defined an additional attribute: The thickness of flesh around it. This way we can get a rough outline of the animal. The thickness is alway perpendicular to the bone and calculated from the front end of the bone.

To make the outline look more organic, I first tried to apply a simple quadratic bezier curve on it. But the outline got smoothed so much that it looked like the animal was made of rubber hoses. Then I discovered the “Rational Bezier Curve”, which assigns a weight on each point. With it I’m able to make the outline just smooth enough.

Rational Bezier Curve (From Wikipedia)

snip20161027_5

Fur

Now that we’ve got the shape of the creature, we can wrap it with its fur. I used the following algorithm to calculate the direction of each hair.

Each hair is enclosed by two curves: One is the outline of the creature, and the other is the skeleton of the creature. There is a point on each of the two curves that is the closest to the hair. Therefore, the direction of the hair is a value between the derivatives of the two curves at these two points. So we have:

Angle of Hair = Tangent of First Curve * Distance to Second Curve/ Distance between First and Second Curve + Tangent of Second Curve * Distance to First Curve/ Distance between First and Second Curve

Then it becomes a simple matter of drawing lots and lots of hair and tweaking the hue and darkness based on the hair’s relative position.

snip20161027_7

Fur Pattern

I Designed three types of patterns: stripes, dots, and rings using a very simple algorithm.

For the stripes, I use mod on the coordinate of each hair to determine which color region it is in. For the dots and rings, I pre-generate a bunch of random points at the beginning of the program, and before drawing each hair, calculate its distance to each point to see if it’s in the radius.

I used Mitchell’s Best Candidate algorithm Golan showed us a few classes ago to generate the random points, so there’s minimal overlapping.

snip20161027_10

snip20161027_9

snip20161027_8

Feathers & Wings

Wings consists of feathers. Here’s my algorithm for a single feather:

snip20161027_11

Notice that the angle of the lines on each side of the shaft changes from one end to the other: It’s lerped from PI/4 to 0. Notice also that the length of the lines also changes: It’s enveloped with an ellipsoid function.

Feathers are then pasted onto the wing just like how fur was drawn onto the skin, with their colors, sizes, directions and shapes calculated based on their relative location to the outline and the skeleton.

snip20161018_6

Misc

Horns – A function that takes a curve and draw a horn shape bent along that curve. This is handy because claws, teeth, tongue can all use this function to draw.

snip20161027_28

Horse tail – A function that takes a curve and draws a bunch of curves that are similar to this curve. It calculates the tangent at each point on the curve, and randomize that angle using Perlin noise to generate the other curves.

Scale – reptilian scales are generated the same way fur is, except they’re orderly arranged regular polygons rather than random short lines.

Randomization

snip20161021_29

I divided the types of creatures my program shall generate into three rough categories: mammals, fishes, birds, then randomized based on special features of these categories.

At first I used the simple random() function to create randomness. The result was chaotic. If the random range was large, I got a jumbled mess of fur and feathers across the screen; If I made the range smaller, I got identical creatures every time.

So I utilized Gaussian Randomness, which has normally distributed output values. I wrote a custom function that took a peak number and a minimum and maximum number and warps gaussian randomness to that range. Therefore for my generator, it is possible, for example, to have extremely long-necked and extremely short-necked animals, however most animals have not-too-long-and-not-too-short necks, which is realistic.

Pseudo-Latin Names

I generated a Latin name for each of my creature with a simple algorithm:

  • Download a list of latin names for real animals from the web.
  • Parse and split all the names into categorized lists of syllables, prefixes and suffixes.
  • Grab a random prefix, grab a couple of random syllables, grab a random suffix.
  • Boom! A pseudo-latin name.

At first I wanted to use a Markov chain word generator, but after playing with it I thought my own method would be more straightforward.

The Island

snip20161027_17

Now that I had my creatures complete, I wanted to make up a place for them to dwell in. First I made a place name generator similar to the latin name generator, then generated island maps using Perlin noises and filters.

At first I also wanted to have a brief description of each creature, along with information about where on the island the creature could be found. These eventually weren’t realized because I found that to write a really good implementation of these features would take days, and I didn’t want a crappy implementation to be next to my carefully generated creatures.

The part of organizing everything and putting them into the book form was rather easy; I used a Basil.js script to automate the process.

Result

I’m actually a bit disappointed with the quality of the printed book. The colors aren’t accurate: The cream background becomes a greenish grey, and everything is darker than it’s supposed to be. Looking at it closely I find that my delicate lines are blurry. The paper and the binding are average. Given that it costs so much, I was expecting something of a much higher quality.

However, nobody else seems to notice, so probably it’s just me nitpicking.

Thoughts

I think this book project can be one in a series of projects or as a part of a larger project. For example, I can generate some weird plants and put them into a book called “Flora of <Island Name>” as a companion to this “Fauna of <Island Name>” book. I can also make a big book about an island, with information about its geography, landscape, animals and plants, climate, history, etc. Or maybe a even bigger book about an imaginary planet, or a bigger bigger book about an imaginary universe.
snip20161027_18

A page from the book

Code

Github Link: https://github.com/LingDong-/60-212/tree/master/book

//Imaginary Creatures Generator

boolean debug = false;
boolean export = false;

void regpoly(float x, float y, float r, float n, float ro){
  beginShape();
  for (int i = 0; i < n; i ++){
    vertex(x+r*cos(ro+i/n*PI*2),y-r*sin(ro+i/n*PI*2));
  }
  endShape(CLOSE);
}

float distsum(float[][] P){ 
  float d = 0;
  for (int i = 0; i < P.length-1; i++){
    d += dist(P[i][0],P[i][1],P[i+1][0],P[i+1][1]);
  }
  return d;
}

float[] midpt(float[] p1,float[] p2){
  return new float[] {(p1[0]+p2[0])/2,(p1[1]+p2[1])/2};
}

float[][] revpl(float[][] pl){
  float[][] nl = new float[pl.length][pl[0].length];
  for (int i = 0; i < pl.length; i++){
    nl[pl.length-1-i]=pl[i];
  }
  return nl;
  
}

float[][] bezmh(float[][] P){
  int cpl = 0;
  float[][] plist = new float[10000][2];
  
  for (int j = 0; j < P.length-2; j++){
    
    float[] p0;float[] p1;float[] p2;
    if (j == 0){
      p0 = P[j];
    }else{
      p0 = midpt(P[j],P[j+1]);
    }
    p1 = P[j+1];
    if (j == P.length-3){
      p2 = P[j+2];
    }else{
      p2 = midpt(P[j+1],P[j+2]);
    }
    float pl = distsum(new float[][]{p0,p1,p2});
    for (int i = 0; i < pl; i+= 1){
       float t = i/pl;
       float w=2;
       plist[cpl][0] = (pow(1-t,2)*p0[0]+2*t*(1-t)*p1[0]*w+t*t*p2[0])/(pow(1-t,2)+2*t*(1-t)*w+t*t);
       plist[cpl][1] = (pow(1-t,2)*p0[1]+2*t*(1-t)*p1[1]*w+t*t*p2[1])/(pow(1-t,2)+2*t*(1-t)*w+t*t);
       cpl++;
    }
  }
  float[][] fplist = new float[cpl][2];
  for (int i = 0; i < cpl; i+= 1){
    fplist[i] = plist[i];
  }
  return fplist;
}



public class Limb{
 Limb[] subs = new Limb[32];
 int subslength = 0;
 Limb par = null;
 float l;
 float tl1;
 float tl2;
 float a;
 float[] alim = new float[2];

 public Limb(float l){
   this.l = l;
 }
 public Limb growlimb(float nl, float na){
   Limb newlimb = new Limb(nl);
   newlimb.par = this;
   newlimb.a = na;
   subs[subslength] = newlimb;
   subslength ++;
   return newlimb;
 }

 public float rot(){
   if (par == null){
     return a;
   }else{
     float la = par.rot();
     float nl = la + a;
     return nl;
   }      
 }
 public float[] loc(){
   float r = rot();
   if (par == null){
     return new float[] {l * cos(r),-l * sin(r)};
   }else{
     float[] ll = par.loc();
     return new float[] {ll[0] + l * cos(r), ll[1] - l*sin(r)};
   }   
 }
  public float[] tlloc(int n){
   float[] lo = this.loc();  
    if (par == null){
   if (n== 1){
     return new float[] {lo[0]+tl1*cos(rot()-PI/2),lo[1]-tl1*sin(rot()-PI/2)};     
   } else if (n== 2){
     return new float[] {lo[0]+tl2*cos(rot()+PI/2),lo[1]-tl2*sin(rot()+PI/2)};     
   }
    }
   lo = par.loc();  
   float ro = (rot()+par.rot()+PI)/2+PI;
   if (n== 1){
     return new float[] {lo[0]+par.tl1*cos(ro),lo[1]-par.tl1*sin(ro)};     
   } else if (n== 2){
     return new float[] {lo[0]+par.tl2*cos(ro+PI),lo[1]-par.tl2*sin(ro+PI)};     
   }
   return new float[2];
   
 }
 
 
 public float[] relcoord(float pr, float di){
   float[] parloc;
   float r = rot();
   if (par == null){
     parloc = new float[]{0,0};
   }else{
     parloc = par.loc();
   }
   return new float[]{parloc[0] + pr * l * cos(r) + di * cos(r+PI/2), parloc[1] - pr * l * sin(r) - di * sin(r+PI/2)};
  
 }
 
 public void bounds(float[] bd){
   if (loc()[0] < bd[0]){bd[0] = loc()[0];} if (loc()[0] > bd[1]){bd[1] = loc()[0];}
   if (loc()[1] < bd[2]){bd[2] = loc()[1];} if (loc()[1] > bd[3]){bd[3] = loc()[1];}
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){
       
       subs[i].bounds(bd);
     }
   } 
 }
 public void flip(){
   if (par != null){
     a = -a;
   }else{
     a = PI-a;
   }
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){ 
       subs[i].flip();
     }
   } 
 } 
 
 public void drawSkel(){
   if (par != null){
     line(par.loc()[0],par.loc()[1],loc()[0],loc()[1]);
   }
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){
       
       subs[i].drawSkel();
     }
   }
 }
  public void drawOline(){
   if (par != null){
     line(par.tlloc(1)[0],par.tlloc(1)[1],tlloc(1)[0],tlloc(1)[1]);
     line(par.tlloc(2)[0],par.tlloc(2)[1],tlloc(2)[0],tlloc(2)[1]);
   }
   for (int i = 0; i < subslength; i++){
     if (subs[i] != null){
       stroke(random(255),random(255),random(255));
       subs[i].drawOline();
     }
   }
 }

}

public class Creature{
 float x;
 float y;
 float[][] dotmap = new float [floor(random(200,800))][2];
 float stpwd = random(20,100);
 float[] stpcol = {random(100),random(30),random(10,40)};
 
 Limb head; Limb jaw_u; Limb jaw_l; Limb neck;
 Limb spine_a; Limb spine_b; Limb spine_c; Limb pelvis;
 Limb tail_a; Limb tail_b; Limb tail_c; Limb tail_d;
 
 Limb shoulder_l; Limb forethigh_l; Limb foreleg_l; Limb forepaw_l;
 Limb midshould_l; Limb midthigh_l; Limb midleg_l; Limb midpaw_l;
 Limb hip_l; Limb hindthigh_l; Limb hindleg_l; Limb hindpaw_l;
 Limb wing_a_l; Limb wing_b_l; Limb wing_c_l;
 
 Limb shoulder_r; Limb forethigh_r; Limb foreleg_r; Limb forepaw_r;
 Limb midshould_r; Limb midthigh_r; Limb midleg_r; Limb midpaw_r; 
 Limb hip_r; Limb hindthigh_r; Limb hindleg_r; Limb hindpaw_r;
 Limb wing_a_r; Limb wing_b_r; Limb wing_c_r;

 Limb[] limbs;


 public Creature(){

   pelvis = new Limb(0);
   spine_c = pelvis.growlimb(0,0);
   spine_b = spine_c.growlimb(0,0);
   spine_a = spine_b.growlimb(0,0);
   neck = spine_a.growlimb(0,0);
   head = neck.growlimb(0,0);
   jaw_u = head.growlimb(0,0);
   jaw_l = head.growlimb(0,0);
   shoulder_l = spine_a.growlimb(0,0);
   shoulder_r = spine_a.growlimb(0,0);
   forethigh_l = shoulder_l.growlimb(0,0);
   forethigh_r = shoulder_r.growlimb(0,0);
   foreleg_l = forethigh_l.growlimb(0,0);
   foreleg_r = forethigh_r.growlimb(0,0);
   forepaw_l = foreleg_l.growlimb(0,0);
   forepaw_r = foreleg_r.growlimb(0,0);
   midshould_l = spine_b.growlimb(0,0);
   midshould_r = spine_b.growlimb(0,0);
   midthigh_l = midshould_l.growlimb(0,0);
   midthigh_r = midshould_r.growlimb(0,0);
   midleg_l = midthigh_l.growlimb(0,0);
   midleg_r = midthigh_r.growlimb(0,0);
   midpaw_l = midleg_l.growlimb(0,0);
   midpaw_r = midleg_r.growlimb(0,0); 
   hip_l = pelvis.growlimb(0,0);
   hip_r = pelvis.growlimb(0,0);
   hindthigh_l = hip_l.growlimb(0,0);
   hindthigh_r = hip_r.growlimb(0,0);
   hindleg_l = hindthigh_l.growlimb(0,0);
   hindleg_r = hindthigh_r.growlimb(0,0);
   hindpaw_l = hindleg_l.growlimb(0,0);
   hindpaw_r = hindleg_r.growlimb(0,0); 
   wing_a_l = spine_a.growlimb(0,0);
   wing_a_r = spine_a.growlimb(0,0);
   wing_b_l = wing_a_l.growlimb(0,0);
   wing_b_r = wing_a_r.growlimb(0,0);
   wing_c_l = wing_b_l.growlimb(0,0);
   wing_c_r = wing_b_r.growlimb(0,0);
   tail_a = pelvis.growlimb(0,0);
   tail_b = tail_a.growlimb(0,0);
   tail_c = tail_b.growlimb(0,0);
   tail_d = tail_c.growlimb(0,0);
   
   limbs = new Limb[] {pelvis,spine_c,spine_b,spine_a,neck,head,jaw_u,jaw_l,
   shoulder_l,shoulder_r,forethigh_l,forethigh_r,foreleg_l,foreleg_r,forepaw_l,forepaw_r,
   midshould_l,midshould_r,midthigh_l,midthigh_r,midleg_l,midleg_r,midpaw_l,midpaw_r,
   hip_l,hip_r,hindthigh_l,hindthigh_r,hindleg_l,hindleg_r,hindpaw_l,hindpaw_r,
   wing_a_l,wing_a_r,wing_b_l,wing_b_r,wing_c_l,wing_c_r,tail_a,tail_b,tail_c};
   
  }
  void makedotmap(float s0, float s1){
    for (int i = 0; i < dotmap.length; i ++){
      float[][] cdd = new float[1000][2];
      for (int j = 0; j < cdd.length; j++){
        cdd[j] = new float[]{random(-width*0.2,width*1.2)-trans[0],random(-height*0.2,height*1.2)-trans[1],random(s0,s1)};
      }
      int maxind = 0;
      float maxdist = 0;
      for (int j = 0; j < cdd.length; j++){
        float shortdist = width*height;
        for (int k = 0; k < i; k++){
           float cd = dist(cdd[j][0],cdd[j][1],dotmap[k][0],dotmap[k][1])-cdd[j][2]-cdd[k][2];
           if (cd < shortdist){ shortdist = cd; } } if (shortdist > maxdist){
          maxdist = shortdist;
          maxind = j;
        }
      }
      dotmap[i] = cdd[maxind];
    } 

 }

 public void feather(float len, float bw, float[] col){

   for (int i = 0; i < len; i+=2){ float ang = PI/4*((len-i)/len);//+random(-0.5*PI/i,0.5*PI/i); float cl = bw*0.03*sqrt(pow(len/2,4)-pow(i-len/2,4)); stroke(col[0],col[1],lerp(min(col[2]+50,100),random(col[2],col[2]+50),i*1.0/len)); line(i,0,i+cl*cos(ang),0+cl*sin(ang)); stroke(col[0],col[1],lerp(min(col[2]+50,100),random(col[2],col[2]+50),i*1.0/len)); line(i,0,i+cl*cos(ang),0-cl*sin(ang)); } stroke(col[0],col[1],min(col[2],100)); line(0,0,len,0); } public void antler(float[] p0, float wd, float len, int depth){ if (depth > 0){
    float[][] cur = bezmh(new float[][]{{p0[0],p0[1]},{p0[0]+len/2,random(len/6,len/3)},{p0[0]+len,p0[1]}});
    hornnoil(cur,wd,new float[]{10,20,100});
    for (int i = 0; i < len/40; i++){
      pushMatrix();
      int ri = floor(random(0,cur.length));
      float[] rc = cur[ri];
      translate(rc[0],rc[1]);
      rotate(random(PI/4));
      float pp = 1.0*(cur.length-ri)/cur.length;
      antler(new float[]{0,0},wd*pp,len*random(pp/2,pp),depth-1);
      popMatrix();
    }
  }
 }
 
 
 public void horn(float[][] cur,float bw){
   
   beginShape();
   for (int i = 0; i < cur.length-1; i++){ float tang = PI/2+atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]); float cw =bw * (cur.length-i)/cur.length; vertex(cur[i][0]+cw*cos(tang),cur[i][1]+cw*sin(tang)); } for (int i = cur.length-1; i >0; i--){
     float tang = PI/2+atan2(cur[i-1][1]-cur[i][1],cur[i-1][0]-cur[i][0]);
     float cw = bw * (cur.length-i)/cur.length;
     vertex(cur[i][0]+cw*cos(tang),cur[i][1]+cw*sin(tang));
   }   
   endShape(CLOSE);
 }
 
 public void hornil(float[][] cur, float bw, float[] col){

     noStroke();
    for (float i = bw; i > 0; i--){
      fill(col[0],col[1],col[2]*0.5+col[2]*0.5*(bw-i)/bw);
      horn(cur,i);  
    }  
   
 }
 
 public void hornnoil(float[][] cur, float bw, float[] col){

  fill(col[0],col[1],col[2]/3);
  noStroke();
  horn(cur,bw);
   for (int i = 0; i < cur.length-1; i++){

     float tang = PI/2+atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]);

     float cw =(bw * (cur.length-i)/cur.length+1)*(0.4+1.1*noise(i*0.05));
     for (int j = 0; j < cw*10; j++){
       float rw = random(-1,1);
       float rc = col[2]*0.4+random(col[2]*0.3*(1-abs(rw)),col[2]*1.2*(1-abs(rw)));
       fill(col[0],col[1],rc);
       ellipse(cur[i][0]+rw*cw*cos(tang),cur[i][1]+rw*cw*sin(tang),1,1);
     }
   }

   
 }
 
 public void anyfill(String filltype, float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){
   if (filltype == "fur"){
     furfill(cur0,cur1,coldat,furdat,patdat,amount);
   }else if (filltype == "scale"){
     scalefill(cur0,cur1,coldat,furdat,patdat,amount);
   }else if (filltype == "feather"){
     featherfill(cur0,cur1,coldat,furdat,patdat,amount);
   }
   if (debug){
     strokeWeight(1);noFill();stroke(255,0,0);
     beginShape(); for (int i = 0; i < cur0.length; i++){
       vertex(cur0[i][0],cur0[i][1]);  
     }endShape(); stroke(255,0,255);
     beginShape(); for (int i = 0; i < cur1.length; i++){
       vertex(cur1[i][0],cur1[i][1]);    
     }endShape();
   }
 }
 
 public void furfill(float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){
   int ml = min(cur0.length,cur1.length);
   for (int i = 0; i < ml; i++){ float ca = atan2(cur1[i][1]-cur0[i][1],cur1[i][0]-cur0[i][0]); float cl = dist(cur0[i][0],cur0[i][1],cur1[i][0],cur1[i][1]); float d2e = min(i,ml-i)/ml; cl = cl*(1.0+d2e*(0.4-0.8*noise(0.005*i))); cur1[i][0] = cur0[i][0]+cl*cos(ca); cur1[i][1] = cur0[i][1]+cl*sin(ca); } strokeWeight(1); if (ml > 0){
   for (int i = 0; i < amount; i++){
     int ir = floor(random(0,min(cur0.length,cur1.length)-1));
     float r = random(1);
     float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r));
     float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r));
     float driv1 = atan2(cur1[ir+1][1]-cur1[ir][1],cur1[ir+1][0]-cur1[ir][0]);
     float driv2 = atan2(cur0[ir+1][1]-cur0[ir][1],cur0[ir+1][0]-cur0[ir][0]);
     float p = dist(xr,yr,cur1[ir][0],cur1[ir][1])/dist(cur0[ir][0],cur0[ir][1],cur1[ir+1][0],cur1[ir+1][1]);
     float d = driv1*p+driv2*(1-p);
     float dr = d+random(-PI*0.1,PI*0.1)+(1-p)*furdat[2];
     stroke(coldat[0]+p*coldat[1]+random(coldat[2]),coldat[3]+p*coldat[4]+random(coldat[5]),coldat[6]+p*coldat[7]+random(coldat[8]));
     if (patdat[0] == 1){
       if (floor(xr/stpwd)%2 == 0){
         stroke(stpcol[0],stpcol[1],random(stpcol[2]));
       }
     }else if (patdat[0] == 2){
       for (int j = 0; j < dotmap.length; j++){
         if (dist(xr,yr,dotmap[j][0],dotmap[j][1])<dotmap[j][2]){
           stroke(stpcol[0],stpcol[1],random(stpcol[2]));
         }
       }
     }else if (patdat[0] == 3){
       for (int j = 0; j < dotmap.length; j++){
         if (dist(xr,yr,dotmap[j][0],dotmap[j][1])<dotmap[j][2] && dist(xr,yr,dotmap[j][0],dotmap[j][1])>dotmap[j][2]/2){
           stroke(stpcol[0],stpcol[1],random(stpcol[2]));
         }
       }       
     }
     float fl = furdat[0]+furdat[1]*noise(0.01*i);
     line(xr,yr,xr+fl*cos(dr),yr+fl*sin(dr));  

   }  
   }

 }
 public void furball(float x, float y,float r,float[] coldat){
   for (int i = 0; i < 100; i++){
     stroke(coldat[0]+0*coldat[1]+random(coldat[2]),coldat[3]+0*coldat[4]+random(coldat[5]),coldat[6]+0*coldat[7]+random(coldat[8]));
     line(x+random(-r,r),y+random(-r,r),x+random(-r,r),y+random(-r,r)); 
   }
 }
 
 public void scalefill(float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){
   int ml = min(cur0.length,cur1.length);
   for (int i = 0; i < ml; i++){
     float ca = atan2(cur1[i][1]-cur0[i][1],cur1[i][0]-cur0[i][0]);
     float cl = dist(cur0[i][0],cur0[i][1],cur1[i][0],cur1[i][1]);
     float d2e = min(i,ml-i)/ml;
     cl = cl*(1.0+d2e*(0.4-0.8*noise(0.005*i)));
     cur1[i][0] = cur0[i][0]+cl*cos(ca);
     cur1[i][1] = cur0[i][1]+cl*sin(ca);
     
   }   
   strokeWeight(1);
   amount = amount/20;
   for (int i = 0; i < amount; i+=5){
     int ir = floor(random(0,min(cur0.length,cur1.length)-1));
     float cd =  dist(cur0[ir][0],cur0[ir][1],cur1[ir][0],cur1[ir][1]);
     for (int j = 0; j < cd; j+=5){
       float r = random(1);
       float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r));
       float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r));
       float driv1 = atan2(cur1[ir+1][1]-cur1[ir][1],cur1[ir+1][0]-cur1[ir][0]);
       float driv2 = atan2(cur0[ir+1][1]-cur0[ir][1],cur0[ir+1][0]-cur0[ir][0]);
       float p = dist(xr,yr,cur1[ir][0],cur1[ir][1])/dist(cur0[ir][0],cur0[ir][1],cur1[ir+1][0],cur1[ir+1][1]);
       float d = driv1*p+driv2*(1-p);
       float dr = d+random(-PI*0.1,PI*0.1)+(1-p)*furdat[2];
       //dr = random(PI*2);
       float[] col = new float[]{coldat[0]+p*coldat[1]+random(coldat[2]),coldat[3]+p*coldat[4]+random(coldat[5]),coldat[6]+p*coldat[7]+random(coldat[8])};
       fill(col[0],col[1],col[2]);
       stroke(col[0],col[1]*0.8,col[2]*0.6);
       regpoly(xr,yr,4,5,dr);

     }
   }     
   
   for (int i = 0; i < ml-1; i+=6){
     int ir = i;//floor((i*1.0)/amount*(ml-1));//floor(random(0,min(cur0.length,cur1.length)-1));
     float cd =  dist(cur0[ir][0],cur0[ir][1],cur1[ir][0],cur1[ir][1]);
     for (int j = 0; j < cd; j+=6){ float r = j/cd;//random(1); float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r)); float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r)); float driv1 = atan2(cur1[ir+1][1]-cur1[ir][1],cur1[ir+1][0]-cur1[ir][0]); float driv2 = atan2(cur0[ir+1][1]-cur0[ir][1],cur0[ir+1][0]-cur0[ir][0]); float p = dist(xr,yr,cur1[ir][0],cur1[ir][1])/dist(cur0[ir][0],cur0[ir][1],cur1[ir+1][0],cur1[ir+1][1]); float d = driv1*p+driv2*(1-p); float dr = d+random(-PI*0.1,PI*0.1)+(1-p)*furdat[2]; float[] col = new float[]{coldat[0]+p*coldat[1]+random(coldat[2]),coldat[3]+p*coldat[4]+random(coldat[5]),coldat[6]+p*coldat[7]+random(coldat[8])}; fill(col[0],col[1],col[2]); stroke(col[0],col[1]*0.8,col[2]*0.6); regpoly(xr,yr,6,5,dr); } } } public void featherfill(float[][] cur0, float[][] cur1, float[] coldat, float[] furdat, float[] patdat, int amount){ int ml = min(cur0.length,cur1.length); if (ml > 0){
   strokeWeight(1);
   float a0 = atan2(cur0[1][1]-cur0[0][1],cur0[1][0]-cur0[0][0])+PI/2;
   float a1 = atan2(cur0[cur0.length-1][1]-cur0[cur0.length-2][1],cur0[cur0.length-1][0]-cur0[cur0.length-2][0]);

   for (int k = 0; k <=4; k+=1){
     for (float i = 1; i < ml-1; i*=1.05){
       int ir = floor(i);
       float cd =  dist(cur0[ir][0],cur0[ir][1],cur1[ir][0],cur1[ir][1]);
       
       float j = (4-k)*cd/4;
       float r = j/cd;
       float xr = floor(lerp(cur0[ir][0],cur1[ir][0],r));
       float yr = floor(lerp(cur0[ir][1],cur1[ir][1],r));
       pushMatrix();
       translate(xr,yr);
       rotate(lerp(a0,a1,(i*1.0)/ml));
       feather(lerp(20,100,j/cd*(i*1.0)/ml+random(-0.1,0.1)),lerp(0.5,5,(cd-j)/cd*(ml-i)/ml),new float[]{coldat[0],coldat[3],pow(j/cd,0.9)*coldat[6]+random(20)*coldat[7]});
       popMatrix();
       
     }
   }    
 } }

 
 public void featherfin(float[][] cur,float[] coldat){
  for(int i = 0; i < cur.length-1; i++){
    float tng = atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]);
     pushMatrix();
     translate(cur[i][0],cur[i][1]);
     rotate(tng+random(-PI*0.1,PI*0.1));
     feather(100,0.1,new float[]{coldat[0],coldat[3],pow(0.5,0.9)*coldat[6]+random(20)});
     popMatrix();
  }
 } 
 
 public void feathertail(float[][] cur, float[] coldat){
  for(float j = 1; j < cur.length-1; j=j*1.5){
    int i = floor(j);
    float tng = atan2(cur[i+1][1]-cur[i][1],cur[i+1][0]-cur[i][0]);
    for (float k = 0; k < 1; k++){
     pushMatrix();
     translate(cur[i][0],cur[i][1]);
     rotate(tng+random(-PI/10*(cur.length-i)/cur.length,PI/10*(cur.length-i)/cur.length));
     feather(lerp(50,200,j/cur.length),lerp(0.5,0.001,j/cur.length),new float[]{coldat[0],coldat[3],pow(0.5,0.9)*coldat[6]+random(20)});
     popMatrix();
    }
  }
 } 
 public void drawfeathertail(float[] coldat){
   feathertail(bezmh(new float[][]{pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}),coldat);   
 }
 
 
 
 
 
 public void draweye(float pr,float di, float rad,float[] col1, float[] col2, float[] coldat, float[] furdat, float[]patdat){

  float[] p1;
  p1 = head.relcoord(pr,di);//0.75,-25
  stroke(col1[0],col1[1],col1[2]/10);
  strokeWeight(5);
  fill(col1[0],col1[1],col1[2]);
  ellipse(p1[0],p1[1],rad,rad);
  
  noStroke();
  
  for (float i = 0; i < PI*2; i+=PI*0.1){
    fill(col1[0],col1[1],col1[2]*random(0.8,1.0));
    float rr = random(rad*0.4);
    ellipse(p1[0]+rr*cos(i),p1[1]+rr*sin(i),2,2);
  }
  
  stroke(col2[0],col2[1],col2[2]*2);
  strokeWeight(2);
  fill(col2[0],col2[1],col2[2]);
  
  
  ellipse(p1[0],p1[1],rad/3,rad/3);
  strokeWeight(1);
  for (float i = 0; i < PI*2; i+=PI*0.2){
    stroke(col2[0],col2[1],col2[2]*1.5);
    line(p1[0]+rad/10*cos(i),p1[1]+rad/10*sin(i),p1[0]+rad/6*cos(i),p1[1]+rad/6*sin(i));
  }

  furfill(
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr+0.05,di-rad+min(rad,13)),head.relcoord(pr-0.25,di+5)}),
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr+0.04,di-rad-5),head.relcoord(pr-0.25,di+5)}),
  coldat,new float[]{furdat[0]/3,furdat[1]/3,furdat[2]},patdat,floor(furdat[3]/100));
  furfill(
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr+0.25,di+rad-min(22,rad)),head.relcoord(pr-0.05,di+rad-min(10,rad)),head.relcoord(pr-0.25,di+5)}),
  bezmh(new float[][]{head.relcoord(pr+0.45,di),head.relcoord(pr-0.05,di+rad),head.relcoord(pr-0.25,di+5)}),
  coldat,new float[]{furdat[0]/3,furdat[1]/3,furdat[2]},patdat,floor(furdat[3]/100));  
   
 }
 public void drawclaw(Limb leg, Limb paw, float size, float bend, float[] col, float[] coldat, float[] furdat, float[] patdat){

  hornil(bezmh(new float[][]{paw.relcoord(1.0,-2),paw.relcoord((size+1)/2,-bend-4),paw.relcoord(size,-5)}),4,col);
  hornil(bezmh(new float[][]{paw.relcoord(1.0,0),paw.relcoord((size+1)/2,-bend),paw.relcoord(size,0)}),4,col);
  hornil(bezmh(new float[][]{paw.relcoord(1.0,2),paw.relcoord((size+1)/2,-bend+4),paw.relcoord(size,5)}),4,col);

  furfill(
  bezmh(new float[][]{paw.relcoord(0.8,0),paw.loc(),paw.relcoord(0.8,0)}),
  bezmh(new float[][]{paw.relcoord(0.8,0),paw.relcoord(1.0,5),paw.relcoord(1.0,-15),paw.relcoord(0.8,0)}),
  coldat,new float[]{furdat[0]/3,furdat[1]/3,furdat[2]},patdat,floor(furdat[3])); 
   
 }
 public void drawear(float wd, float ht, float[] incol, float[] coldat,float[] furdat, float[] patdat){
  float b = -neck.tl1/2;
  furfill(
  bezmh(new float[][]{neck.loc(),neck.relcoord(1.0,b-ht),neck.loc()}),
  bezmh(new float[][]{neck.relcoord(1+wd,0),neck.relcoord(1.0,b-ht-20),neck.relcoord(1-wd,0)}),
  coldat,furdat,patdat,floor(furdat[3]/100)); 
  furfill(
  bezmh(new float[][]{neck.relcoord(1,b-ht/6),neck.relcoord(1.0,b-ht/2),neck.relcoord(1,b-ht/6)}),
  bezmh(new float[][]{neck.relcoord(1+wd/3,b-ht/4),neck.relcoord(1.0,b-ht),neck.relcoord(1-wd/3,b-ht/4)}),
  incol,furdat,new float[]{0},floor(furdat[3]/100)) ;
 }
   
 public void drawteeth(float wd, float ht, float[] col){
    for (float i = jaw_u.l*0.2; i < jaw_u.l*0.8; i+=wd){

     hornil(bezmh(new float[][]{jaw_u.relcoord(i/jaw_u.l,0),jaw_u.relcoord(i/jaw_u.l,ht),jaw_u.relcoord(i/jaw_u.l,ht+ht*4*noise(i*0.1))}),
     wd,col);
       }
    for (float i = jaw_l.l*0.2; i < jaw_l.l*0.8; i+=wd){


     hornil(bezmh(new float[][]{jaw_l.relcoord(i/jaw_l.l,0),jaw_l.relcoord(i/jaw_l.l,-ht),jaw_l.relcoord(i/jaw_l.l,-ht-ht*10*noise(i*0.1))}),
     wd,col);
     }  
   
 }
 public void drawtusk(float wd, float ht, float un, float ln, float[] col){
    for (float ii = 0; ii < un; ii+=1){
     float i = random(jaw_u.l*0.5,jaw_u.l*0.9);
     hornnoil(bezmh(new float[][]{jaw_u.relcoord(i/jaw_u.l,0),jaw_u.relcoord(i/jaw_u.l,ht),jaw_u.relcoord(i/jaw_u.l,ht+ht*4*noise(i*0.1))}),
     wd,col);
       }
  for (float ii = 0; ii < ln; ii+=1){

     float i = random(jaw_l.l*0.5,jaw_l.l*0.9);
     hornnoil(bezmh(new float[][]{jaw_l.relcoord(i/jaw_l.l,0),jaw_l.relcoord(i/jaw_l.l,-ht),jaw_l.relcoord(i/jaw_l.l,-ht-ht*10*noise(i*0.1))}),
     wd,col);
     } 

 }

 public void drawhorsetail(float[] coldat){

  float[][] cur = bezmh(new float[][]{pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()});
  for (int i = 0; i < 500; i++){
    noFill();
    stroke(coldat[0],coldat[3],random(coldat[6]*3));
    
    beginShape();
    float[] lastp = new float[]{cur[0][0],cur[0][1]};
    float rl = random(1);
    for (int j = 1; j < cur.length*rl; j++){

      float dis = dist(cur[j][0],cur[j][1],cur[j-1][0],cur[j-1][1]);
      float ang = atan2(cur[j][1]-cur[j-1][1],cur[j][0]-cur[j-1][0]);
      float nz = noise(i,j*0.01)*2-1;
      lastp = new float[]{lastp[0]+dis*cos(ang+nz*PI/6),lastp[1]+dis*sin(ang+nz*PI/6)};
      vertex(lastp[0],lastp[1]);
    }
    endShape();
  }
 }
   
 public void fishtail(float[][]cur0, float[][]cur1,float[] coldat){
  float sp = cur1.length*1.0/cur0.length;
  
   for (int i = 1; i < cur0.length; i++){
    
    for (int j = 0; j <= sp; j++){
      stroke(coldat[0],coldat[1],coldat[2]/2);
      strokeWeight(3);
      int ji = floor(i*sp+j);
      if (ji < cur1.length && ji >= 0){
        line(cur0[i][0],cur0[i][1],cur1[ji][0]+random(-5,5),cur1[ji][1]+random(-5,5));

      }
    } 
  
   }
  for (int i = 0; i < cur0.length; i++){
    
    for (int j = 0; j <= sp; j++){
      stroke(coldat[0],coldat[1],random(coldat[2]/3,coldat[2]));
      strokeWeight(1);
      int ji = floor(i*sp+j);
      if (ji < cur1.length && ji >= 0){

        float[] nc = new float[]{cur1[ji][0]+random(-5,5),cur1[ji][1]+random(-5,5)};
        float di = dist(cur0[i][0],cur0[i][1],nc[0],nc[1]);
        
        pushMatrix();
        translate(cur0[i][0],cur0[i][1]);
        rotate(atan2(nc[1]-cur0[i][1],nc[0]-cur0[i][0]));
        float lp = random(-1,1);
        for (int k = 0; k <= di; k+=5){ float lp2 = random(-1,1); line(k,lp,k+5,lp+lp2); lp = lp + lp2; } popMatrix(); } } } } public void drawfeatherfin(float[] coldat){ featherfin(bezmh(new float[][]{pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}),coldat); } public void drawfin_l(float[] coldat){ featherfin(bezmh(new float[][]{spine_a.loc(),forethigh_l.loc(),forethigh_l.loc()}),coldat); } public void drawfin_r(float[] coldat){ featherfin(bezmh(new float[][]{spine_a.loc(),forethigh_r.loc(),forethigh_r.loc()}),coldat); } public void drawmidfin_l(float[] coldat){ featherfin(bezmh(new float[][]{spine_b.loc(),midthigh_l.loc(),midthigh_l.loc()}),coldat); } public void drawmidfin_r(float[] coldat){ featherfin(bezmh(new float[][]{spine_b.loc(),midthigh_r.loc(),midthigh_r.loc()}),coldat); } public void drawwing(Limb wing_a, Limb wing_b, Limb wing_c, float[] coldat){ scalefill( bezmh(new float[][]{wing_a.relcoord(0,0),wing_a.loc(),wing_b.loc(),wing_c.relcoord(0.7,0)}), bezmh(new float[][]{wing_a.relcoord(0,-40),wing_b.tlloc(1),wing_c.tlloc(1),wing_c.relcoord(0.7,0)}), new float[]{10,5,0,10,10,0,30,10,10},new float[]{6,8,0},new float[]{2},10); featherfill( bezmh(new float[][]{wing_a.relcoord(0,0),wing_a.loc(),wing_b.loc(),wing_c.loc()}), bezmh(new float[][]{wing_a.relcoord(0,-40),wing_b.tlloc(1),wing_c.tlloc(1),wing_c.loc()}), coldat,new float[]{6,8,0},new float[]{2},10); } public void drawtongue(int len){ if (len > 0){
  float[][] cur = new float[len][2];
  for (int i = 0; i < len; i++){ cur[i] = head.relcoord(1+i*0.2,random(10)*i/len); } hornil(bezmh(cur),10,new float[]{0,100,50}); } } public void drawhorn(float wd, float ht, float[]col){ hornnoil(bezmh(new float[][]{head.relcoord(1.0,0),head.relcoord(1.0,-ht/2),head.relcoord(0.5,-ht)}), wd,col); } public void drawbeak(float[] col){ c.hornil(bezmh(new float[][]{c.head.loc(),c.jaw_u.relcoord(0.5,-random(30)),c.jaw_u.loc()}),c.head.tl1/3,col); c.hornil(bezmh(new float[][]{c.head.loc(),c.jaw_l.relcoord(0.5,-random(30)),c.jaw_l.loc()}),c.head.tl2/3,col); } public void drawback(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{neck.loc(),spine_a.loc(),spine_b.loc(),spine_c.loc(),pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}), bezmh(new float[][]{head.tlloc(1),neck.tlloc(1),spine_a.tlloc(1),spine_b.tlloc(1),pelvis.tlloc(2),tail_b.tlloc(2),tail_c.tlloc(2),tail_c.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawbacknotail(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{neck.loc(),spine_a.loc(),spine_b.loc(),spine_c.loc(),pelvis.loc(),tail_a.loc()}), bezmh(new float[][]{head.tlloc(1),neck.tlloc(1),spine_a.tlloc(1),spine_b.tlloc(1),pelvis.tlloc(2),tail_a.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawbelly(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{neck.loc(),spine_b.loc(),spine_c.loc(),pelvis.loc(),tail_a.loc()}), bezmh(new float[][]{neck.loc(),forethigh_l.relcoord(0.5,0),spine_a.tlloc(2),spine_b.tlloc(2),midpt(hip_l.loc(),hindthigh_l.loc())}), coldat,new float[]{furdat[0]*2,furdat[1],furdat[2]+PI/3},patdat,floor(furdat[3])); } public void drawforeleg_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{forepaw_l.loc(),foreleg_l.loc(),forethigh_l.loc(),shoulder_l.loc(),spine_a.loc(),neck.loc()}), bezmh(new float[][]{forepaw_l.loc(),forepaw_l.tlloc(1),foreleg_l.tlloc(1),forethigh_l.tlloc(1),head.tlloc(2)}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{forepaw_l.loc(),foreleg_l.loc(),forethigh_l.loc(),shoulder_l.loc(),neck.loc(),spine_a.loc()}), bezmh(new float[][]{forepaw_l.loc(),forepaw_l.tlloc(2),foreleg_l.tlloc(2),forethigh_l.tlloc(2),spine_a.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawforeleg_r(String filltype, float[] coldat, float[] furdat, float[] patdat){ float[] nc = new float[coldat.length]; arrayCopy(coldat,nc); nc[6] = nc[6]/3; nc[7] = nc[7]/3; nc[8] = nc[8]/3; anyfill(filltype, bezmh(new float[][]{forepaw_r.loc(),foreleg_r.loc(),forethigh_r.loc(),shoulder_r.loc(),spine_a.loc(),neck.loc()}), bezmh(new float[][]{forepaw_r.loc(),forepaw_r.tlloc(1),foreleg_r.tlloc(1),forethigh_r.tlloc(1),head.tlloc(2)}), nc,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{forepaw_r.loc(),foreleg_r.loc(),forethigh_r.loc(),shoulder_r.loc(),neck.loc(),spine_b.loc()}), bezmh(new float[][]{forepaw_r.loc(),forepaw_r.tlloc(2),foreleg_r.tlloc(2),forethigh_r.tlloc(2),spine_b.loc()}), nc,furdat,patdat,floor(furdat[3])); } public void drawmidleg_r(String filltype, float[] coldat, float[] furdat, float[] patdat){ float[] nc = new float[coldat.length]; arrayCopy(coldat,nc); nc[6] = nc[6]/3; nc[7] = nc[7]/3; nc[8] = nc[8]/3; anyfill(filltype, bezmh(new float[][]{midpaw_r.loc(),midleg_r.loc(),midthigh_r.loc(),midshould_r.loc(),spine_b.loc()}), bezmh(new float[][]{midpaw_r.loc(),midpaw_r.tlloc(1),midleg_r.tlloc(1),midthigh_r.tlloc(1),spine_b.loc()}), nc,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{midpaw_r.loc(),midleg_r.loc(),midthigh_r.loc(),midshould_r.loc(),spine_c.loc()}), bezmh(new float[][]{midpaw_r.loc(),midpaw_r.tlloc(2),midleg_r.tlloc(2),midthigh_r.tlloc(2),spine_c.loc()}), nc,furdat,patdat,floor(furdat[3])); } public void drawmidleg_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{midpaw_l.loc(),midleg_l.loc(),midthigh_l.loc(),midshould_l.loc(),spine_b.loc()}), bezmh(new float[][]{midpaw_l.loc(),midpaw_l.tlloc(1),midleg_l.tlloc(1),midthigh_l.tlloc(1),spine_b.loc()}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{midpaw_l.loc(),midleg_l.loc(),midthigh_l.loc(),midshould_l.loc(),spine_c.loc()}), bezmh(new float[][]{midpaw_l.loc(),midpaw_l.tlloc(2),midleg_l.tlloc(2),midthigh_l.tlloc(2),spine_c.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawhindleg_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{hindpaw_l.loc(),hindleg_l.loc(),hindthigh_l.loc(),hip_l.loc(),pelvis.loc(),tail_a.loc(),tail_b.loc(),tail_c.loc()}), bezmh(new float[][]{hindpaw_l.loc(),hindpaw_l.tlloc(2),hindleg_l.tlloc(2),hindthigh_l.tlloc(2),pelvis.loc(),tail_b.tlloc(1),tail_c.tlloc(1),tail_c.loc()}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{pelvis.loc(),hip_l.loc(),hindthigh_l.loc(),hindleg_l.loc(),hindpaw_l.loc()}), bezmh(new float[][]{spine_c.tlloc(2),hindthigh_l.tlloc(1),hindleg_l.tlloc(1),hindpaw_l.tlloc(1),hindpaw_l.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawhindlegnotail_l(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{hindpaw_l.loc(),hindleg_l.loc(),hindthigh_l.loc(),hip_l.loc(),pelvis.loc()}), bezmh(new float[][]{hindpaw_l.loc(),hindpaw_l.tlloc(2),hindleg_l.tlloc(2),hindthigh_l.tlloc(2),pelvis.loc()}), coldat,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{pelvis.loc(),hip_l.loc(),hindthigh_l.loc(),hindleg_l.loc(),hindpaw_l.loc()}), bezmh(new float[][]{spine_c.tlloc(2),hindthigh_l.tlloc(1),hindleg_l.tlloc(1),hindpaw_l.tlloc(1),hindpaw_l.loc()}), coldat,furdat,patdat,floor(furdat[3])); } public void drawhindleg_r(String filltype, float[] coldat, float[] furdat, float[] patdat){ float[] nc = new float[coldat.length]; arrayCopy(coldat,nc); nc[6] = nc[6]/3; nc[7] = nc[7]/3; nc[8] = nc[8]/3; anyfill(filltype, bezmh(new float[][]{hindpaw_r.loc(),hindleg_r.loc(),hindthigh_r.loc(),hip_r.loc(),pelvis.loc()}), bezmh(new float[][]{hindpaw_r.loc(),hindpaw_r.tlloc(2),hindleg_r.tlloc(2),hindthigh_r.tlloc(2),pelvis.loc()}), nc,furdat,patdat,floor(furdat[3])); anyfill(filltype, bezmh(new float[][]{pelvis.loc(),hip_r.loc(),hindthigh_r.loc(),hindleg_r.loc(),hindpaw_r.loc()}), bezmh(new float[][]{spine_c.tlloc(2),hindthigh_r.tlloc(1),hindleg_r.tlloc(1),hindpaw_r.tlloc(1),hindpaw_r.loc()}), nc,furdat,patdat,floor(furdat[3])); } public void drawhead(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{spine_a.loc(),neck.loc(),head.loc(),jaw_l.loc(),head.loc(),jaw_u.loc(),head.loc(),neck.loc(),spine_a.loc()}), bezmh(new float[][]{forethigh_l.tlloc(2),head.tlloc(2),jaw_l.tlloc(2),jaw_l.loc(),head.loc(),jaw_u.loc(),jaw_u.tlloc(1),head.tlloc(1),neck.tlloc(1)}), coldat,furdat,patdat,floor(furdat[3])); } public void drawbirdhead(String filltype, float[] coldat, float[] furdat, float[] patdat){ anyfill(filltype, bezmh(new float[][]{spine_a.loc(),neck.loc(),head.loc(),jaw_l.relcoord(0.5,0),head.loc(),jaw_u.relcoord(0.5,0),head.loc(),neck.loc(),spine_a.loc()}), bezmh(new float[][]{forethigh_l.tlloc(2),head.tlloc(2),jaw_l.tlloc(2),jaw_l.relcoord(0.5,0),head.loc(),jaw_u.relcoord(0.5,0),jaw_u.tlloc(1),head.tlloc(1),neck.tlloc(1)}), coldat,furdat,patdat,floor(furdat[3])); } public void drawantler(float[] coldat){ pushMatrix(); translate(neck.loc()[0]-10,neck.loc()[1]-30); rotate(-PI/4); antler(new float[] {0,0},5,200,3); popMatrix(); furball(neck.loc()[0]-10,neck.loc()[1]-30,10,coldat); } } float choice(float a,float b){ if (random(1) > 0.5){
    return a;
  }
  return b;
  
}

float rg(float x, float xmin, float xmax){
  if (random(xmin,xmax) < x){
    return x-abs(randomGaussian()*(x-xmin)/2);
  }
  return x+abs(randomGaussian()*(xmax-x)/2);
}

float rtg(float x, float xmin, float xmax){
  if (random(xmin,xmax) < x){
    return x-abs(randomGaussian()*(x-xmin)/5);
  }
  return x+abs(randomGaussian()*(xmax-x)/5);
}

float[] dark(float[] col){
  return new float[] {col[0],col[1],col[2],col[3],col[4],col[5],col[6]/2.2,col[7]/2.2,col[8]/2.2};
  
}

public int maind(float[] arr){
  int ind = 0;
  float ma = 0;
  for (int i = 0; i < arr.length; i++){ if (arr[i]>ma){
      ind = i;
      ma = arr[i];
    }    
  }
  return ind;
}

public class Generator{
  Creature c;
  float[] col;
  float[] fur;
  float[] pat;
  
  float[] bonecol;
  float[] feathercol;
  float[] fincol;
  float[] scalecol;
  String filltype;
  
  float sixleg = floor(random(4));
  float[] ctype;
  
  float patstyle = floor(random(4));
  float tailstyle = floor(random(3));
  float hoofy = (random(1));
  float clawsize = rg(2,1,2.5);
  float clawbend = rg(5,0,20);
  float eyehue = random(70);
  float winged = random(1);
    
  public Generator(){
    float a = random(7);
    float b = random(4);
    float c = random(4);
    ctype = new float[]{a/(a+b+c),b/(a+b+c),c/(a+b+c)};    
  }

  public void gen(){
    c = new Creature();
    if (patstyle == 0){
      c.makedotmap(10,100);
    }else{
      c.makedotmap(20,20);
    }
    if (random(1) > 0.2){
      filltype = "fur";
    }else{
      filltype = "scale";
    }    
    c.pelvis.tl1 = rg(50,30,100);
    c.pelvis.tl2 = rg(50,30,100);
    
    c.spine_c.l = rg(100,10,200);
    c.spine_c.a = rtg(0.9*PI,0.6*PI,1.2*PI);
    c.spine_c.tl1 = rg(20,5,50);
    c.spine_c.tl2 = rg(100,10,200);
    
    c.spine_b.l = rg(100,10,300);
    c.spine_b.a = rtg(0.1*PI,-0.5*PI,0.5*PI);
    c.spine_b.tl1 = rg(50,10,100);
    c.spine_b.tl2 = rg(100,10,200);
        
    c.spine_a.l = rg(100,10,300);
    c.spine_a.a = rtg(0.1*PI,-0.5*PI,0.5*PI);
    c.spine_a.tl1 = rg(50,10,100);
    c.spine_a.tl2 = rg(50,10,100);
    
    
    c.neck.l = rg(50,0,200);
    c.neck.a = rtg(-0.3*PI,-0.5*PI,-0.0*PI);
    c.neck.tl1 = rg(40,0,60);
    c.neck.tl2 = rg(40,0,60);
    
    
    c.head.l = rg(80,0,200);
    c.head.a = rtg(0.1*PI,-0.5*PI,0.5*PI);
    c.head.tl1 = rg(60,10,100);
    c.head.tl2 = rg(30,10,100);    
    
    
    c.jaw_u.l = rg(80,10,250);
    c.jaw_u.a = rtg(-0.1*PI,-0.4*PI,0.0*PI);
    c.jaw_l.l = rg(80,10,250);
    c.jaw_l.a = rtg(0.1*PI,0.0*PI,0.4*PI);
   

    c.shoulder_l.l = rg(20,10,100);
    c.shoulder_r.l = c.shoulder_l.l;
    c.shoulder_l.a = rtg(0.2*PI,-0.1*PI,0.6*PI);
    c.shoulder_r.a = c.shoulder_l.a + rtg(0,-PI*0.5,PI*0.5);
    c.shoulder_l.tl1 = rg(10,0,80);
    c.shoulder_l.tl2 = rg(50,10,150); 
    c.shoulder_r.tl1 = c.shoulder_l.tl1;
    c.shoulder_r.tl2 = c.shoulder_l.tl2;
    
    c.forethigh_l.l = rg(100,20,200);
    c.forethigh_r.l = c.forethigh_l.l;
    c.forethigh_l.a = rtg(0.4*PI,0.2*PI,0.6*PI);
    c.forethigh_r.a = c.forethigh_l.a + rtg(0,-PI*0.5,PI*0.5);    
    c.forethigh_l.tl1 = c.shoulder_l.tl1 * rg(0.8,0,1);
    c.forethigh_l.tl2 = c.shoulder_l.tl2 * rg(0.8,0,1);; 
    c.forethigh_r.tl1 = c.forethigh_l.tl1;
    c.forethigh_r.tl2 = c.forethigh_l.tl2;    
    
    c.foreleg_l.l = c.forethigh_l.l*rtg(1,0,2);
    c.foreleg_r.l = c.foreleg_l.l;
    c.foreleg_l.a = rtg(-0.5*PI,-0.7*PI,-0.3*PI);
    c.foreleg_r.a = c.foreleg_l.a +  rtg(0,-PI*0.5,PI*0.5);       
    c.foreleg_l.tl1 = c.forethigh_l.tl1  * rg(0.8,0,1);
    c.foreleg_l.tl2 = c.forethigh_l.tl2  * rg(0.8,0,1); 
    c.foreleg_r.tl1 = c.foreleg_l.tl1;
    c.foreleg_r.tl2 = c.foreleg_l.tl2;   
  
 
    if (hoofy < 0.3){ 
      c.forepaw_l.l = c.foreleg_l.l*rtg(0.5,0,1);
      c.forepaw_r.l = c.forepaw_l.l;
      c.forepaw_l.a = rtg(-0.2*PI,-0.7*PI,0.3*PI);
      c.forepaw_r.a = c.forepaw_l.a +  rtg(0,-PI*0.5,PI*0.5);         
    }else{
      c.forepaw_l.l = c.foreleg_l.l*rtg(1,0,2);
      c.forepaw_r.l = c.forepaw_l.l;
      c.forepaw_l.a = rtg(0.3*PI,0.0*PI,0.8*PI);
      c.forepaw_r.a = c.forepaw_l.a +  rtg(0,-PI*0.5,PI*0.5);        
    }
    if (sixleg == 1){
      c.midshould_l.l = c.shoulder_l.l * rtg(1,0.8,1.1);
      c.midshould_r.l = c.midshould_l.l;
      c.midshould_l.a = rtg(0.5*PI,-0.1*PI,0.6*PI);
      c.midshould_r.a = c.midshould_l.a + rtg(0,-PI*0.5,PI*0.5);
      c.midshould_l.tl1 = c.shoulder_l.tl1 * rtg(1,0.8,1.1);
      c.midshould_l.tl2 = c.shoulder_l.tl2 * rtg(1,0.8,1.1); 
      c.midshould_r.tl1 = c.midshould_l.tl1;
      c.midshould_r.tl2 = c.midshould_l.tl2;
      
      c.midthigh_l.l = c.forethigh_l.l * rtg(1,0.8,1.1);
      c.midthigh_r.l = c.midthigh_l.l;
      c.midthigh_l.a = rtg(0.4*PI,0.2*PI,0.6*PI);
      c.midthigh_r.a = c.midthigh_l.a + rtg(0,-PI*0.5,PI*0.5);    
      c.midthigh_l.tl1 = c.midshould_l.tl1 * rg(0.8,0,1);
      c.midthigh_l.tl2 = c.midshould_l.tl2 * rg(0.8,0,1);; 
      c.midthigh_r.tl1 = c.midthigh_l.tl1;
      c.midthigh_r.tl2 = c.midthigh_l.tl2;    
      
      c.midleg_l.l =  c.foreleg_l.l * rtg(1,0.8,1.1);
      c.midleg_r.l = c.midleg_l.l;
      c.midleg_l.a = rtg(-0.5*PI,-0.7*PI,-0.3*PI);
      c.midleg_r.a = c.midleg_l.a +  rtg(0,-PI*0.5,PI*0.5);       
      c.midleg_l.tl1 = c.midthigh_l.tl1  * rg(0.8,0,1);
      c.midleg_l.tl2 = c.midthigh_l.tl2  * rg(0.8,0,1); 
      c.midleg_r.tl1 = c.midleg_l.tl1;
      c.midleg_r.tl2 = c.midleg_l.tl2;   
           
      if (hoofy < 0.3){ c.midpaw_l.l = c.midleg_l.l*rtg(0.5,0,1); c.midpaw_r.l = c.midpaw_l.l; c.midpaw_l.a = rtg(-0.2*PI,-0.7*PI,0.3*PI); c.midpaw_r.a = c.midpaw_l.a + rtg(0,-PI*0.5,PI*0.5); }else{ c.midpaw_l.l = c.midleg_l.l*rtg(1,0,2); c.midpaw_r.l = c.midpaw_l.l; c.midpaw_l.a = rtg(0.3*PI,0.0*PI,0.8*PI); c.midpaw_r.a = c.midpaw_l.a + rtg(0,-PI*0.5,PI*0.5); } } c.hip_l.l = rg(20,10,100); c.hip_r.l = c.hip_l.l; c.hip_l.a = rtg(-0.3*PI,-0.5*PI,0.1*PI); c.hip_r.a = c.hip_l.a + rtg(0,-PI*0.4,PI*0.4); c.hip_l.tl1 = rg(80,10,200); c.hip_l.tl2 = rg(20,0,30); c.hip_r.tl1 = c.hip_l.tl1; c.hip_r.tl2 = c.hip_l.tl2; c.hindthigh_l.l = rg(100,20,200); c.hindthigh_r.l = c.hindthigh_l.l; c.hindthigh_l.a = rtg(-0.5*PI,-0.7*PI,-0.3*PI); c.hindthigh_r.a = c.hindthigh_l.a + rtg(0,-PI*0.4,PI*0.4); c.hindthigh_l.tl1 = rg(20,0,30); c.hindthigh_l.tl2 = rg(20,0,30); c.hindthigh_r.tl1 = c.hindthigh_l.tl1; c.hindthigh_r.tl2 = c.hindthigh_l.tl2; c.hindleg_l.l = rg(100,20,200); c.hindleg_r.l = c.hindleg_l.l; c.hindleg_l.a = rtg(0.5*PI,0.3*PI,0.7*PI); c.hindleg_r.a = c.hindleg_l.a + rtg(0,-PI*0.4,PI*0.4); c.hindleg_l.tl1 = rg(5,0,30); c.hindleg_l.tl2 = rg(20,0,30); c.hindleg_r.tl1 = c.hindleg_l.tl1; c.hindleg_r.tl2 = c.hindleg_l.tl2; c.hindpaw_l.l = rg(80,20,150); c.hindpaw_r.l = c.hindpaw_l.l; c.hindpaw_l.a = rtg(-0.3*PI,-0.6*PI,-0.0*PI); c.hindpaw_r.a = c.hindpaw_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_a_l.l = rg(100,10,200); c.wing_a_r.l = c.wing_a_l.l; c.wing_a_l.a = rtg(-0.8*PI,-1.0*PI,-0.5*PI); c.wing_a_r.a = c.wing_a_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_a_l.tl1 = rg(50,0,100); c.wing_a_l.tl2 = rg(0,0,10); c.wing_a_r.tl1 = c.wing_a_l.tl1; c.wing_a_r.tl2 = c.wing_a_l.tl2; c.wing_b_l.l = rg(100,10,200); c.wing_b_r.l = c.wing_b_l.l; c.wing_b_l.a = rtg(0.2*PI,0.0*PI,0.7*PI); c.wing_b_r.a = c.wing_b_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_b_l.tl1 = rg(80,0,160); c.wing_b_l.tl2 = rg(0,0,10); c.wing_b_r.tl1 = c.wing_b_l.tl1; c.wing_b_r.tl2 = c.wing_b_l.tl2; c.wing_c_l.l = rg(100,10,200); c.wing_c_r.l = c.wing_c_l.l; c.wing_c_l.a = rtg(-0.3*PI,-0.6*PI,0.0*PI); c.wing_c_r.a = c.wing_c_l.a + rtg(0,-PI*0.4,PI*0.4); c.wing_c_l.tl1 = rg(80,0,160); c.wing_c_l.tl2 = rg(0,0,10); c.wing_c_r.tl1 = c.wing_c_l.tl1; c.wing_c_r.tl2 = c.wing_c_l.tl2; c.tail_a.l = rg(100,0,200); c.tail_a.a = rg(0,-PI/3,PI/3); c.tail_a.tl1 = rg(5,0,50); c.tail_a.tl2 = rg(10,0,50); c.tail_b.l = rg(100,0,200); c.tail_b.a = rg(0,-PI/3,PI/3); c.tail_b.tl1 = rg(5,0,50); c.tail_b.tl2 = rg(10,0,50); c.tail_c.l = rg(100,0,200); c.tail_c.a = rg(0,-PI/3,PI/3); c.pelvis.a = -PI+ atan2(c.spine_a.loc()[1]-c.pelvis.loc()[1],c.spine_a.loc()[0]-c.pelvis.loc()[0]); bonecol = new float[]{rtg(5,0,50),random(5,15),90}; fincol = new float[]{random(100),0,0,rg(5,0,50),0,0,random(30),random(0.2,1),0}; feathercol = new float[]{rtg(5,0,80),0,0,rg(5,0,50),0,0,random(60),random(0.5,1),0}; if (filltype == "fur"){ col = new float[]{rtg(5,0,30),2,10,random(10,50),-10,10,random(30),20,50}; }else if (filltype == "scale"){ col = new float[]{random(100),2,10,random(10,50),-10,10,random(20),10,10}; } fur = new float[]{rg(6,0,20),8,0,50000}; pat = new float[]{floor(random(4))}; } public void render(){ //c.drawbody(); if (maind(ctype) == 2){ winged = 1; } switch (maind(ctype)){ case 0: c.drawforeleg_r(filltype,col,fur,pat); c.drawhindleg_r(filltype,col,fur,pat); c.drawclaw(c.foreleg_r,c.forepaw_r,(clawsize+1)/2,clawbend,bonecol,dark(col),fur,pat); c.drawclaw(c.hindleg_r,c.hindpaw_r,(clawsize+1)/2,clawbend,bonecol,dark(col),fur,pat); if (sixleg == 1){ c.drawmidleg_r(filltype,col,fur,pat); c.drawclaw(c.midleg_r,c.midpaw_r,(clawsize+1)/2,clawbend,bonecol,dark(col),fur,pat); } break;case 1: c.drawfin_r(dark(fincol)); if (sixleg == 1){ c.drawmidfin_r(dark(fincol)); } break;case 2: c.drawhindleg_r("scale",col,fur,pat); c.drawhindlegnotail_l("scale",col,fur,pat); c.drawclaw(c.hindleg_r,c.hindpaw_r,clawsize,clawbend,bonecol,dark(col),fur,pat); c.drawclaw(c.hindleg_l,c.hindpaw_l,clawsize,clawbend,bonecol,col,fur,pat); break; } if (winged > 0.8){
      c.drawwing(c.wing_a_r,c.wing_b_r,c.wing_c_r,dark(feathercol));
    }

    switch (maind(ctype)){
      case 0:
        if (tailstyle == 1){
          c.drawhorsetail(dark(col));
          c.drawbacknotail(filltype,col,fur,pat);
          
        }else if (tailstyle == 2){
          c.drawfeatherfin(feathercol);
          c.drawbacknotail(filltype,col,fur,pat);
          
        }else{
          c.drawback(filltype,col, fur, pat);
          
        }break;
      case 1:
        c.drawfeatherfin(fincol);
        c.drawbacknotail(filltype,col,fur,pat);
        break; 
      case 2:
      
        if (random(1) > 0.5){
          c.drawfeathertail(feathercol);
        }else{
          c.drawfeatherfin(feathercol);
        }
        c.drawbacknotail(filltype,col,fur,pat);
        break;
       
    }
    c.drawbelly(filltype,col,fur,pat);

    if (random(1) <0.2){
      c.drawhorn(random(5,30),random(100,300),bonecol);
    }
    c.drawtongue(floor(random(2,12)));
    
    if (maind(ctype) == 2){
      c.drawbeak(bonecol);
      c.drawbirdhead(filltype,col,fur,pat);
    }else{
      
      c.drawhead(filltype,col,fur,pat);
      c.drawteeth(random(3,8),random(3,4),bonecol);
      if (random(1) <0.2){ c.drawantler(col); } if (filltype == "fur"){ c.drawear(random(0.1,0.3),random(20,120),new float[]{random(10),2,10,random(20),-10,10,random(50,80),20,50},col,fur,pat); } if (random(1)>0.5){
        c.drawtusk(random(3,10),random(3,20),random(3),random(3),bonecol);
      }
    }

    c.draweye(0.75,-c.head.tl1*random(0.1,0.8),random(15,40),new float[]{eyehue,random(30),random(80,100)},new float[]{eyehue,random(60),random(0,50)},col,fur,pat);
    if (winged > 0.8){
      c.drawwing(c.wing_a_l,c.wing_b_l,c.wing_c_l,feathercol);
    }


    switch (maind(ctype)){
      case 0: 
        c.drawforeleg_l(filltype,col,fur,pat);
        
        if (tailstyle == 0){
          c.drawhindleg_l(filltype,col,fur,pat);
        }else{
          c.drawhindlegnotail_l(filltype,col,fur,pat);
        }
        c.drawclaw(c.foreleg_l,c.forepaw_l,(clawsize+1)/2,clawbend,bonecol,col,fur,pat);
        c.drawclaw(c.hindleg_l,c.hindpaw_l,(clawsize+1)/2,clawbend,bonecol,col,fur,pat);
        
        if (sixleg == 1){
          c.drawmidleg_l(filltype,col,fur,pat);
          c.drawclaw(c.midleg_l,c.midpaw_l,(clawsize+1)/2,clawbend,bonecol,col,fur,pat);
        }
      break;case 1:
        c.drawfin_l(fincol);
        if (sixleg == 1){
          c.drawmidfin_l(fincol);
        }
      break;case 2:
        
      break;
    }

  }

}





float[] trans = new float[] {500,400};
Creature c = new Creature();
Generator g;
float[] bd;

void settings(){
  if(export){size(2000,2000);}
  else{size(800,800);}
}
void setup(){
  if(!export){noLoop();}
  colorMode(HSB,100,100,100);  
}
void draw(){
  //c.wing_c_l.a += PI/4;
  float flippy = choice(0,1);
  pushMatrix();
  g = new Generator();
  bd = new float[]{0,0,0,0};
  g.gen();
  if (flippy == 1){
    g.c.pelvis.flip();
  }
  g.c.pelvis.bounds(bd);
  if (flippy == 1){
    g.c.pelvis.flip();
  }
  background(18,4,100);
  
  float sc = min(width*0.7/(bd[1]-bd[0]),height*0.7/(bd[3]-bd[2]));
  translate(width/2-sc*(bd[1]-(bd[1]-bd[0])/2),height/2.2-sc*(bd[3]-(bd[3]-bd[2])/2));//width/2-bd[0]-(bd[1]-bd[0])/2,height/2+bd[2]+(bd[3]-bd[2])/2);
  scale(sc);
  scale(1-flippy*2,1);
  
  g.render();
  popMatrix();

  if(debug){stroke(0,100,100);g.c.pelvis.drawSkel();}
  if(export){saveFrame("render"+nf(day(),2)+nf(hour(),2)+nf(minute(),2)+nf(second(),2)+nf(millis(),3)+".png");}

}

Sketchbook

snip20161027_23 snip20161027_24

Written by Comments Off on Ngdon-Book Posted in Book

Jaqaur – Book

    GENESONG

img_3175

For my generative book project, I decided to make a song book. I called it “Genesong” (like “Genesis” or “Generate” and “Song”), and it contains 30 songs, each with rhyming lyrics, musical notes, and guitar chords. I had a lot of ideas for this book that I ended up not being able to execute, but I am very proud of how much I was able to do. I’ll go through the steps that are part of generating each song (so this is done thirty times).

song_book_flip

    GENERATION PROCESS

1. A key is chosen. I decided to limit the book’s choices to major keys, particularly Ab major, Eb major, Bb major, D major, C major, F major, and G major. This was mostly because minor keys would require different chord progressions, and would mean a lot more work and conditional statements. Throughout the book, every pitch was represented by an integer, with middle C being 0 and each half step up being an increment of 1. The keys for each song were represented by the integer of their base note, so Ab was -5, Eb was 3, etc. For this project, I disallowed changing keys mid-song.

2. A chord progression is chosen. I limited the options to progressions with four steps, just so that the music would all fit a general form. I picked progressions that are pleasant/generally popular: I-ii-IV-V , I-vi-ii-V, I-IV-V-V, I-V-vi-IV, and I-vi-IV-V.

3. A rhyme scheme is chosen. I wanted to keep these very simple, so that the rhymes can be identified despite what will be fairly nonsense lyrics. So, the only options are “ABAB”, “AAAA”, and “AABB.” The rhyme schemes affect not only the lyrics, but the music, because in general, rhyming lines in songs tend to feature parallel rhythms. That is not a rule in music, but for this project, I decided to make it one.

4. A “rhythm line” is generated. When a rhythm line is created, it is told how many beats it needs to fill. This amount will be four times the number of measures in the line (which is a random int between 3 and 5 inclusive). All songs are 4/4 time, just to keep things simple. A rhythm line knows how many elements (notes and rests) there are, how long each one lasts, and whether each element is a note or a rest. This was all randomly generated, but with a few parameters: the total length must be the length that was passed in (12, 16, or 20), the only possible lengths for elements are 0.5, 1, 2, 3, and 4 (I didn’t want to deal with dotted quarter notes, ties, etc, but as you’ll see I failed in that), the line cannot start with a rest, the line cannot end with a rest, and a rest cannot be longer than 1 beat (with such short lines and so little musical complexity, I didn’t want to make anyone sit through a long rest).

5. More rhythm lines are generated such that there are four per verse and four per chorus, and lines that rhyme have identical rhythm lines.

6. Each note is given an integer pitch based on what chord it is from (which is calculated from the key combined with where we are in the chord progression). Each line starts with the base note of said chord, just to have a nice musical landmark every few measures. Rests are given a pitch value of 100 (which is invalid).

7. Lyrics are generated via RiTa’s random word generator. I had hoped to make these coherent Markov-based sentences, but making those fit syllabically with the music and also fit in the rhyme scheme was too difficult to achieve (I tried a loop that kept re-generating lines until one worked, and would repeat this process for each song, and it often ran for several minutes with no positive results. This happened no matter what texts I put into the Markov chain, and for time’s sake I had to simplify things). So, each note gets one one-syllable word, about 80% of which are from RiTa’s lexicon, and 20% of which are randomly pieced together from an array I made of word beginnings (eg. “br”,”ch”, “st”) and word ends (eg. “ess”, “ind”, “ay”). If a word is at the end of the line, it will 100% of the time be one of the piece-meal words. Then, if it needs to rhyme with something else, it is just given the same word ending and a different beginning. When a line is complete, the first word is capitalized and a random punctuation mark is added. Random hyphens are also added (only after words that are non-lexicon) to imitate multiple-syllabled words. (Three sets of lyrics are generated for each line in the verses, one for the chorus).

8. A title of length 1-4 words is chosen from the beginning of the chorus.

An early rough-draft of a verses page. An early rough-draft of a verses page.

Once all 30 songs are generated, we have to display each one. Here are those steps.

    DISPLAY PROCESS

1. Print the title at the top of the page (if this is a verse page). Then, find your starting point (passed in as a parameter). Calculate how far apart every element in this line needs to be, based on how wide the page is and how many elements there are.

2. For each element, figure out what image you need (based on whether or not its a rest, how long the element is, and what pitch the element is). If you have room in the measure (kept track of by a measure-beat-counter–each measure needs exactly four beats), just put that image on the page, calculating its x position based on which element in the line this is, and its y position based on its pitch and the distance between the staff lines). If you do NOT have room, put down however much you have room for, draw the measure line, and then put down the rest, then draw a tie between them. This part of the code was much more difficult than I thought, because it forced me to use all the things I was trying to avoid before, like dotted quarter notes, ties, and trying to express things like “3.5 beats” with as little superfluous notation as possible. When I used multiple note symbols for one element, I sometimes had to cram them together (see below) so as not to exceed the amount of horizontal space that one element was given. I should have done more to avoid situations like this in my original generation of the rhythms. Ultimately, I accounted for this by reducing the maximum number of possible measures per line from 7 to 5, just to ensure a reasonable amount of space. Also, the lyrics for each note were added to the document at the same time as their note and at the same x position.

screen-shot-2016-10-23-at-11-08-01-pm screen-shot-2016-10-23-at-11-53-34-pm Some squished notes in early iterations of “Genesong”

3. Do step 2 for every line. Then, add things like the staff lines themselves, the treble clef, the time signature, the repeat signs, the page number, and (this was a late addition) the chord names above the lines.

4. A fun addition: add a randomly generated adverb from RiTa’s lexicon at the beginning of the piece. I did this mostly to poke fun at sheet music that demands to be played “nonchalantly” or “youthfully” or other vague terms like that. Some of the generated adverbs made some sense in context, and others (see below) did not, like “architecturally,” or “whitely.” I prefer it this way.

screen-shot-2016-10-25-at-1-49-29-am
screen-shot-2016-10-26-at-11-04-36-am
screen-shot-2016-10-25-at-1-51-04-am
screen-shot-2016-10-25-at-1-49-19-am
screen-shot-2016-10-25-at-1-49-09-am
screen-shot-2016-10-24-at-8-43-48-pm
screen-shot-2016-10-26-at-11-11-33-am
screen-shot-2016-10-25-at-1-51-34-am

Once all of the songs were displayed (actually, this is done first, but I didn’t want to wreck the narrative I had going), a table of contents is generated using the title of every song and its respective page number. This is all put together and exported as one .pdf file. The cover was made separately.

SOME MORE THINGS TO SAY

I chose not to give too much explanation in the book of what it was. On the cover, it mentions that the songs are computationally generated, but otherwise I wanted Genesong to present itself as a real song book. I tried to use fonts that conveyed a slightly old-fashioned piano book vibe, and (in theory) formatted it as if it could be “real” sheet music.

img_3177

There were a lot of ideas I had for this project that I was not able to complete in the two-week time frame, including some I mentioned above (like lyrics that had actual meaning) and some other ones (like the use of Markov chains to put more notes next to each other that “go” together). I would like to return to this project in the future, and hopefully improve upon it. One thing I would really like to add is a base clef with chords and notes to be played alongside the melody. Dynamics, fermatas, and other such musical things would be great, too. Below are some pages of my notes/plans before I began coding.

img_3188

img_3187

My original notes/plans before any coding was done.

Even as it is, my project is imperfect. Sometimes, an usually long syllable and an unusually packed line came together to equal lyrics printed on top of each other (see below). Also, the resolution did not turn out as well as I had hoped, and you can see that the notes were often a bit pixelated on the paper copy (see below). Despite these little things, I am very very proud of Genesong, and I hope to have it played and sung aloud in the near future so people who don’t read music can appreciate it!

img_3181 img_3183

Here’s a video of Professor Levin flipping through my book:

Here is a link to my code on Github: https://github.com/JacquiwithaQ/60212/tree/master/Song_Book

Here is the PDF of the iteration I chose to print: song_book-38-A-good.pdf

Here are some more/alternative iterations of the book:
song_book-22
song_book-32
song_book-36

(Here is where I would embed my code, if it weren’t 1000+ lines across 6 Processing files. Golan, let me know if you really want me to embed it anyway, and I will.)

And, finally, here is a picture of Processing being sassy with me:
img_2653

Written by Comments Off on Jaqaur – Book Posted in Book

Keali-LookingOutwards06

The bot * (@soft_focuses) is a “poetic experiment” whose tweets evoke an atmospheric and serene theme–I spent considerable time deciding between this and the poem.exe (@poem_exe), as both appealed to me with its introspective and calm sense of literary aesthetic. Plenty of the generated poetry are short, subtle, and perhaps mysterious; not always grammatically correct, the diction and brief eloquence of the vocabulary is enough to present some soft and gentle imagery (as its username suggests). The tweets are non-formulaic and almost cursory reminders to myself to ponder within myself, and to think of the outside world, in a sense of “it’s the little things in life…”–and to laugh at the occasional hilarious outputs. In a way, it’s lovely for such fragments to possibly lead to extensive, deep thoughts.

tweets

kander – book

“Ideally We Meeta Few Times xa Week and Have Bottle Rocket Sex” juxtaposes randomly generated cartoon phalluses with sentences from “Men Seeking Women” advertisements from the Pittsburgh Craigslist. There is much comedy to be found in both the images and the sheer absurdity of many of the captions. Each combination makes a different comment on the aims of the poster: some give off an air of patheticness, some douchebaggery, some downright skeeze — highlighting the unexpectedly complex and layered meaning of a ubiquitous form of imagery in our society: the dick drawing.

The idea to draw phalluses actually came from some rather lewd commentary from one of my neighbors about my FaceOSC project. I had been wanting to do some more generative work since seeing Xastol’s generative plotter faces, and I thought the idea of little cartoon dicks would be hilarious. Additionally, right off the bat, I knew I wanted to include some sort of interesting data set or other information with comedic value. For me, having text was a must for this project — not that I couldn’t have worked with text for other projects, but all of my favorite examples from this project in particular (Death Death Death and Crazy But True Cat Stories included) were heavily reliant on text.

I first thought that I could collect data about, oddly enough, bathroom cleaning schedules (the little timesheets that they have in bathrooms of chain restaurants or gas stations that let us know when the bathroom has last been cleaned). Then, I thought I might want to caption the drawings with text messages from my ex-boyfriend, or perhaps from tweets containing a certain hashtag (I was thinking #TGIFridays).  After I made my final decision about the text, I ended up hand-selecting the Craigslist ads (although I was algorithmic in the sense that I used the most recent ones) for several reasons:

1) Craisglist doesn’t have an easily accessible API, and I didn’t have the coding prowess to figure out how to scrape Craigslist from scratch without sacrificing other elements of the project.

2) More importantly, I wanted to minimize the trolling posts that I used in the book (a few are thrown in there) because the genuine ones are much funnier.

3) Going through the posts was a hilariously good time.

Although the posts were selected manually, they were formatted computationally with Basil.js.

I’m glad I finally decided on Pittsburgh Craigslist ads — I think this project is hard to pull off if it’s not funny, and the sentences from the ads really bolster the humor.

I spent a lot of time trying to figure out the best way to draw a dick. At first, I went with the to-be expected two ellipses and a rectangle, but I realized that this approached made boring images. Once I had some of the concrete mathematical details down, I elected to create custom PShapes for the shaft and the head, and have the length, testicle diameter vary, and have the perspective vary slightly as well. Retrospectively, I would have included more variation, hairs, and maybe even warts, and if we’re really dreaming big, I might have even done the whole thing in 3d (but, at the same time, I like the cartooney quality of my result).

Click here to see the interior of the book.

Click here to see the GitHub repository for the project.

This is the original sketch for the idea, showing the variation in dimensions and orientation that I was going for.

unnamed

This was one of my first attempt to get the alignment right with ellipses and a rectangle

prelim_program

This is a later attempt using lines. I kind of like where this one was going — I think if I had adapted it to use curves, it could have also turned out nice.

prelim_program2

A fairly run-of-the-mill representative of the final product

image-19


Here’s a video shot by the professor, flipping through the book:

Below is the code for each respective element:

Image generation:

PShape d;
PShape b;
PShape h;
float theta;
float theta2;
float x;
float y;
float xHold;
float yHold;
float offSetX;
float offSetY;
float scale;
float yScale;
float len;
float isFlipped;
float translation;
float headWidth;
float headHeight;
float girth = random(3, 7);
color d1;
color d2;
int page_num = 0;

void setup() {
  background(255);
  size(550, 550);
  frameRate(30);
}

void draw() {
  background(255);
  stroke(0, 200);
  isFlipped = random(1, 3);
  yScale = random(15, 35);
  len = random(20, 200);
  scale = random(1, 1.2);
  float col = random(1, 4);
  if (col < 2) { setColorsPinks(); } if (col >=2 && col < 3) {
    setColorsBrowns();
  } else {
    setColorsTans();
  }
  //background(100);
  translation = random(30, 50);
  translate(width/2, height/2);
  float angle = random(-PI/2, PI/2);
  rotate(angle);
  translate(-width/2, -height/2);
  if (isFlipped <= 2) {
    translate(width/2, height/2);
    scale(-1, 1);
    translate(-width/2, -height/2);
  }
  drawDick(d1);
  translate(width/2, height/2);
  scale(-scale, scale);
  translate(translation, 0);
  translate(-width/2, -height/2);

  drawDick(d2);

  translate(-translation/2, 5);
  drawHead(d2);
  for (int j = 0; j < random(2, 3); j++) {
    stroke(0);
    drawVeins();
  }
  if(page_num < 30){
    saveFrame("image-" + str(page_num) +".png");
  }
  page_num += 1;
}

void setColorsPinks() {
  d1 = color(random(240, 255), random(210, 225), random(160, 180), 220);
  d2 = color(random(240, 255), random(210, 225), random(160, 180));
  //head = color(random(240, 255), random(210, 225), random(160, 180));
}

void setColorsBrowns() {
  d1 = color(random(75, 85), random(40, 47), random(0, 30), 220);
  d2 = color(random(75, 85), random(40, 47), random(0, 30));
  //head = color(random(75, 85), random(40, 47), random(0, 30));
}

void setColorsTans() {
  d1 = color(random(195, 205), random(150, 160), random(110, 115), 220);
  d2 = color(random(195, 205), random(150, 160), random(110, 115));
  //head = color(random(195, 105), random(150, 160), random(110, 115));
}

void drawDick(color c) {
  stroke(0, 200);
  d = createShape();
  d.beginShape();
  d.strokeWeight(2.5);
  d.fill(c);
  x = width/2;
  y = height/2;

  d.vertex(x, y);
  x = x - girth;
  y = y + len;
  d.vertex(x, y);
  xHold = x + 20*cos(-PI/2) + girth/2;
  yHold = y + 17;
  for (int i = 0; i < 100; i++) {
    theta = map(i, 0, 99, -PI/2, PI);
    x = xHold + 30*cos(theta);
    y = yHold + yScale*sin(theta)*1.25;
    d.vertex(x, y);
  }
  x = x - girth;
  y = height/2;
  d.vertex(x, y);
  d.endShape();
  shape(d);
}

void drawHead(color c) {
  stroke(0, 200);
  headHeight = random(35, 50);
  h = createShape();
  h.beginShape();
  h.fill(c);
  h.strokeWeight(2);
  h.vertex(width/2, height/2);
  h.vertex(width/2 - 20, height/2);
  for (int i = 0; i < 100; i++) {
    theta = map(i, 0, 99, 52*PI/50, 98*PI/50);
    x = width/2 + 27*cos(theta);
    y = height/2 + headHeight*sin(theta);
    h.vertex(x, y);
  }
  h.vertex(width/2 + 20, height/2);
  h.vertex(width/2, height/2);
  h.endShape();

  shape(h);
  stroke(0);
  float ay = height/2 + headHeight*sin(3*PI/2);
  float divLen = random(10, 20);
  stroke(0, 100);
  line(width/2, ay, width/2, ay + divLen);
}

void drawVeins() {
  stroke(0);
  strokeWeight(2);
  float ax = random(width/2 - 5, width/2 + 10);
  float ay = random(height/2, height/2 + len);
  line(ax, ay, ax, ay + random(10, (height/2 + len - ay)));
}

Text generation:

import rita.*;
String entries[] = new String[30];
JSONObject json;

void setup() {
  //basically just initializes all the craiglist entries I'll be using

  entries[0] = "Im in search of a beautiful young woman between the age of does not matter-23 who is seeking an older man for a relationship and to treat her like a princess inside and outside of the bedroom. She should be tall have long hair long legs and a gorgeous butt and on birth control, highly recommended but not a deal breaker. She will receive alot of presents and affection . I am very serious about treating a very special young lady this way and if your interested I expect you to be very serious about accepting. SERIOUS REPLIES ONLY. Send a full clothed body pic with face along with stats. The closer you are to me the more you will be spoiled. Your pic will get mine once you prove your serious enough.";
  entries[1] = "I have a big, roomy Suv and I've got a taste kink. Any women out there feel the same way? I've got all the time in the world today and I'm looking to have some fun. Put your favorite color in the subject line so I know you're not a bot. Let's chat a little bit and see what we'd like to do on this dreary day.";
  entries[2] =
    "I think it's finally reached the point where i should throw in the towel with craigslist. I get "
    +"almost no replies from real people. I used to at least get the occasional interesting reply or two." 
    +" I'm bored. Are you tired of the same old/same old too? Let's talk and take it from there. I'm " 
    + "pretty open-minded even if that means we just talk. Please reply with a picture and a little about"
    + " yourself. Excuse the cheesy pictures, but I'm not putting my face on cl so I figured these were" 
    + " better than nothing. Note: Someone stole one of my pics and is using it to post ads. If you see an"
    + " ad with my picture talking about bi-guys, bi-girls, and hsv...know that it is NOT me. I am not bi."
    + " I do not have hsv. Nothing in the ad is true about me. That is someone using my picture without my"
    + " permission.";

  entries[3] = "Hi! Is anyone else bored tonight? It's so nice outside. Anyways... I'm new-ish to the area and have"
    + " been single for about a year. I miss cuddling and making out. Anyone up for a movie late tonight? I" 
    + " am. Who me? 33, white, attractive, 6'1, financially stable, four twenty friendly, movie lover," 
    + " crazy about kissing.You? Pic for pic! Yours gets mine." 
    + " Ps you can pick the movie.";

  entries[4] = "Swm, 6' tall, average looks, good body, tight tush, great sense of humor, will buy dinner for any" 
    + " female on one condition: you wear a skirt and hose/nylons. I bet I will be eating alone tonight.";

  entries[5] = "Oh it has a first name!!"
    + " Looking for a fwb scenario so my kabossy can fit that bun!!"
    + " Ideally we meeta few times xa week and have bottle rocket sex."
    + " I have skills that will blow you're mind."
    + " In fact if you don't get with me you're missing out on the treat of a lifetime." 
    + " My kabossy is in charge!!";

  entries[6] = "hello , Im 33 yo white male , 6ft1 180lbs, SINGLE ,in good shape,two part time jobs,no kids,been" 
    + " told im goodlooking.looking for a Long term relationship . I have my own place and i drive.. I am a" 
    + " country/redneck/metalhead type guy all wrapped in one...some fav bands...Misfits,Metallica,Social" 
    + " D, Dropkick Murphys, AC-DC, Disturbed, Fear Factory, and thats all i can think of right now.I have" 
    + " 4 tattoos, one on right bicep and 3 on left forearm, id like to get more in the future...I am" 
    + " hoping to meet the right one in 2016 since its been a few years since ive had a relationship...Not"
    + " looking for liars , whores , wana be gangsters , gold diggers , game players ,and drama queens, or" 
    + " women still attached to their ex.";

  entries[7] = "Me:"
    + " I am tall, in shape, and intelligent. I hear I am easy on the eyes, and have all of my teeth. I"
    + " have a decent job and my own place, although I am new to the area. I am 420 friendly, but other" 
    + " wise drug free. I am disease and drama free as well and have no kids or anything."
    + " You:"
    + " Honesty is pretty crucial."
    + " Cute although you dont exactly have to think so haha"
    + " Smart"
    + " Job, car, school, art, or something you do with your life"
    + " 420 friendly is a plus"
    + " Piercings and tattoos are also a plus"
    + " Disease/kid/drama free";

  entries[8] = "Anyone up late this evening and want to chat and see where it leads? Maybe make a friend or maybe" 
    + " something more? Send me an email so we can chat! I am open to any age, relationship status, race," 
    + " etc. as long as you are female lol so don't be shy :)";

  entries[9] = "Anybody feel like hangin out and having good conversation? We could talk about anything you feel" 
    + " like.";

  entries[10] ="I'm white male regular build not bad looking. I have no tats, no piercings, never married, no kids." 
    + " No crazy friends, no crazy ex's. I'm more of a homebody and stay out of trouble. I like going out "
    + " to all depending on what to do. I'm looking for someone to talk too who is just a normal regular "
    + " person,"
    + " I'm friendly and nice. Down to earth and chill. If you wanna know more..."
    + " Put 'email' in subject line or send me a text"
    + " FOUR ONE TWO NINE FIVE ONE FOUR FIVE SEVEN TWO";


  entries[11] = "Hey ladies, I guess I just wanna keep it quick and to the point lol. I'm not looking for a"
    + " relationship or anything serious, just a girl to hangout and mess around with on occasion. If you"
    + " would be interested in that kind of thing reply to my post and let's see what happens. Please"
    + " attach a picture of yourself and put your boob size in the subject line so I know you're legit.";

  entries[12] = "I am not picky tonight. Invite me over already. I can travel to you if things feel right.";

  entries[13] = "Hi There!! I am from California... I live in San Diego, I've always wanted to travel but I haven't" 
    + " been here :/ only through CL haha... But I don't want to go without finding someone who I can trust"
    + " and who can show me around so I have a good time :)"
    + " Possibly to meet and have a connection."
    + " About Me: I'm a chubby white guy, long blond hair and blue eyes :)" 
    + " I'm 27, have a good job and I'm easy going."
    + " You can email me or KIK me at"
    + " Tcandee";

  entries[14] = "Contact me tonight....discreet fun wanted..I'm in shape,sense of humor, 40s..text 'I'm in' to"
    + " fouronetwothreeeightninetwoonefivesix. Married / attached ,couples welcome";

  entries[15] = "Howz about a nice collaboration between friends? We can have pizza pie as I spice it up with my" 
    + " sausage as the perfect ingredient.";

  entries[16] = "Hey anybody want to get together tonight? We can catch a movie or do oral? We can Iggy wiggy liol.";

  entries[17] = "I am at the point where making time to slowdown, relax and enjoy is important, I like the" 
    + " interaction of dinning in, dinning out, the connection created by preparing and sharing a meal" 
    + " together, an evening set aside for a nice wine, conversation, candles, teasing, pleasing and an" 
    + " antipasto to nibble on. Lol";

  entries[18] = "Laughter is important, my sense of humor can range from playful to sarcastic. Not looking to change" 
    + " my life or anyone else's other than filling the void. Passionate, expressive and looking for the"
    + " same. Definitely not an out of sight out of mind person, I like to keep in touch throughout the" 
    + " day, share thoughts, feelings and events. For me the mental connection is important and" 
    + " communication is key. I don't mean to offend anyone but please be local to the Pittsburgh area," 
    + " over 45, ready to slowdown and enjoy, willing and able to make time to do so and looking to share" 
    + " and enjoy plenty of pleasure, passion and fun. If interested message me, put your favorite wine in" 
    + " the subject line, please include a photo, what you're looking for, what you enjoy/find pleasure in" 
    + " and PLEASE!!! Be serious and looking for something regular and ongoing, the more we have to build" 
    + " on the hotter it will get. Thank you.";

  entries[19] = "would love to be a live in companion and a lover to a naughty older woman 60-85,any race," 
    + " religion. I am an east Indian male NY raised 55 years old and an American citizen. I don't have" 
    + " Indian accent when I speak English and I have an MBA. I need a place to stay. I will caress you" 
    + " allover, give full body massages, give you great sex, cook and your chores. Please respond with" 
    + " your phone number.";

  entries[20] = "For reals. I love breast play. Yours, not mine lol. Hoping to find someone out there that loves" 
    + " having your nipples sucked. Such a turn on for me. Well and kissing too." 
    + " I can drive or host. Anyone curious? Please include a face picture of yourself and I'll return the" 
    + " favor. Thanks!";

  entries[21] = "Ok who on this site really wants to meat? Let's meat today and meat consistently if that's okay. So" 
    + " let's do this.";

  entries[22] = "I'm a nightime person who likes to stay up past midnight. Any women on here looking to chat who" 
    + " likes to stay up past midnight? If so put midnight in subject line";

  entries[23] = "Looking for guys and girls who want to throw down some fun. We got the dunking stix so you supply" 
    + " the honey buns.";

  entries[24] = "So as I have stated just out of a bad relationship. My ex just could not understand my experiments" 
    + " with men. So maybe you will understand? Hey we all have needs right?";

  entries[25] = "I'm looking for a nice young thick girl to spoil and give gifts to. This can be one time or" 
    + " ongoing. Please no bbw. Please reply with favorite color in subject. Also send pics and a phone #";

  entries[26] = "This is a long shot I know. And I know most if not all of you that read this will judge and form" 
    + " your opinions. That's okay. Hopefully there is one woman out there that is adventurous and open" 
    + " minded enough or maybe in a similar situation that they understand."
    + " I guess I will just lay it all out on the table. I am a 54 yr old married man. Due to wife's lack" 
    + " of interest, there hasn't been much in the way of 'sex' for quite some time. My release comes from," 
    + " you guessed it, masturbation. I am looking for a woman who is possibly in the same situation that" 
    + " might be interested in getting together for some mutual masturbation. Not looking for intercourse." 
    + " We could watch each other or we could lend a hand and help each other. The choice would be yours." 
    + " Absolute discretion is a must !! Prefer you be over 40 yrs old. Married or single. If this sounds" 
    + " like something you would like to possibly discuss, please send me an email and tell me your" 
    + " thoughts on the subject and we can go from there. Women Only Please !!!!";

  entries[27] = "Are there any cute girls out there who might be interested in a tribute? If you don't know what" 
    + " that is it is when a girl sends a sexy pic or 2 to a guy who then drops his load on the picture and" 
    + " takes a picture of that and sends it back to the girl."
    + " It's just a fun flirty activity and doesn't require any personal meets or anything like that. If" 
    + " this is something you'd be interested in please send me a pic and what you're looking for and" 
    + " please put tribute in the title so I know you're real."
    + " I'm real -- today is Friday and has been sunny but cooler but the weekend will warm up nicely.";

  entries[28] = "I am a mature, married busy workaholic & needing assistance with stress relief from time to time. I" 
    + " am seeking a steady & reliable massage therapist( don't worry about experience, I can teach you the" 
    + " techniques) who can host with great hands for a good sensual full body massage at least once a" 
    + " week. No need to look for a p/t job, do more with the time saved and let's help each other out." 
    + " Deal with one sane and decent guy, no need to hook up with random men. Ideal candidate would be a" 
    + " woman who is uninhibited with a great sense of humor and understands the need for privacy. Looking" 
    + " to start soon. This is a perfect opportunity for stay at home mom (after the kids are at school)" 
    + " retired woman( age is not an issue), college student or any woman looking to supplement her income." 
    + " I have a flexible schedule which will enable us to work around yours. Please put relief in subject"
    + " line so I will know that you are real and sincere.";


  entries[29] = "Quit flagging and sending racist emails, haters. Live and let live."
    + " Some guys like blondes. Some guys like BBWs. I prefer ladies who have a darker skin tone. Don't ask" 
    + " me why. Don't know. Don't care. And it isn't just a phase. I haven't dated a white woman in 10"
    + " years. I'm not looking for anything serious (though I definitely want to keep the option open) nor" 
    + " am I interested in anything financial including a SD/SB arrangement."
    + " What I'm seeing is a situation where we get dinner sometimes, or a happy hour. We catch a movie," 
    + " hear some live music. And there is lots of naked time. We aren't up under each other all the time," 
    + " but we're friends, confidants and lovers. I'm college educated, been told I have a good sense of" 
    + " humor and can usually keep up on the conversation."
    + " Anyway, if that has a beat for you, drop me a note. Put 'Maybe I'm the one' in the subject and"
    + " lets's get coffee."
    + " Please make the subject of your reply something interesting so i know ur not a dbag or spam bot."
    + " Thanks.";

  json = new JSONObject();
  for (int i = 0; i < 30; i++) {
    String result[] = RiTa.splitSentences(entries[i]);
    String sent = result[int(random(0, result.length))];
    json.setInt("id", i);
    json.setString("sentence", sent);
    String add = str(i);
    saveJSONObject(json, "data/text" + add+ ".json");
  }
}

Scripting Illustrator with Basil.js:

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

var jsonString = b.loadString("text.json");
var jsonData;

//--------------------------------------------------------
function setup() {

  // Clear the document at the very start. 
  b.clear (b.doc());

  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Helvetica","Light"); 
  b.textAlign(Justification.LEFT_ALIGN); 
  b.text("Ideally we meeta few times xa week and have bottle rocket sex", 72,72,360,36);
  b.text("Katie Tender", 72,108,360,36);

  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);

  var titleX = 72; 
  var titleY = 72;
  var titleW = 72;
  var titleH = 72;

  var captionX = 72; 
  var captionY = b.height - 108;
  var captionW = b.width-144;
  var captionH = 36;

  var imageX = 72*1.5; 
  var imageY = 72; 
  var imageW = 72*4.5; 
  var imageH = 72*4.5; 

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

    b.noStroke();  // no border around image, please.
    var anImageFilename = "images/" + jsonData[i].image;
    var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
    anImage.fit(FitOptions.PROPORTIONALLY);

    b.noStroke(); 
    b.fill(b.random(180,220),b.random(180,220),b.random(180,220)); 
    b.ellipseMode(b.CORNER);
    b.ellipse (titleX,titleY,titleW,titleH);

    b.fill(255);
    b.textSize(56);
    b.textFont("Helvetica","Bold"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].title, titleX,titleY,titleW,titleH);

    b.fill(0);
    b.textSize(36);
    b.textFont("Helvetica","Regular"); 
    b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN );
    b.text(jsonData[i].caption, captionX,captionY,captionW,captionH);

  };
}

b.go(); 

kander – LookingOutwards06

For this Looking Outwards assignment, I selected Citation Needed (@needsref) by Allison Parrish, which is a bot that crawls through Wikipedia and tweets out sentences that are marked “citation needed.” Seeing a feed full of assertions that we would normally believe in the context of a full-length Wikipedia article, but we actually have no way of actually checking their accuracy, highlights the willingness with which we will accept information. There’s also a certain humorousness to seeing sentences completely out of context, as well as a more subtle humor to seeing sentences that are probably very subjective and up for debate being attempted to be passed off as fact, such as “England is considered the home of the game of football.”

Parrish works primarily in fields of text, and seems to be especially interested in poetry , and words and code.

tweet_1 tweet_2 tweet_3

 

 

Aliot-LookingOutwards06

Reverse OCR is a bot which chooses a word and draws randomly until the OCRad.js library recognizes it as that word. This bot is charming to me because the relationship between the “drawer” and the software “judge.” They are both humorously incompetent. On one hand the bot can’t seem to get the letters right at all and the judge, perhaps out of pity, just accepts the chicken scratch. This bot was created by Darius Kazemi AKA Tiny Subversions (the artist most known for buying random stuff on amazon). Kazemi works in Portland at Feel Train, a creative technology cooperative he founded. Basically this guy just makes a bunch of bots for the lolz.

screen-shot-2016-10-24-at-12-39-55-am screen-shot-2016-10-24-at-12-40-11-am screen-shot-2016-10-24-at-12-39-17-am

Aliot-LookingOutwards05

rachel-rossin-lossy-03 rossin_in6709_rosesinavase_2015_36x48_800pxRachel Rossin

http://rossin.co/

is a New York based painter and programmer. Her exhibit “Lossy” was a combination of oil and canvas (traditional painting techniques) and digital capture. Rossin would scan her surroundings/objects and upload the scans to unity to edit. She would then paint the resulting view which results in a fragmented and “lossy” representation of the original space. I love this work and I love her subject matter because I am exploring a very similar realm in my art. I am interested in memory and the lossy, fragmented attributes of that as well as in the relationship between the real, tangible world and the virtual. Her work spans a gap between the two in a very aesthetically pleasing way (even if it is very straightforward and simple).

 

takos-book

Final Product:

Attempts At Jokes Printed Version PDF
Link to Twitter bot version

14885807_1317431678280588_2032725612_n14877263_1317431571613932_1400395020_n
14914742_1317431461613943_1562010121_n14881410_1317431348280621_2078194419_o

Video of the professor flipping through my book:


Process:
My joke book is a one hundred and twenty five pages, with three hundred and sixty six jokes, with three jokes per page, an intro page – which gives a one sentence blurb on what the contents are, and a title page. Its printed on cream paper and machine bound. It is a collection of jokes based on tweets that where posted at around the same time and combined into a question and answer format to achieve a somewhat coherent, sometimes humorous joke.

For my generative book I wanted I wanted to do something with jokes. I wanted to follow a well known joke format so that they could be somewhat identifiable,the original thing that came to mind was “why did the chicken cross the road?” But I thought that was to specific so I generalized it to just a “why did what is and the idea that I thought up to do it was to combine different tweets into question and answer format. I then erased everything before the word “before” and before “why did” in the question and answer. I had a hard time getting the tweets and the author text to not overlap in the pdf, but overall i think it worked successfully, and I’m happy with the result of using the cream paper instead of white paper even though I’d originally had my doubts about it. I don’t like my joke separator choice of using “-” and think it would have looked better with a thinner version, or simply without one. I am happy with the results of my generator, and think that the twitter version allows me to edit the code to see possible outcomes in a more successful way, and look forward to developing it further.

(note: I didn’t include a picture from my sketchbook because i did not do any image based work.)

The first of my Processing programs uses the Temboo API to find tweets that contain the phrase “Why did”. The second code retrieves tweets with “Because”. The third takes the tweets and gathers the relevant data and also cleans up the text to isolate the relevant parts. The 3rd program below is my inDesign (Basil.js) script, which prints 3 jokes per page. My last program combines the first 3 codes and cleans them up differently in order to match up to the 140 character limit on twitter, and then uses Temboo to tweet the valid ‘jokes’ every time i run my program.

Code:
Processing:

This code retrieves tweets contain the phrase “why did”

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

// Create a session using your Temboo account application details
TembooSession session = new TembooSession("takos", "myFirstApp", REDACTED);
int x = 209;
void setup() {
  // Run the Tweets Choreo function
  runTweetsChoreo();
}

void runTweetsChoreo() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType("recent");

  // Set inputs
  String myQuery  = "\"why did\"";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken(REDACTED);
  tweetsChoreo.setConsumerKey(REDACTED);
  tweetsChoreo.setConsumerSecret(REDACTED);
  tweetsChoreo.setAccessTokenSecret(REDACTED);

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();
  
  // Print results
  println(tweetsResults.getLimit());
  println(tweetsResults.getRemaining());
  println(tweetsResults.getReset());
  //println(tweetsResults.getResponse());

  String[] result = {tweetsResults.getResponse()};
  //println(result);
  String name =  "..\\data\\wdt" + x + ".json";
  saveStrings(name,result);
}

This code retrieves tweets that contain the word “because”

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

// Create a session using your Temboo account application details
TembooSession session = new TembooSession("takos", "myFirstApp", REDACTED);
int x = 209;
void setup() {
  // Run the Tweets Choreo function
  runTweetsChoreo();
}

void runTweetsChoreo() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType("recent");
  // Set inputs
  String myQuery  = "\"because\"";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken(REDACTED);
  tweetsChoreo.setConsumerKey(REDACTED);
  tweetsChoreo.setConsumerSecret(REDACTED);
  tweetsChoreo.setAccessTokenSecret(REDACTED
  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();
  
  // Print results
  println(tweetsResults.getLimit());
  println(tweetsResults.getRemaining());
  println(tweetsResults.getReset());
  //println(tweetsResults.getResponse());

  String[] result = {tweetsResults.getResponse()};
  println(result);
  String name = "..\\data\\bc" + x + ".json";
  saveStrings(name,result);
  //saveStrings(myQuery+".json",result);
}

This code strips both tweets and saves the relevant text selection of “Why” & “Because” into a JSON file with other rewlevant data from the tweets, including Authors and time.

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;

JSONArray tweets;
int number = 201;

void setup() {
  String aJsonFilename = "wdt" + number + ".json";
  JSONObject json = loadJSONObject(aJsonFilename);
  
  String bJsonFilename = "bc" + number + ".json";
  JSONObject bjson = loadJSONObject(bJsonFilename);
  
  tweets = new JSONArray();
  if (json == null || bjson ==null) 
  {
    println("JSONObject could not be loaded!");
  }
  else 
  {
    JSONArray statuses = json.getJSONArray("statuses");
    JSONArray statusesb = bjson.getJSONArray("statuses");
    for (int i=0; i<statuses.size(); i++) { JSONObject aTweet = statuses.getJSONObject(i); String aText = aTweet.getString("text"); String aTime = aTweet.getString("created_at"); JSONObject aUser = aTweet.getJSONObject("user"); String aScreenName = aUser.getString("screen_name"); String aName = aUser.getString("name"); String aColor = aUser.getString("profile_background_color"); println(i + "\t" + aText); JSONObject tweet = new JSONObject(); tweet.setString("text", aText +" "); tweet.setString("name", aName); tweet.setString("color", aColor); tweet.setString("screenName",aScreenName); tweet.setString("date", aTime); //tweets.setJSONObject(i, tweet); JSONObject bTweet = statusesb.getJSONObject(i); String bText = bTweet.getString("text"); String bTime = bTweet.getString("created_at"); JSONObject bUser = bTweet.getJSONObject("user"); String bScreenName = bUser.getString("screen_name"); String bName = bUser.getString("name"); String bColor = bUser.getString("profile_background_color"); println(i + "\t" + bText); tweet.setString("textB", bText +" "); tweet.setString("nameB", bName); tweet.setString("colorB", bColor); tweet.setString("screenNameB",bScreenName); tweet.setString("dateB", bTime); tweet.setString("credits", "By " + aName + " and " + bName + "."); tweets.setJSONObject(i, tweet); tweet.setString("question", getQuestion(aText)+"?"); tweet.setString("answer",getAnswer(bText)); tweet.setString("by",aName + " & " + bName ); saveJSONArray(tweets, number + ".json"); } }} String getQuestion(String text) { int index = (text.toLowerCase()).indexOf("why did"); String cleared = text.substring(index); if((cleared.toLowerCase()).indexOf("http") > 0){
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("http")));}
  if((cleared.toLowerCase()).indexOf("?") > 0){
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));}
    
  return cleared; 
}

String getAnswer(String text)
{
  String lowercasedText = text.toLowerCase();
  int indexOfBecause = lowercasedText.indexOf("because");
  String textStartingFromBecause = text.substring(indexOfBecause);
  String textStartingFromBecauseLowercased = textStartingFromBecause.toLowerCase();
  if (textStartingFromBecauseLowercased.indexOf("http") > 0){
    return text.substring(indexOfBecause, (lowercasedText.indexOf("http")));
  }
  return text.substring(indexOfBecause); 
}
  

Indesign Code:

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

var jsonString = b.loadString("all-2.json");
var jsonData;

//--------------------------------------------------------
function setup() {
    
  b.clear (b.doc()); // Clear the document at the very start. 
  var pageNum = 0;    
  var pgW = 6*72;        
  var pgH= 9*72;

  var doc=b.doc();
  with(b.doc().documentPreferences) {
    pageWidth = pgW + "pt";
    pageHeight = pgH + "pt";
    b.width = pgW;
    b.height = pgH;
  }
    

  // Make a title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Helvetica","Light"); 
  b.textAlign(Justification.RIGHT_ALIGN ); 
  b.text("Attempts at Jokes", 72,72,360,36);
  b.textSize(12);
  b.text("Takos & Twitter", 72,108,360,36);
  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);

  // Initialize some variables for element placement positions.
  // Remember that the units are "points", 72 points = 1 inch.
  var titleX = 36; 
  var titleY = 76;
  var titleW = 72*5;
 
  var titleH = 24;

  // These are the header fields from the tweet CSV:
  // tweet_id,in_reply_to_status_id,in_reply_to_user_id,timestamp,source,text,
  // retweeted_status_id,retweeted_status_user_id,,retweeted_status_timestamp,
  // expanded_urls

  var textW=pgW*0.75;
  var textOffs=(pgW-textW)/2;
  var textOffsY=72;
  var x=textOffs;
  var y=pgH*0.25;
    
  var textSize1=8;
  var textSize2=30;
  var tweetDist=textSize2*2;
    
  var whiteSpaceStr=" ";

  var elements=[];
    
  var doNewPage=false;
  var w = 0;
  // Loop over every element of the book content array
  for (var i = 0; i <jsonData.length; i+=3){ // 10; i+=3){ //jsonData.length; i+=3) { // Create the next page. b.addPage(); b.noStroke(); // no border around image, please. b.fill(0,0,0); var textY = titleY; var fontA = "Itallic"; var fontB = "Regular"; var size1 = 14; var size2 = 8; var font = "Helvetica"; pageNum = pageNum +1; b.textSize(size1); b.textAlign(Justification.CENTER_ALIGN); b.textFont(font,fontB); b.text("-", 216-20, 228, 40, 40); b.text("-", 216-20, 420, 40, 40); // b.textSize(size2); // b.text(pageNum, 216-20, 612, 40, 40); b.textSize(size1); w = 0; if (jsonData[i].question.length > 57){
        w = 15;
    }
    if (jsonData[i].question.length > 114){
        w = 30;
    }  
    b.color(0,0,0);    
    b.textFont(font,fontB); 
    b.textAlign(Justification.LEFT_ALIGN );
    b.text(jsonData[i].question, titleX,textY,titleW,titleH*3); textY+=titleH;//+15; 
      
    b.color(80,80,80);    
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i].name,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;
    
      
          
    w =0; 
      
    if (jsonData[i].answer.length > 57){
        w = 15;
    }
    if (jsonData[i].answer.length > 114){
        w = 30;
    }  
        
    b.color(0,0,0);      
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.RIGHT_ALIGN );
    b.text(jsonData[i].answer, titleX,textY,titleW,titleH*3); textY+=titleH;//+15;
    b.color(80,80,80);
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i].nameB,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5; 
   
      
    w = 0;
    if (jsonData[i+1].question.length > 57){
        w = 15;
    }
    if (jsonData[i+1].question.length > 114){
        w = 30;
    }    
    b.color(0,0,0);
    textY = (pgH-2*titleY)*(1/3)+titleY*1.5;
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.LEFT_ALIGN );
    b.text(jsonData[i+1].question, titleX,textY,titleW,titleH*3); textY+=titleH;//+15; 
    b.color(80,80,80);      
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+1].name,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2;
    
      
    w =0; 
      
    if (jsonData[i+1].answer.length > 57){
        w = 15;
    }
    if (jsonData[i+1].answer.length > 114){
        w = 30;
    }  
      
    b.color(0,0,0);      
    b.textSize(size1);
    b.textFont(font,fontB);
    b.textAlign(Justification.RIGHT_ALIGN );
    b.text(jsonData[i+1].answer, titleX,textY,titleW,titleH*3); textY+=titleH;//+15;
    b.color(80,80,80);
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+1].nameB,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2; 
    w = 0;
    if (jsonData[i+2].question.length > 57){
        w = 15;
    }
    if (jsonData[i+2].question.length > 114){
        w = 30;
    }
    b.color(0,0,0);      
    textY = (pgH-2*titleY)*(2/3)+titleY*2;
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.LEFT_ALIGN );
    b.text(jsonData[i+2].question, titleX,textY,titleW,titleH*3); textY+=titleH;//+15; 
    b.color(80,80,80);      
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+2].name,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2;
    
    w =0; 
      
    if (jsonData[i+2].answer.length > 57){
        w = 15;
    }
    if (jsonData[i+2].answer.length > 114){
        w = 30;
    }
    b.color(0,0,0);      
    b.textSize(size1);
    b.textFont(font,fontB); 
    b.textAlign(Justification.RIGHT_ALIGN );      
    b.text(jsonData[i+2].answer, titleX,textY,titleW,titleH*3); textY+=titleH;//+15;
    b.color(80,80,80);
    b.textSize(size2);
    b.textFont(font,fontA); 
    b.text(jsonData[i+2].nameB,       titleX,textY+w,titleW,titleH); textY+=titleH*1.5;//2; 
 

  };
}

// This makes it all happen:
b.go(); 

Code For Twitter Bot

import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;
import com.temboo.Library.Twitter.Tweets.*;
TembooSession session = new TembooSession("takos", "myFirstApp", "");

JSONArray tweets;
String finAnswer;
String finQuestion;
String finCredits;
String mode = "recent";

void whyDid() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType(mode);

  // Set inputs
  String myQuery = "\"why did\"";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken("-");
  tweetsChoreo.setConsumerKey("");
  tweetsChoreo.setConsumerSecret("");
  tweetsChoreo.setAccessTokenSecret("");

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();

  String[] result = {tweetsResults.getResponse()};
  String name = "wdt.json";
  saveStrings(name, result);
}

void because() {
  // Create the Choreo object using your Temboo session
  Tweets tweetsChoreo = new Tweets(session);
  tweetsChoreo.setResultType(mode);
  // Set inputs
  String myQuery = "because";
  tweetsChoreo.setQuery(myQuery);
  tweetsChoreo.setAccessToken();
  tweetsChoreo.setConsumerKey();
  tweetsChoreo.setConsumerSecret();
  tweetsChoreo.setAccessTokenSecret();

  // Run the Choreo and store the results
  TweetsResultSet tweetsResults = tweetsChoreo.run();

  String[] result = {tweetsResults.getResponse()};
  String name = "bc.json";
  saveStrings(name, result);
}

void tweetIt() {
  StatusesUpdate statusesUpdateChoreo = new StatusesUpdate(session);
  String currentTweet = finQuestion + "\n" + "\n" + finAnswer + "\n" + "\n" + finCredits;

  if (currentTweet.length() <
  = 140) {
    statusesUpdateChoreo.setStatusUpdate(currentTweet);
    statusesUpdateChoreo.setAccessToken();
    statusesUpdateChoreo.setConsumerKey();
    statusesUpdateChoreo.setConsumerSecret();
    statusesUpdateChoreo.setAccessTokenSecret();

    StatusesUpdateResultSet statusesUpdateResults = statusesUpdateChoreo.run();
    //println(statusesUpdateResults.getResponse());'
  } else
  { 
    println(currentTweet);
  }
}

void setup() {

  whyDid();
  because();
  String aJsonFilename = "wdt.json";
  JSONObject json = loadJSONObject(aJsonFilename);

  String bJsonFilename = "bc.json";
  JSONObject bjson = loadJSONObject(bJsonFilename);

  tweets = new JSONArray();
  if (json == null || bjson ==null)
  {
    println("JSONObject could not be loaded!");
  } else
  {
    JSONArray statuses = json.getJSONArray("statuses");
    JSONArray statusesb = bjson.getJSONArray("statuses");
    for (int i=0; i< statuses.size(); i++) { 
      JSONObject aTweet = statuses.getJSONObject(i); 
      String aText = aTweet.getString("text"); 
      String aTime = aTweet.getString("created_at"); 
      JSONObject aUser = aTweet.getJSONObject("user"); 
      String aScreenName = aUser.getString("screen_name"); 
      String aName = aUser.getString("name"); 
      JSONObject bTweet = statusesb.getJSONObject(i); 
      String bText = bTweet.getString("text"); 
      String bTime = bTweet.getString("created_at"); 
      JSONObject bUser = bTweet.getJSONObject("user"); 
      String bScreenName = bUser.getString("screen_name"); 
      String bName = bUser.getString("name"); 
      finAnswer=getAnswer(bText); 
      finQuestion =getQuestion(aText); 
      finCredits = aScreenName + "&" + bScreenName; 
      Cleanup(); 
      tweetIt();
    }
  }
} 
void Cleanup() { 
  finAnswer=endsInAt(clearAmp(finAnswer)); 
  finQuestion =endsInAt(clearAmp(finQuestion)) + "?"; 
  finCredits = clearQ(finCredits);
} 
String getQuestion(String text) { 
  int index = (text.toLowerCase()).indexOf("why did"); 
  String cleared = text.substring(index); 
  if ((cleared.toLowerCase()).indexOf("http") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("http")));
  }
  if ((cleared.toLowerCase()).indexOf("?") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }
  if ((cleared.toLowerCase()).indexOf("\n") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }
  return cleared;
}

String getAnswer(String text)
{
  int index = (text.toLowerCase()).indexOf("because");
  String cleared = text.substring(index);
  if ((cleared.toLowerCase()).indexOf("http") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("http")));
  }
  if ((cleared.toLowerCase()).indexOf("...") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("...")));
  }
  if ((cleared.toLowerCase()).indexOf("\n") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }
  if ((cleared.toLowerCase()).indexOf("!") > 
  0) {
    cleared = cleared.substring(0, ((cleared.toLowerCase()).indexOf("?")));
  }

  return cleared;
}

String clearAmp(String input) {
  int index = input.indexOf("&");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+5, input.length());
    index = input.indexOf("&");
  }
  index = input.indexOf(">");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+4, input.length());
    index = input.indexOf(">");
  }
  return input;
}

String endsInAt(String input) {
  int index = input.indexOf("@");
  if (index>
  -1 &&
  (input.substring(index, input.length())).indexOf(" ") > -1) {
    input = input.substring(0, index);
  }
  return input;
}

String clearSpace(String input) {
  int index = input.indexOf(" ");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+2, input.length());
    index = input.indexOf(" ");
  }
  return input;
}

String clearQ(String input) {
  int index = input.indexOf("?");
  while (index != -1) {
    input = input.substring(0, index) + input.substring(index+2, input.length());
    index = input.indexOf("?");
  }
  return input;
}

Written by Comments Off on takos-book Posted in Book

Lumar-BookOfKindness

BOOK DESCRIPTION: “Something Kind” by Lumar is a book that is computationally generated, leveraging human computation through Amazon Mechanical Turk, several short programs in Processing, and Basil.js.

The text of the book consists of responses provided by Amazon Mechanical Turk workers, when they were asked to “write about something unexpectedly nice that someone did” for them. The program incorporates the RiTa library for content analysis, and singles out the pertinent action verbs of each quote to display on the facing page spread. Altogether the book emphasizes kindness as an action – and ends the book with the actions “GO & DO” with some gradual play on flipbook transitions through both the heart imagery and verb spread/sizing.

bookkind

_dsc2169-copy

_dsc2170-copy

_dsc2171-copy

The action verbs on the page spreads all eventually get redder, smaller and darker the closer the book gets to the end with the last verb page being “GO & DO” in smaller font at the bottom of the page.

screen-shot-2016-10-28-at-4-53-15-pm

I ended the book with a screenshot of an email a Turker sent me thanking me for the HIT (task)! Wow! I was touched! I’m really glad she liked it!screen-shot-2016-10-28-at-4-53-25-pm

 

REGRETS: Besides not being able to execute some parts of the original idea (the original sequence held more of a sentiment of a personalized gift of kind moments book) I had really wanted the extracted verbs to be written in a different color directly within the quote. I had gotten the program to send to the json file the index values of the words I wanted highlighted within the quote if it was a word array, but having the color be unique for a single or couple index values of a string in Basil was difficult. I had not managed to figure it out.

An idea I had before the book of kindness:

scan-6

Physical book of kindness brainstorms:

scan scan-1

I had really wanted the design of the pages to be cognizant of the fact that as a physical book, it had depth and extra dimensions to play with. So while the hearts on each page look as if they are traveling bit by bit to be more fully on the page, the edge it leaves helps form a half heart on the side of the book – which helps form an entire heart when you look at the book from an angle.

Metaphorically, I like the aspect of a heart emerging the deeper into the book you are.

Maeda media was an inspiration! Thank you Golan for the recommendation!

Originally I really wanted the the flipbook heart feature to have more than just a moving heart, but in fact a question mark on the outside side edge of the book – as if the title “Book of kindness” or “Something Kind” ended in a “?” as it wrapped around the corner. The “?” would be lerped gradually into a exclamation mark that would end the book be reaffirming that kindness is an action!!! Exclamation! No more “?”.

 

Idea:

Enter a time (perhaps someone’s birthday, a time you are feeling really down, the date of someone’s death, etc)

Through Temboo – program runs through twitter to finds  365 tweets that have the word “hate” (or “sad” /”lonely”?) in it posted at that time.

It then sends a mechanical turk request for human computation to spurt out 365 examples of either—–

1. An unexpected act of kindness

Take a moment to remember something nice that someone did for you recently, that you weren’t expecting. In a single sentence or two, please describe what happened, and how it made you feel.

Here are a couple examples of what your sentence might look like:

  • One of my roommates cleaned up the fridge in our apartment.
  • I was running late and in a big hurry. A man let me cut in front of him in the line at Starbucks.

//3. A specific little detail of life that makes them smile or laugh

4. A link to a post, picture, quote, gif, video, etc that inspires, uplifts, makes them smile, etc (the program will take that link and generate a visual qr code to include)

Given this, the hate quote is printed really small in black and white on one side of a two page spread, and the happier result on the other side at the edge of a page in a lerping gradient color from the rainbow in relation to the page’s place in the book.

The height position of the happy quote is determined by the page’s position within the book in relation to an arc formula – that way when you flip through the book quickly, the flip book animation is that of a rainbow arching.

or like maeda @ media – the quotes printed at the edges creates a word when you see the book from the side – a sketchbook for somebody

The words/sentences are then processed by RITA to be ordered to rhyme

The VERBS are then processed by RITA and the typography is different to emphasis ACTIOOOONNN


PROCESS/what actually happened:

  1. Index.html Page request form  Wasn’t able to get automated external hit request to amazon to work – something about my AWS authentication key not working? So I ended up just manually submitting requests through the gui request maker amazon provides instead.
  2. External Hit Initialization (use Boto SDK to access Amazon Web Services API)   Tried Boto, AWS authentication key wrong, and just a lot of plumbing issues that didn’t necesarily have to do with art or code…and trying out a different SDK like mturk.js was not something I could figure out/learn quickly enough before the free book deadline. Ah…the joys of plumbing.
  3. Illustration generator see below
  4. Verb identifier & selector see below
  5. JSON Content file generator see below
  6. Basil.js script see below
  7. was on a flight – didn’t check email about extension I learned to bind books by hand!!! It was fun – if incredibly time consuming. My book binding process is below

screen-shot-2016-10-23-at-9-06-17-pm

Wrote a processing program to create the flipbook heart illustrations that go on each page by exporting each frame.heartpages

And then in Photoshop, I took all the pages and created an action series (1. reflect over the vertical axis, 2. save, 3. close file) and was able to automate the batch. All 53 illustrations were flipped horizontally in 3.256 seconds :). LOL….but it took a good chunk of time to figure it all out…../fix issues

hearts

 

Wrote an additional processing program that ran through all the quotes from amazon mechanical turk workers leveraging the RiTa library to identify and single out the verbs in the strings. All the verbs are subsequently gathered into an array – wherein I manually chose which verbs I wanted to include. This list of selected verbs is then used in the next program.

Wrote another processing program to create the json file of all the content each page of the book should have. It took the collection of strings from the csv doc of the turker results, the turker’s ID, a corresponding image file, and an array of all the highlighted verbs within the quote.

It generates differently each time (almost – the string data from the turkers stay consistent because I didn’t pay for more….)

See pdf:

bookofkindness

bookofkindness2

somethingkindagain

Flipping through the pages —  the heart translates closer and closer to the center as the book progresses, this allows the edge to create the heart shape on the side.

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


 

Hand bound the book by organizing the 112 pages into smaller booklets called ‘signatures’ of 4 large sheets printed double sided with spreads – which translated to 16 pages per pamphlet which were then sewn together.

scan-4img_1827

 

screen-shot-2016-10-28-at-2-37-06-am 7 different pdfs for the 7 different signatures needed

The wonkiness of the printing was actually not a computational error on my part, but the side effect of an act of kindness I received!

I had really loved the paper luster of the 80 lb, but my code wasn’t made for that (the measurements of the heart illustration and the placements weren’t adjusted for the widths of the 80 lb papers adding up, I had 50 lb in mind). Fedex only had 32lb and 80 lb. But the 32 lb paper didn’t feel nearly as good. So I had sighed, and grudgingly still went with the 32 lb – it would be narrower than I would’ve liked for the heart’s side edge, but would suffice. Especially since 80 lb printing would cost me $144.

But when I had picked up the order, the fedex guy said he printed it all out on 80 lb paper for me instead, and that he didn’t charge me the extra $70.

….DDISSSAAASSSSTTTEEER!!! HOW COULD I SAY NO?! I couldn’t just ask him to redo 112 pages of $144 printing!

I also thought it added to the meaning of the book – this book was made with the help of an act of kindness.
Here’s the heart generator code:

import processing.pdf.*;
boolean bRecordingPDF;
int pdfOutputCount = 0; 

int numofpg = 53;
int whatpg =0;
float r;
float g;
float b;
float rr;
float gg;
float bb;
float rdiff;
float gdiff;
float bdiff;
void setup(){
  bRecordingPDF = true;
  frameRate(5);
  size(116, 159);
  r = random(100,255);
  g = random(100,235);
  b = random(100,235);
  rr = 255;
  gg = 120;
  bb = 120;
  println("("+r+","+g+","+b+")");
}
void draw(){
  background(255);
    if (bRecordingPDF) {
      if(whatpg<=numofpg){
     beginRecord(PDF, "heart" + pdfOutputCount + ".pdf");
    translate(width-whatpg,height/2.4);
    
    
    noStroke();
    float red = map(whatpg,0,numofpg,r,rr);
    float green = map(whatpg,0,numofpg,g,gg);
    float blue = map(whatpg,0,numofpg,b,bb);
    fill(red,green,blue);
    PShape heart = createShape();
    heart.beginShape();
      for (int i=0; i<50; i++){
        float t = i*2*PI/50;
        float x = 3.3*(16*pow(sin(t),3));
        float y = -3.3*(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t));
        heart.vertex(x,y);
      }
    heart.endShape();
    shape(heart, 0, 0);
    whatpg++;
  }
    endRecord();
    bRecordingPDF = false;
    pdfOutputCount++;
  }
}
void keyPressed() {
  // When you press a key, it will initiate a PDF export
  bRecordingPDF = true;
}

Here is the code that identifies the verbs!

 
import rita.*;
String[] kindverbs = {"hired","save","accept","asked","volunteering","make","made","makes","cursed","thanked","waiting","bought","shocked","given","ask","moved","paying","pray","surprised","helped","pay","offered","give","remember","remembering","gave","inspired","improve","paid","thank","help","making","enjoy","blessed","appreciated","cheered","touching","vouched","listening","cared","touched","wrote","promised","served","found","understood","understand","assured","strive"};
JSONArray[] highlight = new JSONArray[50];
String[] rawKindness = {
   "A coworker hired me for extra work, because she knew I was trying to save up money. At first I was hesitant to accept because she asked me before she asked her assistant, but it was okay with the assistant. I was grateful that she thought of me for the task, which was something I would have been happy volunteering to do.",
 "My neighbor stopped by my house and brought me a cinnamon broom. Those are those little straw brooms that you see in the stores in the fall that make the house smell really nice. It was so sweet of her and so unexpected! It made me very happy and it makes me happy every day when I see it.",
"I was in a hurry to get to the train station and cursed out a woman under my breath as I passed her.  Then, my wallet fell out of my pocket and onto the street.  She picked it up and handed it to me.  I thanked her sincerely and felt like a huge heel!",
"I was waiting on my friend to get back from doing something while in a Skype call with her.  We were about to play a game, however, she had told me to wait.  As soon as she got back, I had gotten an in-game notice stating she bought me an in-game item I have been wanting for quite some time, I was really shocked as she had already given me a couple other things prior to this so it was very unexpected and a huge surprise to me since I didn't ask for it.",
 "I was a single, 18 year old mother with my 1 year old daughter. I had just moved into my own place that was across the street from Walgreens. We walked together to grab some bread and applesauce. I was so embarrassed to be paying in change, and it was obvious the cashier was less than thrilled. As we were walking back across the street and go into the apartment complex a truck pulled up along side me. It was a husband and wife who told me I shouldn't be embarrassed and to be proud of how hard I was working. I was already in tears, and then they handed me a $20 bill, and that they'd pray for me. ",
 "This was over 10 years ago and I've never forgotten that moment. Still makes me tear up :)",
 "I was 7 months pregnant with my first child. My husband and I were visiting my parents and siblings who lived 9 hours away. I thought we were just picking up a crib my parents had bought us. Upon entering my parents house, we were surprised to see a bunch of close family and friends. It was a surprise baby shower for us! We were so happy to see everyone and receive so many, many gifts.",
 "I was in a very desperate financial situation and my best friend knew about it. So he knocked on my door and handed me $20. I was so, so thankful and grateful for that act, because I was able to buy food. I love him.",
 "I had a tire blow out on a rainy night while going off an off-ramp in the middle of nowhere.  A cop pulled over to see if I was alright, and let me borrow his umbrella.  He stayed there until I got my tire changed to make sure I was safe.",
 "My Mom recently made my husband and I sauerkraut with fresh rice (one of my favorite foods of hers). I was surprised and thrilled that she had made it for us - and also relieved, because we don't know how to cook that much nor have much money, and needed some healthy food desperately.",
 "My mother made me food when I was sick. She also helped me pay a bill to. It made me feel great and relived. It was a surprise.",
 "I stopped at a gas station last week because my car had ran out of gas. When I got to the station I realized I didn't have my wallet. Frantically looking around my car a nice woman asked if something was wrong to which I explained to her that I left my wallet. She offered to give me 5$ for gas to get on my way.",
 "I was at the grocery store just trying to buy a few things for dinner that night. Nothing real expensive, about $15-$20 worth of stuff. My card got declined. I had no other payment. I was going to leave the stuff there when the person in line behind me offered to just pay for it. I was really grateful and also super shocked because you don't see that kind of thing anymore.",
 "My car had broken down taking my kids to school. Another mother drove by and offered to take my kids to school. Then she was kind enough to return, picked me up and drove me home.",
 "I am usually the one to make sure the laundry is done for everyone in the house. For a few weeks I had been working really hard, trying to get in a lot of overtime, and in between working from home one evening I put a load of clothes in the washer. I completely forgot about them and before I fell into bed I remember the clothes. I rushed out to the washer to put the clothes in the dryer and saw my son had not only tried the clothes, but had folded them. I got tears in my eyes for how nice that felt.",
 "To be honest I had hard times remembering anything.. A couple weeks ago I was at the groceries store, an old man and I going to the cashier, he was in front of me: he picked the left line and I continued to the right. His line was going to be closed with the client before him so he moved to my line. I told him to pass in front of me and he replied he wasn't in a hurry because he was retired.",
 "One nice thing that someone did for me recently that I wasn't expecting was when my dad went to Trader Joe's and bought healthy food for me after I had been feeling unwell. He usually shops at a different grocery store , but he did it because he knew that it would be food that I would like.",
 "I was in the office before lunchtime somene gave me a candy, . It was super unexpected, I felt super good. I really I needed",
 "My sister surprised me by leaving fruit snacks at my front door the other day. I love fruit snacks. It was a small thing, but made my day.",
 "I was given a really nice compliment at work.  I was told I inspired someone to improve at their job and she got a promotion because of it.  It was incredibly heartwarming.",
 "I was in the drive-thru to pick up my starbucks for my office.  As I pulled up to the window, the barista told me that my order had been paid for by the car in front of me.  I was extremely grateful for the kindness and paid it forward when I could.",
 "It was late at night, and I had just gotten out of the gym. I walked to the parking lot, got in my car, and tried to start it, and the battery was dead. A fellow gym-goer came out shortly after, and I guess he heard me repeatedly try and start my car, which was unsuccessful. He walked up to my window and asked me if I needed a jump - and I said absolutely, yes, thank you! He pulled his car around, we got the batteries connected, and 30 seconds later I was on my way. What a life-saver! I was relieved to not have to waste money calling roadside assistance, and was quite frankly seriously positively surprised that someone would go out of their way to help me. It was very heartening, and I now make it a point to 'pay it forward' as far as these sorts of little kindnesses go.",
 "I was really tired and we were out of coffee. I had to go to a 7 hr online trainig class and didnt know how I'd make it through. My husband shows up with coffee for me, I was so happy and grateful.",
 "Our HR Manager was making cheese, egg and ham biscuits for everyone to enjoy one morning. I am a vegetarian and the ham was mixed in so I would not have been able to partake of the goodies. However she surprised me by making 4 separate ham  free biscuits just for me.  It was a very nice and welcome treat!",
 "My buddy let me sleep on his couch for a week.  I really needed a friend right then.  Felt great to have one pull through for me.",
 "I recently was cleaning my home and wanted to get rid of a lot of items around the house. I started early in the morning and was extremely surprised at how well the house was looking, for the first time in over a year the clutter was gone. But I noticed  I had lots of stuff out by the curb and wondering now if the city will pick it up.",
 "To my surprise my neighbor who has a truck  came over and loaded all the garbage on his truck and took it to the dump.",
 "I was so relieved that it was all gone. I felt so blessed to have such a nice neighbor.",
"A young lady offered me her seat on the subway.  Now, at 63 I'm not that old but I do have gray hair.  I appreciated her offer though I did not take her up on it - what a kind young lady.",
"It was surprising group kindness.  I went to a dance class tonight and the cirque class was doing dive cart wheels.  I thought I could do them and asked to join in.  The teacher let me.  I was the last one and the others cheered really loud when I did it.  Normally, we don't cheer in class, so it was a big surprise!  I'm almost 3 times older than them...I guess that might be the reason....but am not sure.",
"I had had a really bad day and a friend that I had been texting about stuff I had been going through was there for me. She gave up the rest of her afternoon at work and showed up at my apartment and took me to lunch and then we just sat and talked. Totally unexpected. Glad she gave me time.",
 "I had taken the day off work because I was very tired. My mom stopped by just to bring me a can of coke which she knows is my favorite drink. I felt like someone cares about me.",
"I had a fight with my boyfriend and was walking around our neighborhood which was in a downtown area. A homeless person bought me a beer and talked about angels and God. It was a really touching moment. I felt as though it was a message from above even if that sounds ridiculous.",
"I was in between jobs and really eager to get my foot in my career. I had posted an article on Facebook about the type of career I wanted, nothing too long and nothing like rant. A friend read it and messaged me right away, telling me she had a cousin who was in that field. Without me being able to reply, I got a text message from the cousin saying how my friend had vouched for me and if I would be available for an interview. Fast forward to now and I am loving my job! It was such a helpful extended act of kindness that changed my life.",
"My husband made the bed yesterday. I know it sounds trivial but it was a really nice surprise to walk in on. I hate making beds and I also hate sleeping in unmade beds so it meant a lot to me. It made me feel special and appreciated.",
"My dog was sick and I had just brought my favorite goat buck in for a second surgery. I was stressed out and upset because I have no other family than my animals. I was telling all this to a cashier at an organic grocer store I shop at. She an I have been friendly for over a year. After listening to me she said it hurt her to see me so sad and she asked me if she could give me a hug. I haven't had a hug from anyone in years so it made me feel like I was cared about by someone in a genuine way. I felt touched and sad at the same time, realizing just how long it had been since anyone showed me a tender act of care.",
"I was recently on the side of the road with a flat tire and a sweet boy stopped and changed my tire for me. I felt really thankful and also know there is still some good in this world.",
"A person who I didn't expect to show sympathy, actually was very upset when I told him about my mother's recent death - he even wrote a note.",
"I was out to dinner at Outback Steakhouse with my family (5 of us). My 3 young kids were being somewhat challenging, and I was a little stressed and not really enjoying my meal.  When we were done, the waiter came to us and said someone at the bar had paid for our meal, tip and everything. We immediately looked up, and no one was there. I was so in shock, confused, and happy....it really made my day.  I posted about it on Facebook and thanked the stranger who paid for our meal, and promised to pay it forward. I received a lot of positive feedback and comments from people.",
"My female cousin once unexpectedly baked a cake and served me a piece. I felt this was quite unusual since she's normally lazy and never does things like this (for anyone), although the inverse is not quite the same. I enjoyed it and found it unusual (the gesture), but coined it as a random act of kindness from her.",
"I was trying to buy a house and someone offered me the money I needed to put down on the house. I was over joyed and shocked that someone would help me in that.",
"I was in a video store (ok not extremely recently but it is what came to mind) and talking to my daughter about what we could get because I had rent due and was really short on money and it was just a bad time in general.  The next thing I knew a somewhat scruffy-looking guy came up and handed me a five dollar bill and kept walking.  I just stood there in shock for a little bit.  I was a little embarrassed, but is was such a nice thing for somebody to have done.",
"The grocery store where I shop regularly was crowded. I had other important work to do and was short on time. The helper at the grocery store understood my predicament  and helped me pay my grocery bill by opening a new counter. He really made my day!",
"One day, I happened to be in a town quite unfamiliar to me. The people there spoke a different language which I could not understand well. Similarly, they could not understand what I was telling them. I wanted to go to a hotel run by somebody who speaks my language. I knew only my mother tongue besides English. There gathered a number of taxi drivers  around me to take advantage of my miserable situation. Seeing the pathetic condition I was in, a young man stepped forward and asked what the matter was. He spoke in good English. I felt very much relieved and happy. I explained my situation and requested him to help. First he assured me that he was not a con and showed me a chain around his neck to prove that he was a devotee of one of the local Gods. He then called one of the taxi drivers and gave instructions as to where to take me. The taxi took me to a hotel whose owner spoke the same language as mine. I felt greatly grateful to the person who helped me in this difficult situation.",
"A man let me go in front of him at the grocery store when I only had a few items and he had a cart full.  I was very relieved because I had a rowdy toddler with me.  It was very nice of him and I was very appreciative.",
"I was on my way to one of my hardest midterms last year, physics. Unexpectedly, a girl was handing out packets containing snacks and other various test supplies out on campus, which was a really nice.",
"A man I had just met, changed the flat tube in my tire five minutes before the mountain bike race that we were both competing in. He did this without hesitation. Amazing.",
"I just did some groceries. I got some extra snacks and drinks. I saw a homeless senior citizen resting on a bench, and decided on a whim to share my snacks and drinks.",
"I was having a really bad day out of nowhere. My mom showed up to my apartment and brought flowers. She does not even live in the same town as me. It was very thoughtful of her. I felt grateful for her.",
"I always strive for a clean house but rarely get it. Not only did I walk into a clean house after work on my Birthday, but my man also got me the most beautiful flowers! I always want nothing but flowers for my day and this time he came through and then some! It was awesome and made me feel good.",
};
JSONArray values;
void setup(){
  values = new JSONArray();
  for(int i=0;i<rawKindness.length;i++){//go through the entire array of quotes to get to each quote
    String[] quote = RiTa.tokenize(rawKindness[i]);//make each quote into an array of words
    
    for(int j=0; j<quote.length; j++){//run through each word in the quote
      for(int k=0;k<kindverbs.length;k++){//test to see if it matches any of the desired verbs
        String quotedword = quote[j];
        String kindword = kindverbs[k];
        if(quotedword.equals(kindword)){
          //save the index values of the matching words within the quote
        }
       
      //add those index values into an array that gets out into the JSONarray of arrays
      }
    }
  }
}

 


JSON outputter:

 
import rita.*;
String[] images = new String[50];
String[] imageAgain = new String[50];
String[] id = {"A14OO6SSSYQQBL","A17LIM50IMQN7Y","A1CE2XPYCDRHVZ","A1EG66WQGRUPCS","A1GKTC682SWY23","A1L2T6S3WLN692","A1N91AUYC0H7S4","A1NKBXOTZAI1YK","A1O67YS3DU0ZHX","A1P1UEH9PMSHXG","A2JR1GWH41MLRB","A2LEY4X3LD8G26","A2M4YIC3XU2693","A2O2Q0LVWOWJ1G","A2VRDE2FHCBMF8","A2ZE2IPHK152KA","A30QV5VTBRGYTA","A31681CCEVDIH3","A38TZK07ON180S","A3NS1DN6J7Z3EU","AG5X553DRP557","AI4LKMF5QD07D","AITY0PTUTW9HW","AMPMTF5IAAMK8","AW5O1RK3W60FC","A2W7Z10Z5YLLF4","A3A8P4UR9A0DWQ","A2FBJV1VAI9SQ6","A2S3BJYB8MXB9A","A31LK6KUPJHPSP","A2L5GOTR5Y42X1","A1U6A617Q95MTZ","A1FFPO2ZR40NCD","A1MJYGGQ9RQUKX","A3CS3BQE8T1BU0","A3S0INU046UTYK","A2P6UJ9BKRPGUS","A03922113RU44GENR8ATX","A1RSAU5I9NZTOP","A27IUQDZPHETTP","A2A5YQTRSCLO32","A1UJFULCCBBIJI","A3JMJIKX36UNC7","A28JGET2XTPRG5","A2YRYS17DU3A8R","A17X4M2GVN70VB","AX0KHOWMMM6Z2","A10PVSMU1UBP8N","A116GKYM2O195O","A2DAZ0CJ3VKS80"};
//this is the array of arrays of indexvalues of indivudal verbs in each quote that need to change color
JSONArray[] highlightedIndices = new JSONArray[50];
String[] kindverbs = {"hired","save","accept","asked","volunteering","make","made","makes","cursed","thanked","waiting","bought","shocked","given","ask","moved","paying","pray","surprised","helped","pay","offered","give","remember","remembering","gave","inspired","improve","paid","thank","help","making","enjoy","blessed","appreciated","cheered","touching","vouched","listening","cared","touched","wrote","promised","served","found","understood","understand","assured","strive"};
String[] rawKindness = {
   "A coworker hired me for extra work, because she knew I was trying to save up money. At first I was hesitant to accept because she asked me before she asked her assistant, but it was okay with the assistant. I was grateful that she thought of me for the task, which was something I would have been happy volunteering to do.",
 "My neighbor stopped by my house and brought me a cinnamon broom. Those are those little straw brooms that you see in the stores in the fall that make the house smell really nice. It was so sweet of her and so unexpected! It made me very happy and it makes me happy every day when I see it.",
"I was in a hurry to get to the train station and cursed out a woman under my breath as I passed her.  Then, my wallet fell out of my pocket and onto the street.  She picked it up and handed it to me.  I thanked her sincerely and felt like a huge heel!",
"I was waiting on my friend to get back from doing something while in a Skype call with her.  We were about to play a game, however, she had told me to wait.  As soon as she got back, I had gotten an in-game notice stating she bought me an in-game item I have been wanting for quite some time, I was really shocked as she had already given me a couple other things prior to this so it was very unexpected and a huge surprise to me since I didn't ask for it.",
 "I was a single, 18 year old mother with my 1 year old daughter. I had just moved into my own place that was across the street from Walgreens. We walked together to grab some bread and applesauce. I was so embarrassed to be paying in change, and it was obvious the cashier was less than thrilled. As we were walking back across the street and go into the apartment complex a truck pulled up along side me. It was a husband and wife who told me I shouldn't be embarrassed and to be proud of how hard I was working. I was already in tears, and then they handed me a $20 bill, and that they'd pray for me. ",
 "This was over 10 years ago and I've never forgotten that moment. Still makes me tear up :)",
 "I was 7 months pregnant with my first child. My husband and I were visiting my parents and siblings who lived 9 hours away. I thought we were just picking up a crib my parents had bought us. Upon entering my parents house, we were surprised to see a bunch of close family and friends. It was a surprise baby shower for us! We were so happy to see everyone and receive so many, many gifts.",
 "I was in a very desperate financial situation and my best friend knew about it. So he knocked on my door and handed me $20. I was so, so thankful and grateful for that act, because I was able to buy food. I love him.",
 "I had a tire blow out on a rainy night while going off an off-ramp in the middle of nowhere.  A cop pulled over to see if I was alright, and let me borrow his umbrella.  He stayed there until I got my tire changed to make sure I was safe.",
 "My Mom recently made my husband and I sauerkraut with fresh rice (one of my favorite foods of hers). I was surprised and thrilled that she had made it for us - and also relieved, because we don't know how to cook that much nor have much money, and needed some healthy food desperately.",
 "My mother made me food when I was sick. She also helped me pay a bill to. It made me feel great and relived. It was a surprise.",
 "I stopped at a gas station last week because my car had ran out of gas. When I got to the station I realized I didn't have my wallet. Frantically looking around my car a nice woman asked if something was wrong to which I explained to her that I left my wallet. She offered to give me 5$ for gas to get on my way.",
 "I was at the grocery store just trying to buy a few things for dinner that night. Nothing real expensive, about $15-$20 worth of stuff. My card got declined. I had no other payment. I was going to leave the stuff there when the person in line behind me offered to just pay for it. I was really grateful and also super shocked because you don't see that kind of thing anymore.",
 "My car had broken down taking my kids to school. Another mother drove by and offered to take my kids to school. Then she was kind enough to return, picked me up and drove me home.",
 "I am usually the one to make sure the laundry is done for everyone in the house. For a few weeks I had been working really hard, trying to get in a lot of overtime, and in between working from home one evening I put a load of clothes in the washer. I completely forgot about them and before I fell into bed I remember the clothes. I rushed out to the washer to put the clothes in the dryer and saw my son had not only tried the clothes, but had folded them. I got tears in my eyes for how nice that felt.",
 "To be honest I had hard times remembering anything.. A couple weeks ago I was at the groceries store, an old man and I going to the cashier, he was in front of me: he picked the left line and I continued to the right. His line was going to be closed with the client before him so he moved to my line. I told him to pass in front of me and he replied he wasn't in a hurry because he was retired.",
 "One nice thing that someone did for me recently that I wasn't expecting was when my dad went to Trader Joe's and bought healthy food for me after I had been feeling unwell. He usually shops at a different grocery store , but he did it because he knew that it would be food that I would like.",
 "I was in the office before lunchtime somene gave me a candy, . It was super unexpected, I felt super good. I really I needed",
 "My sister surprised me by leaving fruit snacks at my front door the other day. I love fruit snacks. It was a small thing, but made my day.",
 "I was given a really nice compliment at work.  I was told I inspired someone to improve at their job and she got a promotion because of it.  It was incredibly heartwarming.",
 "I was in the drive-thru to pick up my starbucks for my office.  As I pulled up to the window, the barista told me that my order had been paid for by the car in front of me.  I was extremely grateful for the kindness and paid it forward when I could.",
 "It was late at night, and I had just gotten out of the gym. I walked to the parking lot, got in my car, and tried to start it, and the battery was dead. A fellow gym-goer came out shortly after, and I guess he heard me repeatedly try and start my car, which was unsuccessful. He walked up to my window and asked me if I needed a jump - and I said absolutely, yes, thank you! He pulled his car around, we got the batteries connected, and 30 seconds later I was on my way. What a life-saver! I was relieved to not have to waste money calling roadside assistance, and was quite frankly seriously positively surprised that someone would go out of their way to help me. It was very heartening, and I now make it a point to 'pay it forward' as far as these sorts of little kindnesses go.",
 "I was really tired and we were out of coffee. I had to go to a 7 hr online trainig class and didnt know how I'd make it through. My husband shows up with coffee for me, I was so happy and grateful.",
 "Our HR Manager was making cheese, egg and ham biscuits for everyone to enjoy one morning. I am a vegetarian and the ham was mixed in so I would not have been able to partake of the goodies. However she surprised me by making 4 separate ham  free biscuits just for me.  It was a very nice and welcome treat!",
 "My buddy let me sleep on his couch for a week.  I really needed a friend right then.  Felt great to have one pull through for me.",
 "I recently was cleaning my home and wanted to get rid of a lot of items around the house. I started early in the morning and was extremely surprised at how well the house was looking, for the first time in over a year the clutter was gone. But I noticed  I had lots of stuff out by the curb and wondering now if the city will pick it up.",
 "To my surprise my neighbor who has a truck  came over and loaded all the garbage on his truck and took it to the dump.",
 "I was so relieved that it was all gone. I felt so blessed to have such a nice neighbor.",
"A young lady offered me her seat on the subway.  Now, at 63 I'm not that old but I do have gray hair.  I appreciated her offer though I did not take her up on it - what a kind young lady.",
"It was surprising group kindness.  I went to a dance class tonight and the cirque class was doing dive cart wheels.  I thought I could do them and asked to join in.  The teacher let me.  I was the last one and the others cheered really loud when I did it.  Normally, we don't cheer in class, so it was a big surprise!  I'm almost 3 times older than them...I guess that might be the reason....but am not sure.",
"I had had a really bad day and a friend that I had been texting about stuff I had been going through was there for me. She gave up the rest of her afternoon at work and showed up at my apartment and took me to lunch and then we just sat and talked. Totally unexpected. Glad she gave me time.",
 "I had taken the day off work because I was very tired. My mom stopped by just to bring me a can of coke which she knows is my favorite drink. I felt like someone cares about me.",
"I had a fight with my boyfriend and was walking around our neighborhood which was in a downtown area. A homeless person bought me a beer and talked about angels and God. It was a really touching moment. I felt as though it was a message from above even if that sounds ridiculous.",
"I was in between jobs and really eager to get my foot in my career. I had posted an article on Facebook about the type of career I wanted, nothing too long and nothing like rant. A friend read it and messaged me right away, telling me she had a cousin who was in that field. Without me being able to reply, I got a text message from the cousin saying how my friend had vouched for me and if I would be available for an interview. Fast forward to now and I am loving my job! It was such a helpful extended act of kindness that changed my life.",
"My husband made the bed yesterday. I know it sounds trivial but it was a really nice surprise to walk in on. I hate making beds and I also hate sleeping in unmade beds so it meant a lot to me. It made me feel special and appreciated.",
"My dog was sick and I had just brought my favorite goat buck in for a second surgery. I was stressed out and upset because I have no other family than my animals. I was telling all this to a cashier at an organic grocer store I shop at. She an I have been friendly for over a year. After listening to me she said it hurt her to see me so sad and she asked me if she could give me a hug. I haven't had a hug from anyone in years so it made me feel like I was cared about by someone in a genuine way. I felt touched and sad at the same time, realizing just how long it had been since anyone showed me a tender act of care.",
"I was recently on the side of the road with a flat tire and a sweet boy stopped and changed my tire for me. I felt really thankful and also know there is still some good in this world.",
"A person who I didn't expect to show sympathy, actually was very upset when I told him about my mother's recent death - he even wrote a note.",
"I was out to dinner at Outback Steakhouse with my family (5 of us). My 3 young kids were being somewhat challenging, and I was a little stressed and not really enjoying my meal.  When we were done, the waiter came to us and said someone at the bar had paid for our meal, tip and everything. We immediately looked up, and no one was there. I was so in shock, confused, and happy....it really made my day.  I posted about it on Facebook and thanked the stranger who paid for our meal, and promised to pay it forward. I received a lot of positive feedback and comments from people.",
"My female cousin once unexpectedly baked a cake and served me a piece. I felt this was quite unusual since she's normally lazy and never does things like this (for anyone), although the inverse is not quite the same. I enjoyed it and found it unusual (the gesture), but coined it as a random act of kindness from her.",
"I was trying to buy a house and someone offered me the money I needed to put down on the house. I was over joyed and shocked that someone would help me in that.",
"I was in a video store (ok not extremely recently but it is what came to mind) and talking to my daughter about what we could get because I had rent due and was really short on money and it was just a bad time in general.  The next thing I knew a somewhat scruffy-looking guy came up and handed me a five dollar bill and kept walking.  I just stood there in shock for a little bit.  I was a little embarrassed, but is was such a nice thing for somebody to have done.",
"The grocery store where I shop regularly was crowded. I had other important work to do and was short on time. The helper at the grocery store understood my predicament  and helped me pay my grocery bill by opening a new counter. He really made my day!",
"One day, I happened to be in a town quite unfamiliar to me. The people there spoke a different language which I could not understand well. Similarly, they could not understand what I was telling them. I wanted to go to a hotel run by somebody who speaks my language. I knew only my mother tongue besides English. There gathered a number of taxi drivers  around me to take advantage of my miserable situation. Seeing the pathetic condition I was in, a young man stepped forward and asked what the matter was. He spoke in good English. I felt very much relieved and happy. I explained my situation and requested him to help. First he assured me that he was not a con and showed me a chain around his neck to prove that he was a devotee of one of the local Gods. He then called one of the taxi drivers and gave instructions as to where to take me. The taxi took me to a hotel whose owner spoke the same language as mine. I felt greatly grateful to the person who helped me in this difficult situation.",
"A man let me go in front of him at the grocery store when I only had a few items and he had a cart full.  I was very relieved because I had a rowdy toddler with me.  It was very nice of him and I was very appreciative.",
"I was on my way to one of my hardest midterms last year, physics. Unexpectedly, a girl was handing out packets containing snacks and other various test supplies out on campus, which was a really nice.",
"A man I had just met, changed the flat tube in my tire five minutes before the mountain bike race that we were both competing in. He did this without hesitation. Amazing.",
"I just did some groceries. I got some extra snacks and drinks. I saw a homeless senior citizen resting on a bench, and decided on a whim to share my snacks and drinks.",
"I was having a really bad day out of nowhere. My mom showed up to my apartment and brought flowers. She does not even live in the same town as me. It was very thoughtful of her. I felt grateful for her.",
"I always strive for a clean house but rarely get it. Not only did I walk into a clean house after work on my Birthday, but my man also got me the most beautiful flowers! I always want nothing but flowers for my day and this time he came through and then some! It was awesome and made me feel good.",
};
JSONArray values;
void setup(){
  values = new JSONArray();

  for (int i = 0; i < rawKindness.length; i++) {
    JSONArray indicesArray = new JSONArray();
    StringList tempIndexValue = new StringList();
    String[] quote = RiTa.tokenize(rawKindness[i]);//make each quote into an array of words
    
    for(int j=0; j<quote.length; j++){//run through each word in the quote to test for verb matches
      for(int k=0;k<kindverbs.length;k++){//test to see if it matches any of the desired verbs
        String quotedword = quote[j];
        String kindword = kindverbs[k];
        if(quotedword.equals(kindword)){//test to see if it matches any of the desired verbs
          tempIndexValue.append(kindword);//add those index values into a list
        }
      }
    }
    for(int j=0;j<tempIndexValue.size();j++){//the list gets added into a json array
      String indexvalue = tempIndexValue.get(j);
      indicesArray.setString(j,indexvalue);
    }
    String filename = "heart"+i+"copy"+".jpeg";
    images[i] = filename;
    filename = "heart"+i+".jpeg";
    imageAgain[i] = filename;
    JSONObject kindness = new JSONObject();

    kindness.setInt("pg", i+2);
    kindness.setString("kindness", "\""+rawKindness[i]+"\"");
    kindness.setString("image", images[i]);
    kindness.setString("image2", imageAgain[i]);
    kindness.setString("author", id[i]);
    kindness.setJSONArray("indicesToHighlight", indicesArray);

    values.setJSONObject(i, kindness);
  }

  saveJSONArray(values, "data/book_content.json");
}

Here’s the Basil.js!

 
#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
// Load a data file containing your book's content. This is expected
// to be located in the "data" folder adjacent to your .indd and .jsx. 
var jsonString = b.loadString("book_content.json");
var jsonData;

//--------------------------------------------------------
function setup() {
  b.canvasMode(b.BLEED);
  // Clear the document at the very start. 
  b.clear (b.doc());

  // Make a Title page. 
  b.fill(0,0,0);
  b.textSize(24);
  b.textFont("Taz","UltraLight"); 
  b.textAlign(Justification.LEFT_ALIGN); 
  b.text("SOMETHING KIND", 72,72,360,36);
  b.noStroke();  // no border around img
  var coverimg = b.image("images/heartCover.jpg",b.width-50,b.height*0.8039,50,b.height-b.height*0.8039);
  coverimg.fit(FitOptions.PROPORTIONALLY);

  
  // Parse the JSON file into the jsonData array
  jsonData = b.JSON.decode( jsonString );
  b.println("Number of elements in JSON: " + jsonData.length);

  b.addPage();
  //b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
  //b.text("Much thanks to Professor Golan Levin for the many all nighters you pulled with us",b.width*0.2,b.height*0.2, b.wdith*0.6, b.height*0.4);

  // Initialize some variables for element placement positions.
  // TIP: units are "points", 72 points = 1 inch.
  var quoteW = b.width/2;
  var quoteH = b.height/1.5;
  var quoteX = b.width/2-quoteW/2; 
  var quoteY = b.height/2-quoteH/2;

  var captionX = 0.2*b.width; 
  var captionY = b.height*0.7058;
  var captionW = b.width*0.6;
  var captionH = 36;

  var imageX = 0; 
  var imageXx = 496/612*b.width;
  var imageY = 453/612*b.height;

   b.println("dedication page");
  // Loop over every element of the book content array
  for (var i = 0; i < jsonData.length; i++) {
    b.println(i);
    // Create the next page. 
    b.addPage();

    // Load an image from the "images" folder inside the data folder;
    var anImageFilename = "images/" + jsonData[i].image;
    var anotherImageFilename= "images/" + jsonData[i].image2;
    /*if(i!=jsonData.length-1){
      anotherImageFilename = "images/" + jsonData[i+1].image2;
    }
    else anotherImageFilename = "images/" + jsonData[i].image2;*/
    var anImage = b.image(anImageFilename, 0, b.height*0.8039, 50, b.height-b.height*0.8039);
    anImage.fit(FitOptions.PROPORTIONALLY);

    b.fill(0);
    b.textSize(16);
    b.textKerning(22);
    b.textFont("Taz","UltraLight"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text(jsonData[i].kindness, quoteX,quoteY,quoteW,quoteH);

    // Create textframes for the "caption" fields
    b.fill(0);
    b.textSize(12);
    b.textFont("Taz","UltraLight"); 
    b.textAlign(Justification.RIGHT_ALIGN, VerticalJustification.CENTER_ALIGN );
    b.text("- "+jsonData[i].author, captionX,captionY,captionW,captionH);

    b.addPage();

    var anImageAgain = b.image(anotherImageFilename,b.width-50,b.height*0.8039,50,b.height-b.height*0.8039);
    anImageAgain.fit(FitOptions.PROPORTIONALLY);

    for(var j=0;j<jsonData[i].indicesToHighlight.length;j++){
      b.println(i+"verb"+j+" "+jsonData[i].indicesToHighlight[j]);
      var fillcolor = b.lerp(200, 50, b.pageCount()/104);
      b.fill(b.random(255,b.pageCount()),b.random(fillcolor+55,fillcolor),b.random(fillcolor+55,fillcolor));
      var tsizing = b.lerp(72,24,b.pageCount()/104);
      b.textFont("Taz","SemiLight"); 
      b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.CENTER_ALIGN );
      b.textSize(b.random(tsizing+20, tsizing-10));
      var tcoordinatey = b.random(b.height*0.1, b.height*0.9);
      var tcoordinatex = (b.random(0,b.width*0.7));
      b.text(jsonData[i].indicesToHighlight[j],tcoordinatex,tcoordinatey, b.width-tcoordinatex, 72);
    }
    

  };

  b.addPage();
  var anotherHeart = b.image("images/heart53copy.jpeg", 0, 526, 50, b.height-b.height*0.8039);
  anotherHeart.fit(FitOptions.PROPORTIONALLY);

  ///////GO & DO PAGE------------------------------
  b.addPage();
  var lastHeart = b.image("images/heart53.jpeg", 526,b.height*0.8039,50,b.height-b.height*0.8039);
  lastHeart.fit(FitOptions.PROPORTIONALLY);
  b.textSize(36);
  b.textFont("Taz","SemiLight"); 
  b.textAlign(Justification.LEFT_ALIGN, VerticalJustification.TOP_ALIGN);
  b.fill(255);
    b.text("DO", 533,528.25,72,72);
  b.fill(0);
    b.text("GO", 452,528.25,72,72);
  b.textSize(34);
  b.textFont("DIN","Regular"); 
  b.text("&", 501,524.5,72,72);

  //last words----------------------
  b.addPage();
  var lastImage = b.image("images/TurkThank.png", 0.2*b.width, 0.2*b.height, 0.6*b.width, 0.4*b.height);
  lastImage.fit(FitOptions.PROPORTIONALLY);
  b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
  b.textSize(10);
  b.text("This book is a computationally compilated and formatted collection of Mechanical Turk Worker responses. Above is an email I recieved from a turker",
    captionX,captionY,captionW,2*captionH);

}

// This makes it all happen:
b.go(); 
Written by Comments Off on Lumar-BookOfKindness Posted in Book

tigop-FaceOSC

plushuluminionbetter

My faceware software is based on a story that I made called the Gumwad Fairy. It is a story about a baby gumwad who lives under a bus seat. Baby gumwad is taken from underneath the bus seat and is flung out through the window by a wretched human. Later in the story, baby gumwad sprouts wings- realizing baby gumwad’s potential as Gumwad Fairy. Gumwad Fairy runs into a creature I call Plushulu (a spin off of Cthulhu) Plushulu’s minions crawl out of Plushulu’s tentacle-thingies, and they convince Gumwad Fairy to join them in their Utopia and leave the land of the god awful humans. Gumwad Fairy decides it is a good idea to leave, and follows them to their land. In this program, you get to control a narrative, you get to perform as a Plushulu minion. You squeak when you talk, can move into the distance and up close, and there’s some lovely music playing in the background.

I had created a separate program to draw the Plushulu Minion. This program stores an ArrayList of coordinates that I have sketched. I later edited the program so that it would export a PDF of the image I created. This was after I actually used the program, but now I have a tool I can use later on! The sketch I made was not a Plushulu minion, I was trying to draw Plushulu but thought to start off simpler.

cousin-of-plushulu

 

Here is a link to Gumwad Fairy!

mport oscP5.*;
OscP5 oscP5;
import processing.sound.*;
SoundFile squeak;
SoundFile happy;


PVector posePosition;
boolean found;
boolean start = true;
boolean open = false;
float eyeLeftHeight;
float eyeRightHeight;
float mouthHeight;
float mouthWidth;
float nostrilHeight;
float leftEyebrowHeight;
float rightEyebrowHeight;
float jawOpen;

float poseScale;

int dieRoll;
PShape head;
PShape sidefluffs1;
PShape sidefluffs2;
PShape eyeball1;
PShape pupil1;
PShape eyeball2;
PShape pupil2;
PShape speechbubble;

PFont speechFont;

void setup() {
  speechFont= loadFont("CourierNewPS-BoldMT-48.vlw");
  size(800, 800);
  frameRate(30);
  
  squeak = new SoundFile(this, "squeak.wav");
  happy = new SoundFile(this, "happy.mp3");
  happy.play();
  happy.stop();
  happy.loop();
  
  //head
  head = createShape();
  head.beginShape();
  head.stroke(250,189,5);
  head.fill(250,206,56);
  head.beginShape();
  head.vertex(322,37);
  head.vertex(320,34);
  head.vertex(311,31);
  head.vertex(302,30);
  head.vertex(293,28);
  head.vertex(282,30);
  head.vertex(276,32);
  head.vertex(270,39);
  head.vertex(264,47);
  head.vertex(256,58);
  head.vertex(249,70);
  head.vertex(243,81);
  head.vertex(239,92);
  head.vertex(235,103);
  head.vertex(233,115);
  head.vertex(233,123);
  head.vertex(233,134);
  head.vertex(233,140);
  head.vertex(234,147);
  head.vertex(235,153);
  head.vertex(239,157);
  head.vertex(243,160);
  head.vertex(251,160);
  head.vertex(261,160);
  head.vertex(273,161);
  head.vertex(286,166);
  head.vertex(296,169);
  head.vertex(305,169);
  head.vertex(316,169);
  head.vertex(324,168);
  head.vertex(330,166);
  head.vertex(337,162);
  head.vertex(343,159);
  head.vertex(352,156);
  head.vertex(358,154);
  head.vertex(364,150);
  head.vertex(367,141);
  head.vertex(367,134);
  head.vertex(366,125);
  head.vertex(366,115);
  head.vertex(365,105);
  head.vertex(363,94);
  head.vertex(359,84);
  head.vertex(355,75);
  head.vertex(352,66);
  head.vertex(347,57);
  head.vertex(342,49);
  head.vertex(338,43);
  head.vertex(334,41);
  head.vertex(331,39);
  head.vertex(328,37);
  head.vertex(324,36);
  head.endShape(CLOSE);

//sidefluffs
  sidefluffs1 = createShape();
  sidefluffs1.beginShape();
  sidefluffs1.stroke(113,34,244);
  sidefluffs1.strokeWeight(2);
  sidefluffs1.fill(149,43,255);
  sidefluffs1.vertex(254,55);
  sidefluffs1.vertex(254,54);
  sidefluffs1.vertex(249,50);
  sidefluffs1.vertex(234,44);
  sidefluffs1.vertex(208,38);
  sidefluffs1.vertex(174,36);
  sidefluffs1.vertex(177,37);
  sidefluffs1.vertex(180,39);
  sidefluffs1.vertex(189,44);
  sidefluffs1.vertex(193,47);
  sidefluffs1.vertex(197,51);
  sidefluffs1.vertex(199,53);
  sidefluffs1.vertex(192,51);
  sidefluffs1.vertex(183,49);
  sidefluffs1.vertex(169,47);
  sidefluffs1.vertex(154,47);
  sidefluffs1.vertex(133,53);
  sidefluffs1.vertex(115,61);
  sidefluffs1.vertex(104,70);
  sidefluffs1.vertex(112,69);
  sidefluffs1.vertex(124,67);
  sidefluffs1.vertex(137,65);
  sidefluffs1.vertex(152,67);
  sidefluffs1.vertex(167,71);
  sidefluffs1.vertex(174,73);
  sidefluffs1.vertex(176,74);
  sidefluffs1.vertex(172,76);
  sidefluffs1.vertex(162,85);
  sidefluffs1.vertex(149,97);
  sidefluffs1.vertex(139,109);
  sidefluffs1.vertex(132,119);
  sidefluffs1.vertex(130,122);
  sidefluffs1.vertex(130,123);
  sidefluffs1.vertex(136,116);
  sidefluffs1.vertex(144,109);
  sidefluffs1.vertex(156,103);
  sidefluffs1.vertex(167,91);
  sidefluffs1.vertex(169,97);
  sidefluffs1.vertex(183,92);
  sidefluffs1.vertex(189,90);
  sidefluffs1.vertex(190,90);
  sidefluffs1.vertex(189,90);
  sidefluffs1.vertex(184,98);
  sidefluffs1.vertex(177,111);
  sidefluffs1.vertex(173,124);
  sidefluffs1.vertex(172,128);
  sidefluffs1.vertex(177,121);
  sidefluffs1.vertex(187,109);
  sidefluffs1.vertex(197,101);
  sidefluffs1.vertex(207,93);
  sidefluffs1.vertex(217,89);
  sidefluffs1.vertex(227,87);
  sidefluffs1.vertex(232,86);
  sidefluffs1.vertex(238,86);
  sidefluffs1.vertex(239,86);
  sidefluffs1.vertex(240,82);
  sidefluffs1.vertex(240,82);
  sidefluffs1.vertex(240,79);
  sidefluffs1.vertex(240,76);
  sidefluffs1.vertex(243,75);
  sidefluffs1.vertex(232,86);
  sidefluffs1.vertex(246,78);
  sidefluffs1.vertex(247,78);
  sidefluffs1.vertex(247,75);
  sidefluffs1.vertex(247,72);
  sidefluffs1.vertex(247,71);
  sidefluffs1.vertex(248,69);
  sidefluffs1.vertex(252,71);
  sidefluffs1.vertex(255,73);
  sidefluffs1.vertex(253,66);
  sidefluffs1.vertex(253,63);
  sidefluffs1.vertex(253,62);
  sidefluffs1.vertex(254,62);
  sidefluffs1.vertex(259,65);
  sidefluffs1.vertex(259,66);
  sidefluffs1.endShape(CLOSE);
  
  //sidefluffs2
  sidefluffs2 = createShape();
  sidefluffs2.beginShape();
  sidefluffs2.fill(149,43,255);
  sidefluffs2.stroke(113,34,244);
  sidefluffs2.strokeWeight(2);
  sidefluffs2.vertex(355,49);
  sidefluffs2.vertex(353,49);
  sidefluffs2.vertex(344,54);
  sidefluffs2.vertex(342,55);
  sidefluffs2.vertex(352,56);
  sidefluffs2.vertex(348,67);
  sidefluffs2.vertex(347,69);
  sidefluffs2.vertex(348,69);
  sidefluffs2.vertex(355,65);
  sidefluffs2.vertex(358,64);
  sidefluffs2.vertex(355,72);
  sidefluffs2.vertex(353,78);
  sidefluffs2.vertex(352,79);
  sidefluffs2.vertex(352,80);
  sidefluffs2.vertex(353,79);
  sidefluffs2.vertex(368,76);
  sidefluffs2.vertex(380,76);
  sidefluffs2.vertex(395,78);
  sidefluffs2.vertex(412,83);
  sidefluffs2.vertex(439,93);
  sidefluffs2.vertex(443,96);
  sidefluffs2.vertex(442,95);
  sidefluffs2.vertex(433,88);
  sidefluffs2.vertex(423,80);
  sidefluffs2.vertex(416,75);
  sidefluffs2.vertex(415,74);
  sidefluffs2.vertex(418,74);
  sidefluffs2.vertex(429,76);
  sidefluffs2.vertex(440,81);
  sidefluffs2.vertex(447,85);
  sidefluffs2.vertex(453,88);
  sidefluffs2.vertex(458,92);
  sidefluffs2.vertex(459,93);
  sidefluffs2.vertex(458,90);
  sidefluffs2.vertex(416,75);
  sidefluffs2.vertex(415,74);
  sidefluffs2.vertex(418,74);
  sidefluffs2.vertex(429,76);
  sidefluffs2.vertex(440,81);
  sidefluffs2.vertex(447,85);
  sidefluffs2.vertex(453,88);
  sidefluffs2.vertex(458,92);
  sidefluffs2.vertex(459,93);
  sidefluffs2.vertex(458,90);
  sidefluffs2.vertex(454,81);
  sidefluffs2.vertex(446,73);
  sidefluffs2.vertex(437,66);
  sidefluffs2.vertex(429,62);
  sidefluffs2.vertex(430,62);
  sidefluffs2.vertex(441,62);
  sidefluffs2.vertex(455,62);
  sidefluffs2.vertex(470,66);
  sidefluffs2.vertex(477,69);
  sidefluffs2.vertex(478,70);
  sidefluffs2.vertex(478,69);
  sidefluffs2.vertex(467,60);
  sidefluffs2.vertex(455,55);
  sidefluffs2.vertex(455,53);
  sidefluffs2.vertex(437,50);
  sidefluffs2.vertex(430,50);
  sidefluffs2.vertex(429,50);
  sidefluffs2.vertex(439,48);
  sidefluffs2.vertex(469,43);
  sidefluffs2.vertex(482,44);
  sidefluffs2.vertex(490,46);
  sidefluffs2.vertex(492,46);
  sidefluffs2.vertex(489,45);
  sidefluffs2.vertex(478,41);
  sidefluffs2.vertex(468,39);
  sidefluffs2.vertex(456,37);
  sidefluffs2.vertex(439,36);
  sidefluffs2.vertex(430,35);
  sidefluffs2.vertex(427,36);
  sidefluffs2.vertex(433,33);
  sidefluffs2.vertex(447,25);
  sidefluffs2.vertex(457,21);
  sidefluffs2.vertex(461,19);
  sidefluffs2.vertex(462,19);
  sidefluffs2.vertex(450,19);
  sidefluffs2.vertex(437,19);
  sidefluffs2.vertex(419,22);
  sidefluffs2.vertex(408,29);
  sidefluffs2.vertex(404,32);
  sidefluffs2.vertex(403,33);
  sidefluffs2.vertex(406,27);
  sidefluffs2.vertex(408,21);
  sidefluffs2.vertex(410,17);
  sidefluffs2.vertex(400,22);
  sidefluffs2.vertex(395,25);
  sidefluffs2.vertex(390,28);
  sidefluffs2.vertex(383,31);
  sidefluffs2.vertex(371,35);
  sidefluffs2.vertex(362,39);
  sidefluffs2.vertex(355,42);
  sidefluffs2.endShape(CLOSE);
  
  //eyeball1
  eyeball1 = createShape();
  eyeball1.beginShape();
  eyeball1.noStroke();
  eyeball1.fill(255);
  eyeball1.vertex(288,68);
  eyeball1.vertex(288,66);
  eyeball1.vertex(267,62);
  eyeball1.vertex(263,68);
  eyeball1.vertex(262,73);
  eyeball1.vertex(263,80);
  eyeball1.vertex(267,84);
  eyeball1.vertex(272,86);
  eyeball1.vertex(279,86);
  eyeball1.vertex(283,86);
  eyeball1.vertex(287,85);
  eyeball1.vertex(290,81);
  eyeball1.vertex(293,77);
  eyeball1.vertex(293,72);
  eyeball1.vertex(292,69);
  eyeball1.vertex(290,66);
  eyeball1.endShape(CLOSE);
  
  //pupil1
  pupil1 = createShape();
  pupil1.beginShape();
  pupil1.stroke(0);
  pupil1.fill(0);
  pupil1.vertex(278,59);
  pupil1.vertex(276,58);
  pupil1.vertex(275,58);
  pupil1.vertex(270,59);
  pupil1.vertex(268,61);
  pupil1.vertex(267,65);
  pupil1.vertex(268,68);
  pupil1.vertex(271,71);
  pupil1.vertex(274,72);
  pupil1.vertex(277,70);
  pupil1.vertex(279,69);
  pupil1.vertex(280,67);
  pupil1.vertex(281,65);
  pupil1.vertex(281,63);
  pupil1.vertex(280,61);
  pupil1.vertex(278,60);
  pupil1.endShape(CLOSE);
  
  //eyeball2
  eyeball2 = createShape();
  eyeball2.beginShape();
  eyeball2.noStroke();
  eyeball2.fill(255);
  eyeball2.vertex(288+45,68);
  eyeball2.vertex(288+45,66);
  eyeball2.vertex(267+45,62);
  eyeball2.vertex(263+45,68);
  eyeball2.vertex(262+45,73);
  eyeball2.vertex(263+45,80);
  eyeball2.vertex(267+45,84);
  eyeball2.vertex(272+45,86);
  eyeball2.vertex(279+45,86);
  eyeball2.vertex(283+45,86);
  eyeball2.vertex(287+45,85);
  eyeball2.vertex(290+45,81);
  eyeball2.vertex(293+45,77);
  eyeball2.vertex(293+45,72);
  eyeball2.vertex(292+45,69);
  eyeball2.vertex(290+45,66);
  eyeball2.endShape(CLOSE);
  
  //pupil2
  pupil2 = createShape();
  pupil2.beginShape();
  pupil2.stroke(0);
  pupil2.fill(0);
  pupil2.vertex(278+50,59);
  pupil2.vertex(276+50,58);
  pupil2.vertex(275+50,58);
  pupil2.vertex(270+50,59);
  pupil2.vertex(268+50,61);
  pupil2.vertex(267+50,65);
  pupil2.vertex(268+50,68);
  pupil2.vertex(271+50,71);
  pupil2.vertex(274+50,72);
  pupil2.vertex(277+50,70);
  pupil2.vertex(279+50,69);
  pupil2.vertex(280+50,67);
  pupil2.vertex(281+50,65);
  pupil2.vertex(281+50,63);
  pupil2.vertex(280+50,61);
  pupil2.vertex(278+50,60);
  pupil2.endShape(CLOSE);
  
  //speechbubble
  speechbubble = createShape();
  speechbubble.beginShape();
  speechbubble.noStroke();
  speechbubble.fill(235,35,90);
  speechbubble.vertex(183,280);
  speechbubble.vertex(182,280);
  speechbubble.vertex(204,266);
  speechbubble.vertex(231,248);
  speechbubble.vertex(243,234);
  speechbubble.vertex(219,213);
  speechbubble.vertex(204,190);
  speechbubble.vertex(207,158);
  speechbubble.vertex(227,134);
  speechbubble.vertex(247,123);
  speechbubble.vertex(279,119);
  speechbubble.vertex(319,117);
  speechbubble.vertex(366,115);
  speechbubble.vertex(402,119);
  speechbubble.vertex(440,123);
  speechbubble.vertex(469,133);
  speechbubble.vertex(487,156);
  speechbubble.vertex(493,192);
  speechbubble.vertex(483,220);
  speechbubble.vertex(459,223);
  speechbubble.vertex(409,237);
  speechbubble.vertex(352,241);
  speechbubble.vertex(314,240);
  speechbubble.vertex(281,239);
  speechbubble.vertex(261,242);
  speechbubble.vertex(263,242);
  speechbubble.vertex(250,256);
  speechbubble.vertex(230,275);
  speechbubble.vertex(208,278);
  speechbubble.endShape(CLOSE);
  
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseScale", "/pose/scale");
  
  
}

void draw() {  
  background(255,166,255);
  stroke(0);
  
  if (found) {
    delay(100);
    
    if(mouthHeight > 6){
      open = true;
      textFont(speechFont);
      squeak.play();
      textAlign(CENTER,CENTER);
      
      if(open == true){
        dice();
      }
      
    }
    
    switch(dieRoll){
        case 1: textSize(30);text("Comrade!",400,500); open = false; break;
        case 2: textSize(30); text("Free yourself from the wretched humans!",400,500); open = false; break;
        case 3: textSize(28);text("The mighty Plushulu has \n summoned us to warn you!",400,500); open = false;break;
        case 4: textSize(25);text("Leave this place, bask in \n the glory of our utopia!",400,500); open = false;break;
        case 5: textSize(20);text("The humans can never wrong you again!",400,500); open = false;break;
        case 6: textSize(30);text("You are dissatisfied, are you not? \n You need not settle for this.",400,500);open = false; break;
        
      }
    //translate(-(posePosition.x)-30,posePosition.y);
    
    scale(poseScale*0.38);
    
    shape(head,-posePosition.x+400,posePosition.y);
    shape(sidefluffs1,-posePosition.x+400,posePosition.y);
    shape(sidefluffs2,-posePosition.x+400,posePosition.y);
    
    if(eyeLeftHeight>2.7){
      shape(eyeball1,-posePosition.x+400,posePosition.y);
      shape(pupil1,-posePosition.x+400,posePosition.y);
    }
    else{
      strokeWeight(2);
      line(-posePosition.x+672,posePosition.y+70,-posePosition.x+684,posePosition.y+70);
    }
    
    if(eyeLeftHeight>2.7){
      shape(eyeball2,-posePosition.x+400,posePosition.y);
      shape(pupil2,-posePosition.x+400,posePosition.y);
    }
    else{
      strokeWeight(2);
      line(-posePosition.x+718,posePosition.y+70,-posePosition.x+730,posePosition.y+70);
    }
    
    noStroke();
    fill(136,0,0,180);
    ellipse(-posePosition.x+700, posePosition.y+120, mouthWidth*2, mouthHeight * 3);
    
    /*
    noFill();
    // ellipse(0,0, 3,3);
    
    ellipse(-20, eyeLeftHeight * -9, 20, 7);
    ellipse(20, eyeRightHeight * -9, 20, 7);
    ellipse(0, 20, mouthWidth* 3, mouthHeight * 3);
    ellipse(-5, nostrilHeight * -1, 7, 3);
    ellipse(5, nostrilHeight * -1, 7, 3);
    rectMode(CENTER);
    fill(0);
    rect(-20, leftEyebrowHeight * -5, 25, 5);
    rect(20, rightEyebrowHeight * -5, 25, 5);
    */
  }
}

void dice(){
  dieRoll = int(random(1,7));
}

public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}

public void eyebrowLeftReceived(float h) {
  println("eyebrow left: " + h);
  leftEyebrowHeight = h;
}

public void eyebrowRightReceived(float h) {
  println("eyebrow right: " + h);
  rightEyebrowHeight = h;
}

public void eyeLeftReceived(float h) {
  println("eye left: " + h);
  eyeLeftHeight = h;
}

public void eyeRightReceived(float h) {
  println("eye right: " + h);
  eyeRightHeight = h;
}

public void jawReceived(float h) {
  println("jaw: " + h);
  jawOpen = h;
}

public void nostrilsReceived(float h) {
  println("nostrils: " + h);
  nostrilHeight = h;
}

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

public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition = new PVector(x, y);
}

public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}

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


void oscEvent(OscMessage theOscMessage) {
  if (theOscMessage.isPlugged()==false) {
    println("UNPLUGGED: " + theOscMessage);
  }
}
Written by Comments Off on tigop-FaceOSC Posted in FaceOSC

kadoin-lookingoutwards05

I’ve watched all the Dan Shiffman videos on Perlin noise so it was really cool to hear Ken Perlin speak at Weird Reality. I didn’t really know all that much about his work outside of the noise function, so to see more of the cool things he’s been doing was awesome. Something I hadn’t really considered until I saw the Google Daydream talk the day before was VR with multiple people that could interact with each other, and his demo for tracking the with phone VR sets and the ping pong balls was really cool. The Poop VR game was the only demo at the VR salon that incorporated interaction with other people, but it was limited in action and movement because you’re limited to a toilet and just Google Cardboard. I think Ken Perlin’s examples he showed where multiple people wandered around a room and were able to interact with each other and other elements in virtual space did a good job of exploring new kinds interactions with their more advanced headsets. I also think it’s interesting how he said no one ran into each other while they had the headsets on. I didn’t think with all the perspective distortions to make the virtual space seem bigger, it would still be accurate enough to mirror real space. One thing it made me think of that no one really brought up during the conference, what getting the same sort of interactions, but with people who aren’t in the same room. I think a VR space that allows people across the globe to enter and interact would be something pretty amazing with a lot of potential.

tigop-Looking Outwards 05

Being at Weird Reality really allowed me to see both the possibilities as well as the limitations of Virtual Reality. A piece that I got to spend a lot of time with was Jeremy Bailey’s piece called Preterna. It was a piece in which the viewer became part of a commercial, and was able to experience “a stage before pregnancy”, the stage in which an individual is contemplating pregnancy. In the background, you are able to hear Jeremy and his wife, Kristen, bickering over whether or not they wanted to have a child. Jeremy had included 3D scans of his wife in the environment which was created in the piece, and the viewer is able to invade one of the 3D scans, taking over the body and manipulating severed arms which seem to float in the air (this had been achieved through the use of a motion detector placed on the Oculus Rift). The way the viewer steps into the 3D scan raises a question as to whether or not this individual is truly able to empathize with one who is pregnant- all while they are not actually able to experience all the other physical symptoms associated with pregnancy, such as shortness in breath, fatigue, morning sickness (though VR may very well make the nausea accessible), frequent need to urinate, etc. How do we make all these other sensory experiences accessible in the environment which can be created through VR? Talking to Jeremy, it had been made clear that the viewer was not truly supposed to be able to empathize with one who is pregnant, and they are in fact playing a more parasitic role in the invasion of this pregnant woman’s body, participating in a more exploitative role within the commercial. I had spoken to a pregnant woman who tried on the rift, and she said something that stuck with me: “When you step into someone’s shoes, you only take their shoes.” You are never going to fully experience what it is like to be a particular individual because as much as you might know about them, you will never truly know what it felt like to come out of the circumstances that they came out of and to feel the things that they feel. This idea of exploiting the pregnant female body and lacking a connection to who this individual is is a very cynical view on the idea of commercialism and possibly even a critique on commercialism within the health sector. I had a lot of fun working that night.

Darca-FaceOSC

 

Testing the curves

face

Add wiggles when opening mouth.

face3

Change mask color same as the surroundings.

face7

face0

 

Add mirror effect.

face15

 

Here it is, me embracing the weirdness.

screen-shot-2016-10-15-at-9-34-42-pm        screen-shot-2016-10-15-at-9-35-59-pm

screen-shot-2016-10-15-at-9-37-31-pm      screen-shot-2016-10-15-at-9-38-26-pm

screen-shot-2016-10-15-at-9-38-38-pm      screen-shot-2016-10-15-at-9-39-42-pm

screen-shot-2016-10-15-at-9-43-44-pm       screen-shot-2016-10-15-at-9-47-49-pm

screen-shot-2016-10-15-at-9-48-47-pm         screen-shot-2016-10-15-at-9-52-26-pm

screen-shot-2016-10-15-at-9-54-58-pm        screen-shot-2016-10-15-at-9-55-14-pm

// a template for receiving face tracking osc messages from
// Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker
//
// 2012 Dan Wilcox danomatika.com
// for the IACD Spring 2012 class at the CMU School of Art
//
// adapted from from Greg Borenstein's 2011 example
// http://www.gregborenstein.com/
// https://gist.github.com/1603230
//
import oscP5.*;
OscP5 oscP5;
import processing.video.*;
Capture cam;

// num faces found
int found;
float[] rawArray;
float[] tempArray;
FloatList rawBorderArray;
FloatList waveBorderArray;
IntList indexArray;
FloatList otherHalfArray;
float mid;
int tempPixel;


int highlighted; //which point is selected

// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();

// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;

void setup() {
  size(640, 480);
  frameRate(30);

  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "rawData", "/raw");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
  
  String[] cameras = Capture.list();
  
  if (cameras.length == 0) {
    println("There are no cameras available for capture.");
    exit();
  } else {
    cam = new Capture(this, 640, 480, cameras[0]);
    cam.start();     
  }     
}



void draw() {  
  background(255);
  stroke(0);
  if (cam.available() == true) {cam.read();}
  set(0, 0, cam);
  
  if(found != 0) 
  {//totally 132 elements in rawArray, 66 points
    getRawBorderData();
    
    //getMirrorBorder();
    randomness();
    drawMask();
    otherHalf();
    //drawMask2();
    for (int val = 0; val < rawArray.length -1; val+=2) {
      if (val == highlighted) { 
        //fill(0, 255, 0);
      } else {
        noFill();
      }
      noStroke();
    }
  }
  
}



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

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

float chinWidth;
float foreheadWidth;
float noseWidth;
void getRawBorderData() {
  tempArray = rawArray;
  rawBorderArray = new FloatList();
  indexArray = new IntList(); //16,
  
  chinWidth = abs(rawArray[8]-rawArray[24]);
  foreheadWidth = abs(rawArray[0]-rawArray[32]);
  noseWidth = abs(rawArray[62]-rawArray[70]);
  rawBorderArray.append(rawArray[16]);
  rawBorderArray.append(rawArray[17]);
  rawBorderArray.append(rawArray[16]+3);
  rawBorderArray.append(rawArray[17]-(rawArray[17]-rawArray[115])/3);
  rawBorderArray.append(rawArray[16]-3);
  rawBorderArray.append(rawArray[17]-(rawArray[17]-rawArray[115])*2/3); 
  rawBorderArray.append(rawArray[114]);
  rawBorderArray.append(rawArray[115]);//12 13
  rawBorderArray.append(rawArray[128]-3);
  rawBorderArray.append(rawArray[129]);//14 15
  rawBorderArray.append((rawArray[128]+rawArray[122])/2-8);
  rawBorderArray.append((rawArray[129]+rawArray[123])/2);
  rawBorderArray.append(rawArray[122]-5);
  rawBorderArray.append(rawArray[123]);//16 17
  rawBorderArray.append(rawArray[102]);
  rawBorderArray.append(rawArray[103]);//18 19 mouth
  rawBorderArray.append(rawArray[64]);
  rawBorderArray.append(rawArray[65]);
  rawBorderArray.append(rawArray[66]);
  rawBorderArray.append(rawArray[67]-5);
  rawBorderArray.append(rawArray[60]+noseWidth/3);
  rawBorderArray.append(rawArray[71]-10);//nose tip
  rawBorderArray.append(rawArray[60]+noseWidth/2);
  rawBorderArray.append(rawArray[61]);
  rawBorderArray.append(rawArray[58]+noseWidth/2-5);
  rawBorderArray.append(rawArray[59]);
  rawBorderArray.append(rawArray[54]+noseWidth/2-18);
  rawBorderArray.append(rawArray[55]-5); //nose bridge 20~32
  rawBorderArray.append((rawArray[42]+rawArray[44])/2);
  rawBorderArray.append((rawArray[43]+rawArray[45])/2+10);
//33 34 eyebrow
  rawBorderArray.append((rawArray[42]+rawArray[44])/2+5);
  rawBorderArray.append((rawArray[43]+rawArray[45])/2-5);
//33 34 eyebrow bone
  rawBorderArray.append((rawArray[42]+rawArray[44])/2);
  rawBorderArray.append((rawArray[43]+rawArray[45])/2-40);//
  rawBorderArray.append((rawArray[42]+rawArray[44])/2);
  rawBorderArray.append((rawArray[43]+rawArray[45])/2-70);//
  rawBorderArray.append((rawArray[42]+rawArray[44])/2);
  rawBorderArray.append((rawArray[43]+rawArray[45])/2-300);//hairline
  rawBorderArray.append((rawArray[42]+rawArray[44])/2+1000);
  rawBorderArray.append((rawArray[43]+rawArray[45])/2-300);//hairline
  rawBorderArray.append(1000);
  rawBorderArray.append(0);
  rawBorderArray.append(1000);
  rawBorderArray.append(1000);
  
  rawBorderArray.append((rawArray[42]+rawArray[44])/2+50);
  rawBorderArray.append(500);
  //rawBorderArray.append(rawArray[16]+100);
  //rawBorderArray.append(rawArray[17]+100);
  rawBorderArray.append(rawArray[16]);
  rawBorderArray.append(rawArray[17]);
  rawBorderArray.append(rawArray[16]);
  rawBorderArray.append(rawArray[17]+10);
  println("rawBorderArray has:"+ rawBorderArray.size()+"points");
}

float perc;
void randomness() {
  for (int a= 0; a < rawBorderArray.size(); a+=2) {
    perc = random(-2, 2);
    rawBorderArray.set(a, rawBorderArray.get(a)+(mouthHeight*perc));
  }
  
}


void drawWaves() {
  for (int a= 0; a < rawBorderArray.size(); a+=2) {
    perc = random(-2, 2);
    waveBorderArray.append(rawBorderArray.get(a)+(mouthHeight*perc));
  }
}


int r;
int s;
void drawMask() {
  //color c = 0;
  color c = get(400, 10);
  beginShape();
  noFill();
    fill(c);
    stroke(c);
    strokeWeight(3);
    r = 0;
    while (r <= rawBorderArray.size()-1) {
      curveVertex(rawBorderArray.get(r), rawBorderArray.get(r + 1));
      r += 2;
    }
  endShape();
}

void drawMask2() {
  color c = get(400, 10);
  beginShape();
  noFill();
    fill(c);
    stroke(c);
    strokeWeight(3);
    s = 0;
    while (s <= rawBorderArray.size()-1) {
      curveVertex(otherHalfArray.get(s), otherHalfArray.get(s + 1));
      s += 2;
    }
  endShape();
}



void getMirrorBorder() {
  for (int i = 0; i < rawBorderArray.size(); i++) {
    if (i%2 == 0){//x 
      otherHalfArray.append(rawBorderArray.get(i)*(-1)+200);
    }
    else {
      otherHalfArray.append(rawBorderArray.get(i+1));
    }
  }
}


void otherHalf() {
  mid = (rawArray[42]+rawArray[44])/2;
  int Image = cam.width * cam.height; //int(cam.width-mid)
  loadPixels();
  for (int i = 0; i < Image; i++) { if (i%cam.width > int(mid)-80 && i%cam.width < int(mid)+100) { //if on he right side of face 
      pixels[max(0,(i-200))] = pixels[cam.width-i%cam.width+ (i/cam.width)*cam.width];
    }
  }
  updatePixels();
}


//
public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}

public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + 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);
}

public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}

public void eyeLeftReceived(float f) {
  println("eye left: " + f);
  eyeLeft = f;
}

public void eyeRightReceived(float f) {
  println("eye right: " + f);
  eyeRight = f;
}

public void eyebrowLeftReceived(float f) {
  println("eyebrow left: " + f);
  eyebrowLeft = f;
}

public void eyebrowRightReceived(float f) {
  println("eyebrow right: " + f);
  eyebrowRight = f;
}

public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}

public void nostrilsReceived(float f) {
  println("nostrils: " + f);
  nostrils = f;
}

// all other OSC messages end up here
void oscEvent(OscMessage m) {
  if(m.isPlugged() == false) {
    println("UNPLUGGED: " + m);
  }
}





void keyPressed() {
  if (keyCode == RIGHT) {
    highlighted = (highlighted + 2) % rawArray.length;
  }
  if (keyCode == LEFT) {
    highlighted = (highlighted - 2) % rawArray.length;
    if (highlighted < 0) {
      highlighted = rawArray.length-1;
    }
  }
}

Code in Github: https://github.com/DarcyCao/60-212/tree/master/Darca_face

 

Written by Comments Off on Darca-FaceOSC Posted in FaceOSC

cambu-LookingOutwards05

Of all the games I played at the VR Salon, none had the craft and quality of SuperHyperCube. Of course this isn’t a knock against the lesser funded and more artisan efforts — some of them were thoroughly interesting (and thought provoking) experiences.

But, I do think it’s important to recognize the difference between VR experiences that are fun for a 5-minute demo and ones that I could really imagine spending hours in. SuperHyperCube certainly falls into the latter camp. After playing for only a few moments, I was enamored with the slick graphics and slowly building complexity, yet, it had enough similarities to existing game metaphors that it wasn’t overwhelming. Also, the game was simply really fun — never underestimate fun!

I really hope we continue to see VR experiences that focus on both being fun and being of quality — not everything has to be an artist statement.

Antar-LookingOutwards06

ˇÿˇ€

@FFD8FFDB

Creator: Derek Arnold. More info: Medium.

When working on a content-generating bot, there is a point where what you intend for the project to be gives way to what the art wants to be.

Runner Ups for this assignment:

  1. @GIFsofWiki
  2. @pomological
  3. @dronesweetie
  4. @autocompletejok

After looking through many Twitter bots, I really thought this security camera one was the most fascinating. This bot will post random stills from a list of unsecured webcams. According to the Twitter profile, all images are CC0 licensed, and the Quartz article I read explains that the bot will not post photos from particularly invasive cameras, or will try to crop out anything that will show too much information.

I think a signifying detail of good generative art is that it surprises even the owner. Even when the piece is complete, it stays unfinished.

The artist uses the invasiveness of security cameras as the constant, every photo feels like you’re spying on a space, hidden from view—a voyeur. Once you overcome the sense of intrusion, you can focus on the majesty of being able able to see the candid day to day of an enormous array of unknown locations. Looking through these photos brought me back to a story I read last spring from The New Yorker. At the time I felt uncomfortable when reading the story of The Voyeur’s Motel, but after exploring Arnold’s work, I think I can understand it a bit better now. There’s something transfixing about being the fly on the wall, looking out from where no one suspects there to be eyes. Arnold’s piece allows one to have that experience without the guilt. However, in The Voyeur’s Motel, you can begin to understand the adrenalin of seeing what you shouldn’t and nearly being caught.

[@FFD8FFDB is an] attempt to profit on the curiosities of casual voyeurs, piquing the curiosity of people that normally wouldn’t be a peeping tom.

In terms of creating @FFD8FFDB Arnold explains that he pulls the stills from a smaller list of his known camera’s, excluding any that are private. He will crop, colour adjust, and use Wordnik to caption the tweets with characters and graphics (just for effect).

This bot–rather, piece, social statement, expression, exploration, study– really struck me. It was the only one that really felt like the creator was trying to share something meaningful through social media and a programmed bot. I haven’t used Twitter in years but I think I may come back to this one every now and again, if not simply just to use the photos in projects.

screen-shot-2016-10-15-at-1-02-35-pm
screen-shot-2016-10-15-at-1-02-09-pm screen-shot-2016-10-15-at-1-01-25-pm screen-shot-2016-10-15-at-1-01-16-pm

anson-FaceOSC

screen-shot-2016-10-13-at-9-40-06-pm

screen-shot-2016-10-13-at-9-40-39-pm

screen-shot-2016-10-13-at-9-40-57-pm

I used Rebecca Fiebrink’s Machine Learning tool, the Wekinator, to connect FaceOSC to Isadora (software used most often in live performance for video cueing / playback and some interactivity). My face is controlling the speed of the video playback, and in a sense becomes a remote control. This project for me was about plumbing – aka connecting different pieces of software to each other, enabling communication. It was also a chance for me to work with Dr. Fiebrink’s Wekinator again – a tool I am very interested in exploring further. I enjoyed this process.