Rainbow Disks

Link to code

Square made these super cool shirts for Pride 2015 that filled in the negative space around the Square logo with rainbow bands of circles. This project is one way to automate that process.

Problem Definition

This isn't really the sphere packing problem in the pure, mathematical sense. The actual sphere packing problem aims to find an optimal solution, filling a region with the greatest number of spheres of some size possible. Instead, we only want to make a patch of circles that is relatively dense in the middle and decreases in density as you approach the edge, while avoiding the areas blocked by an image (which we represent with a 2d numpy array).


For "packing" the circles, we'll use the "growing circles" method described here. Basically, we create circles that grow until they cannot grow any more, iteratively adding tiny circles to the canvas:

block_mask := input image mapped to a 2d boolean matrix
circles := ∅
while |circles| < N
    Add tiny circle newc that does not touch block_mask to circles
    For each pair of circles i, j in circles
        If i collides with j, mark i and j as dead
    If newc is dead, remove it from circles
    For each circle c in circles
        If c is dead
        If c's radius exceeds max_radius
            Mark c as dead.
        If growing by const x does not touch block_mask
            Grow c's radius by x

To make the patch centrally dense, we take the following two measures:

  1. Pull locations for newc from a 2d normal distribution aligned with the image
  2. Vary max_radius by distance from the center of the patch (decreasing as you approach the edge)

Without further ado, some results:

Of course, we can't do all this without reproducing the original!

Color Segmentation

The final step of this would be to make it shirt-able. The version below is segmented into 7 colors (using a simple mod operation) with entropy inversely proportional to circle size, producing a slightly more gradient look than the simple segmentation step allows for.