diff --git a/chris_ellipse_exploration/attempt_0.png b/chris_ellipse_exploration/attempt_0.png new file mode 100644 index 0000000..deb2b3e Binary files /dev/null and b/chris_ellipse_exploration/attempt_0.png differ diff --git a/chris_ellipse_exploration/ellipse.py b/chris_ellipse_exploration/ellipse.py new file mode 100644 index 0000000..a6dac84 --- /dev/null +++ b/chris_ellipse_exploration/ellipse.py @@ -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)) + diff --git a/chris_ellipse_exploration/ellipse_test.py b/chris_ellipse_exploration/ellipse_test.py new file mode 100644 index 0000000..d56d454 --- /dev/null +++ b/chris_ellipse_exploration/ellipse_test.py @@ -0,0 +1,5 @@ +from ellipse import ellipse + +ellipse(100, 200, 1024) + +input() diff --git a/turtle.py b/turtle.py deleted file mode 100644 index 14ddfb6..0000000 --- a/turtle.py +++ /dev/null @@ -1,7 +0,0 @@ -def draw_turtle(frame): - # ... - - -for frame in animate(100): - draw_turtle(frame) - draw_bubbles(frame)