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).

Solution

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
continue
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:

Pull locations for newc from a 2d normal distribution aligned with the image

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.