Added Chris's attempt at an ellipse function

This commit is contained in:
Chris Proctor 2025-06-26 11:41:43 -04:00
parent 882cedb841
commit 59ad818194
4 changed files with 107 additions and 7 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,102 @@
from math import pi, sqrt, sin, cos, atan
from turtle import penup, pendown, forward, left, radians, degrees
π = pi
def ellipse(rx, ry, num_points, points_to_draw=None):
"""
Draws an ellipse with x-radius rx and y-radius ry,
with num_points control points. Optionally specify
points_to_draw if you want only a partial ellipse.
Assumes the turtle starts at the center, pointed in
the +x direction, and ends in the same position and heading.
"""
radians()
fly(rx)
left(π/2)
for i in range(points_to_draw or num_points):
φi = get_angle(rx, ry, num_points, i)
di = get_distance(rx, ry, num_points, i)
left(φi)
forward(di)
left(-π/2)
fly(-rx)
degrees()
def fly(distance):
"""Like forward, but with the pen up.
"""
penup()
forward(distance)
pendown()
def get_angle(rx, ry, n, i):
"""Returns the angle φi, or how far left the turtle should
turn at point i of n.
"""
coords_prev = get_coordinates(rx, ry, n, i - 1)
coords = get_coordinates(rx, ry, n, i)
coords_next = get_coordinates(rx, ry, n, i + 1)
vec_prev = get_vector(coords_prev, coords)
vec_next = get_vector(coords, coords_next)
vec_parallel, vec_perpendicular = project_onto_basis(vec_next, vec_prev)
φi = atan(mag(vec_perpendicular) / mag(vec_parallel))
return φi/3
def get_distance(rx, ry, n, i):
"""
Returns the distance from point i to point i + 1.
"""
coords = get_coordinates(rx, ry, n, i)
coords_next = get_coordinates(rx, ry, n, i + 1)
vec_next = get_vector(coords, coords_next)
return mag(vec_next)
def get_coordinates(rx, ry, n, i):
"""Returns the (x, y) coordinates of point i for the ellipse
specified by rx, ry, and n.
Normally, we can get the coordinates of a point on the circle
at angle θ as (r * cos(θ), r * sin(θ)). An ellipse is a circle
scaled differently along the x- and y- axes, so we need to
apply different scaling factors rx and ry.
"""
θi = 2*π*i/n
xi = rx * cos(θi)
yi = ry * sin(θi)
return xi, yi
def get_vector(start, end):
"""Returns a vector from the start point to the end point.
"""
x0, y0 = start
x1, y1 = end
return (x1 - x0, y1 - y0)
def project_onto_basis(vec, basis):
"""Projects a vector onto a basis vector, returning two vectors:
the component which is parallel to basis (vec_a) and the component which
is perpendicular (vec_b).
"""
v0, v1 = vec
b0, b1 = basis
len_a = dot(vec, basis)
vec_a = (len_a * v0, len_a * v1)
vec_b = get_vector(vec_a, vec)
return vec_a, vec_b
def dot(vec0, vec1):
"""Computes the dot product of vec0 and vec1.
The result is the magnitude of vec0 projected onto vec1.
"""
x0, y0 = vec0
x1, y1 = vec1
return x0 * x1 + y0 * y1
def mag(vec):
"""Returns the magnitude, or length, of a vector.
We can simplify the familiar formula sqrt(x*x + y*y)
using the dot product.
"""
return sqrt(dot(vec, vec))

View File

@ -0,0 +1,5 @@
from ellipse import ellipse
ellipse(100, 200, 1024)
input()

View File

@ -1,7 +0,0 @@
def draw_turtle(frame):
# ...
for frame in animate(100):
draw_turtle(frame)
draw_bubbles(frame)