[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky] [Facebook]
[Build Your Own Python Action Arcade!]

[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Place three small circles inside a larger circle so they are all tangent in Python

[Three green circles inside a pink circle]

The first step in making an Apollonian circle packing (which I'll get to in a later post) is to place three small tangent circles inside a larger circle as shown in the picture at the top of this post.

Finding the Inner Circles

[Measuring the circles] Let R be the radius of the outer circle, let r be the radius of the smaller circles, and consider the picture on the right. It's pretty obvious that angle ∠BAC is 30°, so triangle △ ACB is a 30-60-90 right triangle.

The distance from the big circle's center to its circumference is R, so the distance from that center to the center of the smaller circle at point A is R - r.

Because the triangle is a 30-60-90 triangle, the ratio of the side lengths gives us this:

[These equations find r in terms of R]

Now that we know R, we can find the centers of the three smaller circles. Let C = (cx, cy).

The X coordinate of the center of the upper circle is cx. Its Y coordinate is cy minus (R - r).

The other two circles have center X coordinates equal to cx ±r.

The Y coordinate of those centers equals cy plus d (see the picture with the measurements on it). But d is the same as distance BC. Because ACB is a 30-60-90 triangle, we know:

[These equations find BC in terms of R and r]

That means the Y coordinate of the two lower circles' centers is cy + (R - r) / 2.

The following get_inner_circles function returns the centers and radii of the inner circles.

def get_inner_circles(cx, cy, R): '''Find the three circles inside the circle at (cx, cy) with radius R.''' r = R / (1 + 2 / math.sqrt(3)) return [ (cx, cy - (R - r), r), (cx - r, cy + (R - r) / 2, r), (cx + r, cy + (R - r) / 2, r), ]

Drawing the Circles

When the program starts, it calls the following method to draw the four circles.

def draw_circles(self): '''Draw a big circle with three smaller circles inside it.''' # Create a PIL image to hold the drawing. wid = hgt = 400 transparent = (0, 0, 0, 0) image = Image.new('RGBA', (wid, hgt), transparent) dr = ImageDraw.Draw(image) margin = 10 R = min(wid, hgt) / 2 - margin cx = wid / 2 cy = hgt / 2 # Draw the big circle. rect = (cx - R, cy - R, cx + R, cy + R) dr.ellipse(rect, outline='red', fill='pink', width=5) # Draw the smaller inside circles. centers = get_inner_circles(cx, cy, R) for x, y, r in centers: rect = (x-r, y-r, x+r, y+r) dr.ellipse(rect, outline='green', fill='lightgreen', width=2) # Display the image. self.canvas.config(width=wid, height=hgt) self.photo_image = ImageTk.PhotoImage(image) self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo_image) # Save the image. image.save('circles.png')

The code first creates a PIL image with a transparent background. It finds the center and radius R for the larger circle and draws that circle.

Next, the code calls get_inner_circles to get the inner circles' centers and radii. It loops through that information to draw the inner circles.

The code converts the image into a ImageTk.PhotoImage and displays it on the program's Canvas widget. It finishes by saving the image into the file circles.png.

Conclusion

Finding the three inner circles is the first step in Apollonian circle packing, which I'll describe in a future post. Meanwhile, download the example to experiment with it and to see additional details.
© 2025 - 2026 Rocky Mountain Computer Consulting, Inc. All rights reserved.