[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: Make a ring of tangent circles inside another ring of tangent circles in Python

[Seven pink circles inside seven green circles]

My earlier post Make a ring of small, tangent circles inside a larger circle in Python explained how to create a ring of small, tangent circles inside a larger circle. After seeing that result, I wondered how hard it would be to make a ring of even smaller circles inside that one. It turns out it's a bit harder but it's still manageable if you know the law of cosines and the quadratic formula.

Finding the Inner Circles

[Measuring the circles] Consider the picture on the right which shows part of a ring of green circles with a ring of red circles inside it. The circles' radii and distances from the rings' centers are r/R and g/G respectively.

To make the following equations simpler, let's abbreviate c = cos(α) and s = sin(α).

The first equation is simple. (In these equations, I'm highlighting the unknown values R and r in red so it's easy to see which values are known and which are mysteries to be solved.)

[Calculating circle sizes and positions, equation 1]

The second equation is trickier. Looking at the triangle formed by the dashed lines in the picture, the law of cosines says:

[Calculating circle sizes and positions, equation 2]

If you don't remember the law of cosines (I don't), see this Wikipedia article.

Now it's just a matter of algebra. First we plug in the value for R in terms of r:

[Calculating circle sizes and positions, equation 3]

Expanding this out gives:

[Calculating circle sizes and positions, equation 4]

Now we can group the r terms:

[Calculating circle sizes and positions, equation 5]

Now let:

[Calculating circle sizes and positions, equation 6]

Now we can use the quadratic formula to find r:

[Calculating circle sizes and positions, equation 7]

Calculating the Inner Circles

The following get_nested_circles function returns a list holding the centers and radii of the nested circles.

def get_nested_circles(n, cx, cy, G, g): alpha = 2 * math.pi / n / 2 s = math.sin(alpha) c = math.cos(alpha) A = 1/s**2 - 1 B = -2 * G * c / s - 2 * g C = G**2 - g**2 r1 = (-B + math.sqrt(B**2 - 4 * A * C)) / (2 * A) r2 = (-B - math.sqrt(B**2 - 4 * A * C)) / (2 * A) r = r2 R = r / s centers = [] theta = -math.pi / 2 + alpha dtheta = 2 * math.pi / n for i in range(n): x = cx + R * math.cos(theta) y = cy + R * math.sin(theta) centers.append((x, y, r)) theta += dtheta return centers

This function first creates variables holding the various values used in the earlier equations. It sets values for alpha, s, c, A, B, and C. It then uses the quadratic formula to calculate the two possible solutions r1 and r2.

The smaller solution is the one we want, so the code sets r = r2 and uses it to calculate R.

Next, the function sets theta equal to the angle to the first nested circle's center. The value -math.pi / 2 makes the angle point upward. The additional alpha offsets the first circle so its center angle is halfway between the center angles of two of the outer ring of circles.

The function then uses a loop to generate the n circles and adds their centers and radii to the circles list. When it's done, it returns the list.

Drawing the Circles

Here's the code that draws the circles.

# Draw the big circle. rect = (cx - R, cy - R, cx + R, cy + R) dr.ellipse(rect, outline='black') # Draw the smaller inside circles. centers = get_inner_circles(n, 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) # Get next level. d = math.dist((cx, cy), (x, y)) centers = get_nested_circles(n, cx, cy, d, r) for x, y, r in centers: rect = (x-r, y-r, x+r, y+r) dr.ellipse(rect, outline='red', fill='pink', width=2)

[20 circles inside 20 larger circles] This snippet first draws the outer black circle. It then calls get_inner_circles (described in my previous post) to get the positions and radii of the smaller green circles and uses a loop to draw them.

The code then finds the distance between the center of the black circle (cx, cy) and the last green circle's center (x, y). That gives it the distance d in the picture and the equations shown earlier.

The program then calls get_nested_circles to get the data about the red circles and uses another loop to draw them.

Conclusion

Drawing a ring of circles inside an outer ring is a bit harder than drawing circles inside a circumscribed circle, but it's still doable. Download the example to experiment with it and to see additional details.
© 2025 - 2026 Rocky Mountain Computer Consulting, Inc. All rights reserved.