Approximating Spheres with Triangles

This is a homemade solution to a problem that probably already has very thoroughly investigated solutions - how do we make a collection of triangles look like a sphere? For me, this screams immediately of solving using recursion — if some shape looks like a sphere, then we can make it look more like a sphere.

The initial shape that we start off with is the octahedron — two pyramids with their bases glued together, and we scale it so that it fits within a unit sphere. This gives us 8 initial triangles to work with.

Edge Subdivision

We recursively generate more triangles from each face as follows. Each triangle has 3 midpoints along 3 of it's edges. We call these \( m_{01}, m_{02}, m_{12} \). We then lift these midpoints so they touch the surface of the sphere — in other words we normalise them. We can then form 4 new triangles from the midpoints and the existing vertices. It's easier to see this on a diagram:

       p0                  p0
       /\                  /\
      /  \                /  \
     /    \      =>  m01 /....\ m02
    /      \            / \  / \
   /________\          /___\/___\
  p1          p2      p1    m12  p2

Because this algorithm subdivides triangles based on the midpoints of their edges, we call this edge subdivision. Below we visualise the results of running it twice:

Looks like a huge DnD die.
Starting to look more like a sphere.

We can repeat this procedure on the triangles until we run out of memory/patience or we reach a certain depth \( d \). This algorithm generates \( 8 * 4^d \) triangles at the end. The number of triangles generated throughout the lifetime is similarly, \( 8( 4^1 + 4^2 + \ldots + 4^d ) \) which fortunately still works out to \( \Theta( 4^d ) \).

Running it with \( d = 4 \) gives something that looks a lot like a sphere, as shown below, while running it with \( d = 3 \) gives an aesthetically pleasing "chunky" sphere (first diagram on the page).

Alternatives

Midpoint Subdivision

An alternative to edge subdivision is midpoint subdivision. This is a variant which lifts only one midpoint on the triangle, and creates two new triangles. The tricky thing about this algorithm is how you define the points for the new triangles:

       p0                 p0
       .                  .
      / \                /|\
     /   \      =>      / | \
    /     \            /  |  \
   /_______\          /___|___\
  p1          p2      p1  m   p2

The algorithm should generate two new triangles \( (m, p_0, p_1) \) and \( (m, p_2, p_1) \), instead of the more natural \( (p_0, p_1, m) \), \( (p_0, m, p_2) \). In the first case, we make sure that the next subdivision would produce a diagonal cut, from \( m \) to \( (p_0, p_1) \) and \( (p_2, p_0) \), whereas in the second case the algorithm just keeps making cuts from \( p_0 \). A picture paints a thousand words:

Incorrect triangulation - just looks like 2 cones glued together.
Correct triangulation, more like a sphere.

Midpoint subdivision gives us \( \Theta( 2^d ) \) triangles, so we would expect to use a higher depth for it to look more spherical. At higher depths we see the pattern of spheres drawn by the algorithm - it contains more obtuse and scalene (thin and wide) triangles compared to that of edge subdivision, which has more equilateral-looking triangles:

Midpoint-subdivision with \( d = 6 \).

An advantage that it has over edge-subdivision is that the memory consumption can be controlled more easily because it grows in a factor of 2 rather than a factor of 4.

Centroid Subdivision

Yet another alternative is called centroid subdivision. Here, we lift the centroid \( c \) from the triangle, and then make 3 new triangles:

       p0                 p0
       .                  .
      / \                /|\
     /   \      =>      / | \
    /     \            /._c_.\
   /_______\          //_____\\
  p1          p2      p1      p2
The ASCII art doesn't quite do it justice.

This was my original solution to this problem, and as we see it doesn't quite work; there are deep grooves in the surface of the sphere that cannot be fixed even at higher depths:

\( d = 3 \), notice the deep cuts.
\( d = 4 \) still has deep cuts.

The lesson learnt here is that the subdivision strategies should avoid producing thin triangles. This is because intuitively there is more value in producing "fatter", regular triangles. Thin triangles, when subdivided are going to fill less of the sphere's surface, and it will be even worse when you subdivide thinner and thinner strips.

Hybrids

A couple more nice pictures of running hybrid algorithms - a mix between multiple algorithms:

Edge-Centroid

We switch between edge- and centroid-subdivision as the algorithm progresses - i.e. first run edge, then centroid, then edge, ... until the depth is 0.

Centroid-Edge

Similar to Edge-Centroid, but we use centroid-subdivision first. Notice how the "bias" in the algorithm has changed completely.

Midpoint-Centroid-Edge

Probably the most interesting out of all the hybrid algorithms because of the shapes produced.

Misc

You can get the code that generates these spheres here. Requires matplotlib. Usage example:

$ python spheres.py <method> <depth> <colormap>
$ python spheres.py edge 3 YlGnBu_r
$ python spheres.py centroid 3 YlGnBu_r
$ python spheres.py midpoint 3 YlGnBu_r

The colormap used for the diagrams is YlGnBu_r.