I wanted to tile the Mandelbrot set with a hyperbolic tiling. I thought
about various methods, and decided to do one using circle packings,
which happens to be a third a subject I like. So with 3 subjects I like
coming together, a cool project was born. This web page is to
give a bit more explanation than just the pictures themselves.
I chose as title 'Poincare meets Mandelbrot'. Although Poincare was not
the first to discover the 'Poincare disc', he is easily cool
and
famous enough to still be in the title. (For example, he discovered
chaos in the 3 body problem)
One way to describe what the Poincare
disc is, is to take a
triangle formed by circle segments, whose angles at the
vertices are whole number divisions of 2π,
for example in the above picture I use
(π/7, π/3, π/2). Then you
generate new
triangles by repeatedly inverting triangles in one of of the sides,
using circle
inversion.
If (π/a + π/b + π/c) > π,
then you get an elliptic
tiling.
If (π/a + π/b + π/c) =
π, then you get a
parabolic tiling
If (π/a + π/b + π/c) <
π, then you get a
hyperbolic tiling
Check out another web page of
mine
for many examples of these.
We have π/7 + π/3 + π/2 = π*41/42, so that
is hyperbolic.
In fact, it is the closest of all hyperbolic tilings to being
non-hyperbolic, which is why it is probably the prettiest. The others
tend to have large differences in sizes of neighboring triangles.
For hyperbolic tilings, it turns out that all the triangles end up a
disc, which they
asymptotically fill: near the edge the triangles become infinitely
small.
That is the Poincare disc. It has many more interesting properties, but
here we just focus on tilings.
The Mandelbrot
set is a famous fractal. It
has an inifinitely ddetailed
structure, yet is generated by a very simple formula: z -> z2
+ z0
z is complex number, but you can interpret the coordinates of pixel on
you screen as the components (x0,y0)
of the
complex number z0=
(x0+iy0).
You start iterating z -> z0
-> z2
+ z0
-> (z2
+ z0)2
+ z0->
((z2
+ z0)2
+ z0)2
+ z0 ->...
Or, splitting up z into its components
x -> x0 ->
(x2
- y2)
+ x0
->...
y -> y0
-> (2xy) + y0
->...
Doing this iteration for a number of steps, you can end up going to
infinity. In fact, once you get larger than (x2
+ y2)
=4, you can conclude that you are doomed to 'escape' to infinity.
The Mandelbrot set is the set of all points that do not escape
to infinity. It is
not so hard to write a computer program that generates the Mandelbrot
set. An extra thing you can do is note the number of iterations it took
for you to conclude that it would escape to infinity. This gives the escape rings.
On the 3D
print photo at the top of the page, you can see these some of these
rings. We will use them later.
To map the Poincare disc onto the interior of the Mandelbrot set, we
can use circle
packings. The idea of a
circle packing, is to create a
connectivity network of circles and then demand that they all touch.
You are allowed to vary the radii. It turns out that solutions exist
for a wide range of situations.
To solve the packing, one algorithm is to calculate the angular deficit
at each
circle. In the figure below, we chose a particular circle, and
constructed triangles from lines from its scene to the centers of its
neighbors. We know the lengths of these lines: They are just ri+rj
for circles i and j. From the lengths we can calculate the angles of
the triangles using the cosine law.:
cos(alfa12)
= ( (r0+r1)2
+
(r0+r2)2)
- (r1+r2)2
) / (2 (r0+r1)
((r0+r2))
If the circles all touch, then the sum of the angles at each vertex,
should be 2π.
If it is larger, we shrink the circle, if it is smaller, we expand the
circle. That process seems to converge well to a solution, in all cases
I know.
On the edges
the
angles do not need to sum, so we can think of something else. In our
case, we are going to tell them to be in a
particular escape ring of
the Mandelbrot. How do we
know? Well, we just take the
coordinates of the circle center, plug them in the Mandelbrot
iteration, and see how long it takes them to escape to infinity. If it
takes too long, make the circle larger, and vice versa.
Above is an animation I made years ago, showing how circle packings can
adapt to shape.
For more on circle packings, check out the web page of Kenneth
Stephenson.
Creating a (7,3) connectivity
network.
To get going, we need to tell each circle what its neighbors are. This
seems initially quite messy, but the solution is nice, involving
Fibonacci numbers.
Looking at the (7,3) tiling, we note that it can be organized into
concentric rings of heptagons (circles and heptagons are interchanged
here). I call each ring a Generation. Generation(0) is the central
heptagon. Generation(1) is a ring of 7 heptagons.
In Generation(2), we notice there are 2 different neighbor
structures for the heptagons:
Type A has 1 'parents', 2 'sisters', and 4 'children' of who 1 type B,
2 type A ,and a type B which we don't count to avoid double counting
children with 2 parents'.
Type B has 2 'parents', 2 'sisters', and 3 'children' of who 1 type B,
1 type A ,and a type B which we don't count to avoid double counting
children with 2 parents'.
Generation(1) was all Type A.
To count how many we get for each generation, we write down:
Ai+1
= 2*Ai
+ Bi
Bi+1
= Ai
+ Bi
If we initialise with 1,0 we get Fibonacci numbers (with a bit of
effort, you can prove this):
So our generations will contain 7-folds of Fibonacci numbers of members.
You can derive the total number of heptagons:
nHepta = 1+7 * (Fibonacci(2 * (Ngenerations) + 1) - 1)
If we number the members of a generations counterclockwise, this is a
number scheme that will navigate us through neighbor
administration hell.
Lets Go!
If we bias the edge cells to be on a circle of a given circle, we get
the Poincare disc:
To make things a bit easy for our cells, lets first tell them to go to
a low escape ring, say 7. This escape rings get more and more twisty
and complex as they get higher (closer to the Mandelbrot).
One thing visible in the above picture is that in this case the number
of cells is too small to penetrate into the main filament.