The first step in the example Draw an Apollonian gasket in C# is to circumscribe three circles that all meet tangentially with a larger circle as shown in this example. In the previous example I glossed over how the program figures out how big the large circle should be and how the inner circles are sized and positioned. This example explains that step in greater detail.

Consider the following diagram.

The dashed triangle has corners at the inner circles’ centers. If the radius of the inner circles is `r`, then the sides of the triangles have length `2 * r`. Because all three sides have the same lengths, this is an equilateral triangle. That means all of its angles are 60 degrees.

The orange triangle’s smaller angle is half as large as the angles in the dashed triangle so it is 30 degrees. That makes the orange triangle is a 30-60-90 triangle (the angles are 30, 60, and 90 degrees). One of the properties of the 30-60-90 triangle is that `C/A = 1/2` and `C/B = 1/Sqrt(3)`. Rearranging gives you `A = 2 * C` and `C = B/Sqrt(3)`.

The distance `B` is the radius `r` of the smaller circle. The radius of the outer circle is `R = r + A = B + A`. You can use the previous equations to substitute for `A` to get:

R = B + A = B + 2 * C = B + 2 * (B / Sqrt(3)) = B * (1 + 2 / Sqrt(3))

Solving for `B` gives `B = R / (1 + 2 / Sqrt(3))`.

This gives you the radius of the inner circles in terms of the radius of the outer circle. To make the outer circle just fit the form, we need `R` to be half of the smaller of the form’s available width or height.

So the basic idea is to calculate `R` so the outer circle just fits the form and use that value to solve for `B`. Then use the other equations to solve for `A` and `C`, and use them to figure out where all of the circles’ centers are.

The following code shows how this example does that.

// Draw the circles. private void Form1_Paint(object sender, PaintEventArgs e) { e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Get the available area. float R = Math.Min( ClientSize.Width - 1, ClientSize.Height - 1) / 2; float cx = ClientSize.Width / 2; float cy = ClientSize.Height / 2; // Calculate the radius for the inner circles. float B = (float)(R / (1 + 2 / Math.Sqrt(3))); float A = (float)(2 * B / Math.Sqrt(3)); float C = A / 2; // Draw the big circle. e.Graphics.DrawEllipse(Pens.Black, cx - R, cy - R, 2 * R, 2 * R); // Draw the top inner circle. float cx1 = cx; float cy1 = cy - A; e.Graphics.DrawEllipse(Pens.Red, cx1 - B, cy1 - B, 2 * B, 2 * B); // Draw the left inner circle. float cx2 = cx - B; float cy2 = cy + (float)(B / Math.Sqrt(3)); e.Graphics.DrawEllipse(Pens.Green, cx2 - B, cy2 - B, 2 * B, 2 * B); // Draw the right inner circle. float cx3 = cx + B; float cy3 = cy2; e.Graphics.DrawEllipse(Pens.Blue, cx3 - B, cy3 - B, 2 * B, 2 * B); }

The code sets the outer circle’s radius `R` equal to half of the smaller of the form’s width and height. It subtracts 1 from the width and height so the edge of the circle isn’t clipped off the edge of the form. The code then finds the center of the outer circle `(cx, cy)`.

Next the code solves for `B`, `A`, and `C` as in the diagram. It then uses those values to draw the smaller circles.

The Apollonian gasket example calculates `r` by multiplying the smaller of the form’s width and height by `0.225`. To make the outer circle fit exactly, it should multiply that value by `1 / (1 + 2 / Sqrt(3)) / 2`. (The final `/ 2` is to convert from a form width/height into half of that to get a radius.) This value is approximately `0.232` rather than `0.225`. The Apollonian gasket example uses `0.225` so the circles don’t go all the way to the edge of the form, leaving a small margin.