thinkCutie: Process

How to share your heart 5.6 million times

Douglas Dollars
Generative Art
6 min readFeb 4, 2014

--

This is a process post for the piece I created which I call thinkCutie. I think it’ll be a good idea going forward that any time I make a new or large-enough piece that I also write about what went into making it.

Building the image came in four major components, applying shapes or imagery along the path of another image, the hearts, the type, and the generation of the output images.

the Noun Project contains thousands of vector images, generally simplifying our visual language across the web and in print.

Finding imagery

For this project I knew that I wanted to compose the thinkITEM image out of hearts, and have some text overlaid in the ‘chest’ area of the photo both as logo and as part of the piece itself. Not yet having a scanner or Illustrator to draw the shapes I wanted, I found many hearts through The Noun Project which fit the theme.

the image used for mapping shapes atop

Creating a path for shapes to sit upon

I attached a layout to the drawable pool (HDrawablePool.layout), and specified that it would take its Shape Layout (HShapeLayout) from the thinkITEM mask image. HShapeLayout works in such a way that any solid (read: non-transparent) pixel can be used as a map or a ‘solid’ point of reference.

Making sure that my reference/mask was the same size as my desired output (800 × 1000 pixels) seemed like a good plan for ensuring accurate placement. I haven’t experimented with using an over- or under-scaled map for reference imagery, but it might prove interesting.

 pool = new HDrawablePool(7000); // we want 7000 hears draw
pool.autoAddToStage()
.layout(
new HShapeLayout()
.target(
new HImage(“cutieITEM.png”) // map to the cutieITEM image
)
)

Finding hearts

As I now had a pile of different heart shapes to use, I had to tell the script to add them to the pool of items to be selected from when drawing. Inside the ‘pool’ call above, I specified:

 .add(new HShape(“icon_822.svg”),2)
.add(new HShape(“icon_1308.svg”))
.add(new HShape(“icon_5169.svg”))
.add(new HShape(“icon_15259.svg”),2)
.add(new HShape(“icon_17452.svg”))
.add(new HShape(“icon_29424.svg”),2)

The ‘,2’ in some instances tells the script to add that many of the object to the pool, in the form of a ratio. For example if I specified ‘,20’ when selecting an image at random later in the script there would be 20 of that item represented for each 1 of those not increased. This helped make sure that certain heart images I preferred the look of rose to visual promience, while not waiting for the randomness gods to shine their light on me while simultaneously ensuring that the same single heart image wouldn’t be the only one represented.

Different hearts

I now had a large pool of hearts, but needed to keep their presentation visually interesting. As they were vector imagery, Processing (with Hype) can apply a large number of effects on their filling, strokes, and scale, affecting their presentation wildly.

.onCreate(
new HCallback() {
public void run(Object obj) {
HShape d = (HShape) obj;
d
.enableStyle(false)
.strokeJoin(ROUND)
.strokeCap(ROUND)
.noStroke()
.anchorAt(H.CENTER)
.rotate( (int)random(72) * 5 )
.size( (int)random(5,35) )
;
d.randomColors(colorsHearts.fillAndStroke());
}
}
)
.requestAll();

This might look confusing, but we’re only telling Processing what to do to the vector (shape/path) images. We tell it to ignore the styles potentially saved in the image, to join the points where lines meet and the ends of lines in a rounded fashion, to ignore any pre-created stroke or outside line to images, and a few other settings. No matter how our vector or SVG file was saved, we’ve cleared away most of its potential nonsense.

To introduce randomness we allow a random rotation to the images in 5° increments, rather than just straight images as they were saved, and set the size of the hearts to be drawn randomly between five and thirty-five pixels. Each time the program would attempt to draw another heart it would run through this small callback, choosing a new random rotation, random size, as well as random colors for the interior and outer tracing of the hearts from a pool.

Swimming in the color pools

A quick note on color selection. Both the type and hearts were colored by selecting color randomly from a pool. One pool of color was set for the hearts, and another for type.

A single red color would mean we don’t see the individual hearts and instead just see one jumbled red mess, and similarly, entirely random coloring would be even more ugly. Using Hype with Processing I build a color pool of hexidecimal color values, and similar to the weighting of vector images above, I made sure that more “true” heart colors were injected in the mix than statistically would happen as individual representations in the pool.

Saying hello to type

Type was included through calling the Processing Font class, PFont in the setup call

PFont type = createFont(“Bello-Pro.otf”, 120);

PFont has great support for smoothing and specifying type size, and seems to be able to read every font file I’ve been able to feed it.

Further in the script, I needed to specify the text to be used. As thousands of hearts would be drawing in rapid fire in a pool atop an image, and I only wanted a single instance of text to be present, I had to create a new drawable pool. Creating a new pool allowed me to also set controls for the type that wouldn’t interfere with the hearts.

typePool = new HDrawablePool(1);
typePool.autoAddToStage()
.add (new HText( “Cutie”, 260, type ))
.onCreate (
new HCallback() {
public void run(Object obj) {
HText t = (HText) obj;
t.fill( colorsText.getColor() );
t.scale( ((float)random(1,1.35)) );
t.anchorAt(H.CENTER);
t.loc( (width/2), ((float)random(810,830)) );
}
}
)
.requestAll();

Through setting the scale of the text to a random size between 1 and 1.35×, each time the code would compile a new value would be set, additionally setting the location along the x-axis to the middle of the image, while vertically between 810-830 pixels meant that it wouldn’t end up in the way of the overall image and maintained a nice random ‘range’. Variability is now present, but won’t get out of hand.

Dinner for 700 guests

Now that I was able to keep the image in the creative space between recognizability and randomness, producing results I found appealing each time, it was now ahead of me to generate the work for each of thinkITEM’s Twitter followers. Nearly 700 people.

I was able to save images via the saveFrame command, which lets me place the output to drive as well as the screen. It respects paths both relative to your build directory as well as your system paths.

saveFrame(“cuties/thinkCutie.png”);

But what about multiple images?

This is where things got ugly. See, I haven’t been creating generative art for long, and most of what is described above was cobbled together in the last couple weeks. That part was now going fine, but I had no idea how to set this program on a loop and have it spit out image after image. The thought of having to hit run, move the resulting image to another folder, and rename it was bad, but having to do it seven hundred times was a non-starter.

So I hacked it together with a loop.

int x = 1;
void draw() {
setup();
if (x < 25) {
H.drawStage();
saveFrame(“cuties/thinkITEM-###.png”);
x++;
} else {
noLoop(); // stop the output
}

Draw() is the main rendering command in Processing. By default if it runs, it will keep looping until given the noLoop() command. Processing began running this program incredibly slow after about 20 iterations and would take days to get to 700 images, so I decided to have it loop to 25 and end. From then I could re-run the program, and scoop up another set of 25 images.

As long as ‘x’ was less than 25, that the draw() command would keep drawing to the stage, and the output images would be saved in sequence into a directory named ‘cuties’.

All combinations taken into account, something in the range of 15 billion generations would be required to run to get a true duplicate. It was an interesting piece to make, and I’m looking forward to what comes next.

The full source is available in my Processing Github repository. I’ll continue updating this category on generative art as well as the code/idea sketch repository as I create new work.

If you have any questions about how I made this piece, or Processing, definitely get ahold of me! I’m still in the early stages of learning, but it’s nice to share ideas with people.

Your pal,
Douglas / @theDoug

--

--

Douglas Dollars
Generative Art

North America’s second-greatest investor in interesting projects, deeply trusted technical consultant, and possessing characteristics strangers cite as genuine.