Bodypaint Generator: Just draw some spots

Now that I have all the photos from the Solstice Cyclists organized, I can start looking at cyclists, breaking down their paintjobs, and creating generators for them.

There are many ways I could implement the generator. Do I make an interactive webpage (Javascript, SVG) so that the public can create and modify paintjobs?  Since people are complicated three-dimensional objects, maybe I should use a 3D program, like Unity. To keep it simple, I’m using SVG.js to draw patterns on a square in a web browser. I’ll teach it about human bodies later.

The first cyclist I photographed just had some spots. (Here’s a photo. content warning: nudity)  How hard can drawing some spots be, right?  Harder than I thought.

Spots are mostly the same size, but not exactly,  so the generator has baseSize and sizeVariation parameters. I defined some constants for colors and had the generator pick a few each time.  Now it just has to place those spots randomly on the canvas.

Figure 1. Randomly placed spots.
Figure 2. randomly placed spots

This is not how a human would draw spots.  Some spots overlap, and the distribution of spots is very uneven.  Humans tend to draw spots roughly the same distance from each other, but will avoid placing the spots in a grid.  How can I imitate that behavior?

  1. define a reasonable distance between spots, but vary it a bit (baseDistance, distanceVariation)
  2. When placing a new spot, start from the position of the previous spot & move some distance in a random direction.
  3. Make sure that position is still on the canvas (bounds checking)
  4. Make sure that position isn’t too close to any other previous spot
  5. If placement fails, repeat step 2 again.
  6. If placement fails too many times, give up.

After writing a few helper functions for polar coordinates, I tried again.  I added a small white square to indicate an attempted placement that was rejected.

Figure 3. Place next spot near previous spot.

The spots don’t overlap or crowd each other anymore, but the algorithm tends to make chains of spots instead of filling regions.

Figure 4. Having trouble placing spots.

In Figure 4, the algorithm tried hundreds of times to place the last two spots. It should have given up after 10 attempts, but this bug accidentally visualizes the band of acceptable distances between spots.

In order to make the spots form clumps instead of chains, I changed where I tried to place the next spot.  Instead of placing near the previous spot, I’d try to place it near the first spot. If that failed a number of times, the region around the first spot must be too crowded, so I’ll try near the second spot, then the third, and so on.

Figure 5. A clump of spots
Figure 6. Failed placements visualized

This looks more like what I would produce if I was drawing spots on a piece of paper, or someone’s skin. Figure 6 shows that the algorithm fails to place a spot quite often, but the execution is still fast enough to seem real-time to a human observer. I may have to worry about efficiency later, but for now it’s fine.

This algorithm has a maximum number of spots it attempts to place. If it can’t find an open space near any of the existing spots, it will give up early, but it also doesn’t keep going to fill the entire canvas. Fortunately, that’s an easy fix. Instead of looping a set number of times, use while(true), and then the emergency break statement becomes the normal way of exiting.

Figure 7. Enough spots to fill the canvas.
Figure 8. Enough spots to fill the canvas.

Now that I like the arrangement of spots, I can easily switch out the circles for anything with roughly the same dimensions: letters, stars, squares, or flowers.  This algorithm won’t work for objects that are significantly bigger in one dimension than another, like fish, words, or snakes.

In conclusion, I can fill a space with random spots in a way that imitates how a human would do it, which isn’t that random at all.