[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[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: Draw a hypotrochoid (Spirograph curve) in Python

[A hypotrochoid drawn in Python]

[To draw a hypotrochoid, you roll a circle around inside another circle] An epitrochoid is traced out by a point p attached to a circle of radius b rolling around inside a circle of radius a. (See the figure on the right.) It's similar to an epitrochoid except the circle of radius b rolls around inside the other circle instead of outside it.

The shape is drawn by the following parametric equations:

    [The equations for a hypotrochoid]
The program uses a hypotrochoid_point function to evaluate those equations and a get_hypotrochoid_points function to generate points.

hypotrochoid_point

The following hypotrochoid_point function evaluates those equations for given values of a, b, h, and t. The cx and cy values give the hypotrochoid's desired center and scale gives a scale factor.

def hypotrochoid_point(a, b, h, t, cx, cy, scale): '''Calculate the coordinates for a point on the hypotrochoid.''' angle = (a - b) / b * t x = (a - b) * math.cos(t) + h * math.cos(angle) y = (a - b) * math.sin(t) - h * math.sin(angle) return cx + x * scale, cy + y * scale

The code simply evaluates the equations, scales the results, and adds them to the point (cx, cy).

To trace the entire curve, the variable t must vary from 0 to 2 π B / GCD(A, B).

get_hypotrochoid_points

The following code shows how the get_hypotrochoid_points function generates the points for a hypotrochoid.

def get_hypotrochoid_points(a, b, h, dt, cx, cy, radius): ''''Generate the hypotrochoid's points.''' # Calculate the stop value for t. stop_t = 2 * math.pi * b / gcd(a, b) + dt # Convert radius into a scale factor. max_r = a - b + h scale = radius / max_r # Find the points. points = [hypotrochoid_point(a, b, h, 0, cx, cy, scale)] t = dt while t <= stop_t: points.append(hypotrochoid_point(a, b, h, t, cx, cy, scale)) t += dt print(f'{len(points)} points') return points

The code first calculates the stopping value for t.

It then converts the radius into a scale factor. Left to their own devices, the equations above make a hypotrochoid with radius equal to a - b + h. To make the final result have radius given by the radius parameter, the code calculates a scale factor equal to radius / (a - b + h).

The function then makes a points list and enters a loop where it adds points to the list and increments t until t reaches the stopping value. It then returns the list of points.

Main Program

When you fill in values for the parameters and click Draw, the program executes the following code.

def draw(self): '''Generate and display the hypotrochoid.''' # Get the parameters. a = int(self.a_var.get()) b = int(self.b_var.get()) h = float(self.h_var.get()) dt = float(self.dt_var.get()) # Find the center and radius. self.canvas.update() wid = self.canvas.winfo_width() hgt = self.canvas.winfo_height() margin = 10 cx = wid / 2 cy = hgt / 2 radius = (min(wid, hgt) - 2 * margin) / 2 # Generate the curve's points. points = get_hypotrochoid_points(a, b, h, dt, cx, cy, radius) # Draw the points. self.canvas.delete(tk.ALL) self.canvas.create_line(points, fill='red') # Draw the radius (for debugging). # self.canvas.create_oval((cx - radius, cy - radius, cx + radius, cy + radius), outline='blue')

This code gets the input parameters and gets the canvas widget's current dimensions. It then calls get_hypotrochoid_points to generate the curve's points.

The program then clears the canvas and draws the curve's points as a single "line." Uncomment the last line if you want to draw a circle around the whole thing. (That mostly shows that the scaling works.)

Special Cases

Just a epitrochoids have special cases, so too do hypotrochoids.

[A hypocycloid drawn in Python]
Hypocycloid
h = b
[A deltoid drawn in Python]
Deltoid
a = 3 × b
h = b
[An astroid drawn in Python]
Astroid
a = 4 × b
h = b
[A pentoid drawn in Python]
Pentoid
a = 5 × b
h = b

Conclusion

An epicycloid is a special case of a hypotrochoid where h = b so the generating point lies on the edge of the outer circle.

Download the example to experiment with it and to see additional details. For example, try drawing some epicycloids so see the difference between epicycloids and non-epicycloids.

You may also find some parameter combinations where the scaling doesn't work. For example, try settig b > a.

© 2024 - 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.