Compare commits

...

8 Commits

Author SHA1 Message Date
Chris Proctor
bf2d2b0774 Add demos 2025-06-26 13:00:45 -04:00
Chris Proctor
02902dac51 ADjust ellipse starting angle 2025-06-26 13:00:31 -04:00
Chris Proctor
c027ffbf9f Fixed ellipse after writing some tests to identify the problem 2025-06-26 12:27:07 -04:00
Chris Proctor
edac317460 remove random guess param 2025-06-26 11:44:20 -04:00
Chris Proctor
59ad818194 Added Chris's attempt at an ellipse function 2025-06-26 11:41:43 -04:00
Chris Proctor
882cedb841 Adding examples 2025-06-24 12:53:06 -04:00
Hope
0b609de21e I removed the easing from the bubbles because I
didn't realize how the function worked before and now realized that the function
will not animate the bubbles the way that I had envisioned.  I may try to use the easing
for my turtles swim.

I drew the bubbles and have coded the drawing of the top two flippers.  I need to complete the changes to the
flipper function for the bottom two quadrants.

Next step is animating the flippers and then the turtle.  I'm a little aprehensive about
nesting the animations, but we'll see how it goes.
2025-06-24 10:31:16 -04:00
Hope
6bc9ef9f0b I'm stuck! I tried to draw the bubbles to ease up on a loop
but when I run it, the bubbles speed up and go crazy.  I
don't know what I'm missing.  Also, when the bubbles are on a loop
it doesn't continue on to draw the turtle.  Can I run the two simultaniously?
2025-06-23 14:08:01 -04:00
9 changed files with 318 additions and 8 deletions

View File

@@ -9,13 +9,7 @@ which allows basic formatting.
## Description ## Description
(I would like to create an animated swimming sea turtle with some bubbles. (I would like to create an animated swimming sea turtle with some bubbles.)
If you want to link to an
image, move the image to this directory, and then use the following syntax:
![Description](filename.png)
)
## Planning ## Planning

34
bubbles.py Normal file
View File

@@ -0,0 +1,34 @@
from turtle import *
from superturtle.animation import animate
from random import random, seed
HOW_FAR_BUBBLES_GO = 400
def random_bubble_x():
return -200 + 400 * random()
def random_bubble_y():
return -200 + 400 * random()
def random_bubble_size():
return 10 + 40 * random()
def generate_bubble_positions(n):
bubble_positions = []
for i in range(n):
x = random_bubble_x()
y = random_bubble_y()
r = random_bubble_size()
bubble_positions.append([x, y, r])
return bubble_positions
def draw_bubbles(bubble_positions, frame):
for x, y, r in bubble_positions:
with frame.translate([x, y], [x, y + HOW_FAR_BUBBLES_GO]):
circle(r)
number_of_bubbles = 25
bubble_positions = generate_bubble_positions(number_of_bubbles)
for frame in animate(100):
draw_bubbles(bubble_positions, frame)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

View File

@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body style="background-image: url(demo.gif); background-repeat: cover;">
</body>
</html>

View File

@@ -0,0 +1,26 @@
from ellipse import ellipse
from superturtle.animation import animate
from superturtle.easing import easeInOutCubic
from turtle import color, fillcolor, begin_fill, end_fill
n_frames = 128
def is_even(n):
return n % 2 == 0
def set_colors(n):
"Sets the color and fill color to white when n is even, otherwise black."
if is_even(n):
color('white')
fillcolor('white')
else:
color('black')
fillcolor('black')
for frame in animate(n_frames, loop=True, gif_filename="demo.gif"):
for r in reversed(range(2, 30)):
set_colors(r)
with frame.rotate(0, 360, r, n_frames - r, easing=easeInOutCubic):
begin_fill()
ellipse(5 * r, 10 * r, 128)
end_fill()

View File

@@ -0,0 +1,112 @@
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()
φstart = get_angle(rx, ry, num_points, 0) / 2
fly(rx)
left(π/2 - φstart)
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)
print(i, φi, di)
left(φi)
forward(di)
left(-π/2 + φstart)
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
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
unit_basis = normalize(basis)
b0, b1 = unit_basis
len_a = dot(vec, unit_basis)
vec_a = (len_a * b0, len_a * b1)
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 normalize(vec):
"""Scales vec to magnitude 1.
"""
x, y = vec
len_vec = mag(vec)
return x / len_vec, y / len_vec
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,24 @@
# To run these tests run: python -m unittest tests
from unittest import TestCase
from ellipse import *
from math import sqrt
class TestEllipseFunctions(TestCase):
def test_normalize(self):
observed = normalize((0, 1))
expected = (0, 1)
self.assertEqual(observed, expected)
observed = normalize((2, 0))
expected = (1, 0)
self.assertEqual(observed, expected)
observed = normalize((1, 1))
expected = (1/sqrt(2), 1/sqrt(2))
self.assertEqual(observed, expected)
def test_project_onto_basis(self):
observed = project_onto_basis((4, 3), (4, 0))
expected = ((4, 0), (0, 3))
self.assertEqual(observed, expected)

View File

@@ -9,11 +9,13 @@
from turtle import * from turtle import *
from superturtle.movement import fly from superturtle.movement import fly
from superturtle.animation import animate from superturtle.animation import animate
from superturtle.easing import easeInQuad
import math import math
import random
def draw_turtle(size): def draw_turtle(size):
pencolor("green")
#draw an oval for the shell #draw an oval for the shell
height = size*2 height = size*2
width = size*1.5 width = size*1.5
@@ -52,10 +54,122 @@ def draw_turtle(size):
forward(size*1.5) forward(size*1.5)
pendown() pendown()
draw_flipper(size, 1)
draw_flipper(size, 4)
def draw_flipper(size, quad):
width = 2.5 * size
height = 1.5 * size
steps = 100 # Number of points to smooth the curve
penup()
if quad == 1:
angle_range = range(0, steps + 1) # Top half
for i in angle_range:
angle = math.pi * i / steps # Only go from 0 to π radians
x = ((width / 2) * math.cos(angle))+(size*2)
y = ((height / 2) * math.sin(angle))+size
if i == angle_range[0]:
goto(x , y )
pendown()
else:
goto(x, y)
forward (size*2.5)
elif quad == 4:
angle_range = range(0, steps + 1) # Top half
for i in angle_range:
angle = math.pi * i / steps # Only go from 0 to π radians
x = ((width / 2) * math.cos(angle))-(size*2)
y = ((height / 2) * math.sin(angle))+size
if i == angle_range[0]:
goto(x , y )
pendown()
else:
goto(x, y)
forward (size*2.5)
elif quad == 3:
angle_range = range(0, steps + 1) # Top half
for i in angle_range:
angle = math.pi * i / steps # Only go from 0 to π radians
x = ((width / 2) * math.cos(angle))-(size*2)
y = ((height / 2) * math.sin(angle))+size
if i == angle_range[0]:
goto(x , y )
pendown()
else:
goto(x, y)
forward (size*2.5)
else:
angle_range = range(0, steps + 1) # Top half
for i in angle_range:
angle = math.pi * i / steps # Only go from 0 to π radians
x = ((width / 2) * math.cos(angle))-(size*2)
y = ((height / 2) * math.sin(angle))+size
if i == angle_range[0]:
goto(x , y )
pendown()
else:
goto(x, y)
forward (size*2.5)
def draw_bubble(b_size):
pencolor("blue")
circle(b_size)
def place_bubbles(number):
#draws bubbles of random sizes and locations in each of the 4 quadrants
for i in range(number):
x = random.random()*400
y = random.random()*200
fly(x,y)
b_size = random.random()*15
draw_bubble(b_size)
for i in range(number):
x = random.random()*400
y = random.random()*200
fly(-x,y)
b_size = random.random()*15
draw_bubble(b_size)
for i in range(number):
x = random.random()*400
y = random.random()*200
fly(x,-y)
b_size = random.random()*15
draw_bubble(b_size)
for i in range(number):
x = random.random()*400
y = random.random()*200
fly(-x,-y)
b_size = random.random()*15
draw_bubble(b_size)
fly(0,0)
"""
#pass the number of bubbles you want in each quadrant (so it will be 4 X what you pass)
for frame in animate(50, loop=False):
x = frame.interpolate(-10, 10)
y = frame.interpolate(20, -10, easing=easeInQuad)
fly(x, y)
place_bubbles(1)
"""
place_bubbles(5)
draw_turtle(20) draw_turtle(20)
done() done()