/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.E0ohiB$D7c6/rev.5181
*
* authors:
* John Conwell
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
/*
* NOTE: !!!!Its working now, YAY!!!
*
* This is a rough port of the Processing library of the same name
* written by Michael Ogawa (http://code.google.com/p/wordookie/)
*
* Usage: you only need to do two things if you want to change things
* First: change the TEXT_ALIGNMENT variable below to one of
* the 6 options.
* Second: change the json object to include the words you want
* to be Wordookied. The weight is just a relative weight that
* gets normalized, so use any weight that makes sense to you.
* The type key isnt used yet.
*
* Another Note: currently it tried 500 times to randomly pick
* a blank place to write a word. After 500 tries, it just slaps
* it in, otherwise it could run for a long time
*/
var ANYWAY = 1;
var HORIZONTAL = 2;
var MOSTLY_HORIZONTAL = 3;
var HALF_AND_HALF = 4;
var MOSTLY_VERTICAL = 5;
var VERTICAL = 6;
int minFontSize = 30;
int maxFontSize = 70;
color backgroundColor = color(0);
color fontColor = color(204, 102, 0);
var fontName = "FFScala.ttf";
PFont wordFont;
var TEXT_ALIGNMENT = MOSTLY_HORIZONTAL;
var wordList = {"words": [
{"text": "one", "weight": "70", "type": "2"},
{"text": "two", "weight": "40", "type": "1"},
{"text": "three", "weight": "60", "type": "2"},
{"text": "four", "weight": "45", "type": "1"},
{"text": "five", "weight": "40", "type": "2"},
{"text": "six", "weight": "25", "type": "1"},
{"text": "seven", "weight": "70", "type": "2"},
{"text": "eight", "weight": "40", "type": "1"},
{"text": "nine", "weight": "60", "type": "2"},
{"text": "ten", "weight": "45", "type": "1"},
{"text": "eleven", "weight": "40", "type": "2"},
{"text": "twelve", "weight": "25", "type": "1"},
{"text": "thirteen", "weight": "70", "type": "2"},
{"text": "fourteen", "weight": "40", "type": "1"},
{"text": "fifteen", "weight": "60", "type": "2"},
{"text": "sixteen", "weight": "45", "type": "1"},
{"text": "seventeen", "weight": "40", "type": "2"},
{"text": "eighteen", "weight": "25", "type": "1"},
]
};
PFont font;
PGraphics buffer;
void setup() { // this is run once.
//width, height
size(700, 600);
// limit the number of frames per second
frameRate(30);
wordFont = loadFont("Meta-Bold.ttf");
//setup graphics buffer
buffer = createGraphics(700, 600, JAVA2D);
buffer.background(backgroundColor);
buffer.smooth();
buffer.textMode(SCREEN);
wordList.nextWordToDraw = 0;
parseMinMaxWeights(wordList);
println(wordList.minWeight + ", " + wordList.maxWeight);
}
void draw() { // this is run repeatedly.
var index = wordList.nextWordToDraw;
if (int(index) < wordList.words.length) {
var word = wordList.words[index];
calcLayoutOfWord(word, TEXT_ALIGNMENT);
buffer.fill(fontColor);
paintWord(word, buffer);
image(buffer, 0, 0);
wordList.nextWordToDraw = index + 1;
}
else {
println("finished Wordookie layout");
noLoop();
}
}
void paintWord(Word word, PGraphics graphics) {
graphics.beginDraw();
graphics.pushMatrix();
graphics.translate(word.x, word.y);
graphics.rotate(word.angle);
graphics.textFont(wordFont, word.fontSize);
graphics.textAlign(CENTER, CENTER);
graphics.text(word.text, 0, 0);
graphics.popMatrix();
graphics.endDraw();
}
void parseMinMaxWeights(words) {
var minWeight = 9999999;
var maxWeight = -9999999;
for(var i = 0; i< words.words.length; i++){
// update min/max weights
var weight = words.words[i].weight;
if ( weight < minWeight )
minWeight = weight;
if ( weight > maxWeight )
maxWeight = weight;
}
//sort list largest to smallest to have better chance of finding a
//spot for big words
words.words.sort(sortByWeight);
words.minWeight = minWeight;
words.maxWeight = maxWeight;
}
var centerPoint = new Object;
void calcLayoutOfWord(word, angleType) {
//set font size
word.fontSize = int(map(word.weight, wordList.minWeight, wordList.maxWeight, minFontSize, maxFontSize));
buffer.textFont(wordFont, word.fontSize);
//position the word
makeInitialPosition(word, angleType);
centerPoint.x = int(word.x);
centerPoint.y = int(word.y);
int tries = 1;
while (intersects(word)) {
tries++;
makeInitialPosition(word);
if (tries == 500)
break;
}
println("Found spot for " + word.text + " in " + tries + " tries");
}
void makeInitialPosition(word, angleType) {
//set the word angle
word.angle = generateAngle(angleType);
//set word height and width
var dim = wordHalfDimensions(word);
float x, y;
do {
x = (float)rnd_snd() * width/8 + width/2;
y = (float)rnd_snd() * height/8 + height/2;
}
while( x > width - dim.width || x < dim.width
|| y < dim.height || y > height - dim.height );
word.x = (float)x;
word.y = (float)y;
}
int generateAngle(angleType) {
switch(angleType) {
case ANYWAY:
return (float)( Math.random() * PI - HALF_PI );
case HORIZONTAL:
return 0f;
case MOSTLY_HORIZONTAL:
if ( Math.random() < 0.75 )
return 0f;
else
return -HALF_PI;
case HALF_AND_HALF:
if ( Math.random() < 0.5 )
return 0f;
else
return -HALF_PI;
case MOSTLY_VERTICAL:
if ( Math.random() < 0.25 )
return 0f;
else
return -HALF_PI;
case VERTICAL:
return -HALF_PI;
}
return 0f;
}
Object wordHalfDimensions(Word word) {
buffer.textFont(wordFont, word.fontSize);
float wWidth = buffer.textWidth(word.text) / 2 + 1;
float wHeight = buffer.textAscent();
// tilted word dimensions
float aAngle = word.angle;
float bAngle = word.angle > HALF_PI ? word.angle - HALF_PI : HALF_PI - word.angle;
float cosA = abs( cos( aAngle ) );
float cosB = abs( cos( bAngle ) );
float sinA = abs( sin( aAngle ) );
float sinB = abs( sin( bAngle ) );
float e = wWidth * cosA;
float f = wHeight * cosB;
float h = wWidth * sinA;
float g = wHeight * sinB;
float aWidth = e + f;
float aHeight = h + g;
var dim = new Object;
dim.width = int(aWidth);
dim.height = int(aHeight);
return dim;
}
boolean intersects(Word word) {
// test each pixel
// optimize by searching within new word's bounding box
var dim = wordHalfDimensions(word);
int xStart, xEnd, yStart, yEnd;
int sideBuffer = 3;
xStart = (int)( word.x - dim.width - sideBuffer );
xEnd = (int)( word.x + dim.width + sideBuffer );
yStart = (int)( word.y - dim.height - sideBuffer );
yEnd = (int)( word.y + dim.height + sideBuffer );
// sanitize bounds
yStart = max( yStart, 0 );
xStart = max( xStart, 0 );
yEnd = min( yEnd, buffer.height );
xEnd = min( xEnd, buffer.width );
// test each pixel for overlap
// note: using pixels[] is faster than get()
buffer.loadPixels();
for( int y = yStart; y < yEnd; y++ ) {
for( int x = xStart; x < xEnd; x++ ) {
color c1 = buffer.pixels[ y * buffer.width + x ];
if (c1 != backgroundColor) {
return true;
}
}
}
return false; // no intersection found
}
/*
* returns an approximation of random.nextGaussian, where the mean is 0,
* and std dev of 1
*/
function rnd_snd() {
return (Math.random()*2-1)+(Math.random()*2-1)+(Math.random()*2-1);
}
/*
* sort words largest to smallest
*/
function sortByWeight(word1, word2) {
return word2.weight - word1.weight;
}