[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 an epitrochoid in Python

[An epitrochoid drawn in Python]

[To draw an epitrochoid, you rotate a circle around another circle] An epitrochoid is traced out by a point p attached to a circle of radius b rolling around a circle of radius a. (See the figure on the right.)

The shape is drawn by the following parametric equations:

    [The equations for an epitrochoid]
The program uses an epitrochoid_point function to evaluate those equations and a get_epitrochoid_points function to generate points.

epitrochoid_point

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

def epitrochoid_point(a, b, h, t, cx, cy, scale): '''Calculate the coordinates for a point on the epitrochoid.''' 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 out the entire curve, the values passed into the sine and cosine functions must start at 0 and end at a multiple of 2 * Pi. That means both t and (a + b) / b * t must end at such a multiple. That happens if t ranges to b * 2 * Pi. (Assuming a and b are whole numbers.)

get_epitrochoid_points

The following code shows how the get_epitrochoid_points function generates the points for an epitrochoid.

def get_epitrochoid_points(a, b, h, dt, cx, cy, radius): ''''Generate the epitrochoid's points.''' # Calculate the stop value for t. stop_t = b * 2 * math.pi + dt # Convert radius into a scale factor. max_r = a + b + h scale = radius / max_r # Find the points. points = [epitrochoid_point(a, b, h, 0, cx, cy, scale)] t = dt while t <= stop_t: points.append(epitrochoid_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 an epitrochoid 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 epitrochoid.''' # 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_epitrochoid_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_epitrochoid_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

You can set the parameter values to create a few special cases of epitrochoids.

[A nephroid drawn in Python]
Nephroid
a = 2 × b
h = b
[A dimpled limaçon drawn in Python]
Dimpled Limaçon
a = b
h < b
[A cardioid drawn in Python]
Cardioid
a = b = h
[A looped limaçon drawn in Python]
Looped Limaçon
a = b
h > b

The word limaçon, pronounced "lee-mah-sahn," is French for "cochlea."

Conclusion

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

In my next post, I'll show how you can draw a similar family of curves called hypotrochoids.

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