generated from mwc/project_drawing
Compare commits
11 Commits
514af40b47
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf2d2b0774 | ||
|
|
02902dac51 | ||
|
|
c027ffbf9f | ||
|
|
edac317460 | ||
|
|
59ad818194 | ||
|
|
882cedb841 | ||
|
|
0b609de21e | ||
|
|
6bc9ef9f0b | ||
|
|
c82a594a05 | ||
|
|
4de3e0b03b | ||
|
|
b0198e03d7 |
13
README.md
13
README.md
@@ -9,13 +9,12 @@ which allows basic formatting.
|
|||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
(Describe your goal for this project. If you want to link to an
|
(I would like to create an animated swimming sea turtle with some bubbles.)
|
||||||
image, move the image to this directory, and then use the following syntax:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
## Planning
|
## Planning
|
||||||
|
|
||||||
(Include your planning here, including your project milestone.)
|
(1. Create a function that draws various sizes of blue bubbles that start low on the screen and then
|
||||||
|
move up and also use the easing function to make them behave like bubbles as they move up the screen. This will be my first milestone.
|
||||||
|
A simple drawing and allowing me to play with the animate functions.
|
||||||
|
2. Draw a turtle body and head. Draw turtle fins with parameters of size and transposition to be added to the turtle body.
|
||||||
|
3. Animate the turtle to swim from the bottom right of the screen to the top left increasing in size and moving the fins.)
|
||||||
|
|||||||
34
bubbles.py
Normal file
34
bubbles.py
Normal 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)
|
||||||
|
|
||||||
BIN
chris_ellipse_exploration/attempt_0.png
Normal file
BIN
chris_ellipse_exploration/attempt_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
chris_ellipse_exploration/demo.gif
Normal file
BIN
chris_ellipse_exploration/demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
6
chris_ellipse_exploration/demo.html
Normal file
6
chris_ellipse_exploration/demo.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body style="background-image: url(demo.gif); background-repeat: cover;">
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
26
chris_ellipse_exploration/demo.py
Normal file
26
chris_ellipse_exploration/demo.py
Normal 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()
|
||||||
112
chris_ellipse_exploration/ellipse.py
Normal file
112
chris_ellipse_exploration/ellipse.py
Normal 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))
|
||||||
|
|
||||||
24
chris_ellipse_exploration/tests.py
Normal file
24
chris_ellipse_exploration/tests.py
Normal 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)
|
||||||
172
drawing.py
172
drawing.py
@@ -1,7 +1,175 @@
|
|||||||
# drawing.py
|
# drawing.py
|
||||||
# ----------
|
# ----------
|
||||||
# By ____(you)___________
|
# By Hope Wright
|
||||||
#
|
#
|
||||||
# (Briefly describe what this program does.)
|
# a turtle and bubbles will be drawn and animate swimming across the screen
|
||||||
|
# phase 2 would be to have it start smaller and grow is it swims across the screen to look like it's getting closer
|
||||||
|
# phase 3 would be to animate the bubbles.
|
||||||
|
|
||||||
from turtle import *
|
from turtle import *
|
||||||
|
from superturtle.movement import fly
|
||||||
|
from superturtle.animation import animate
|
||||||
|
from superturtle.easing import easeInQuad
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
def draw_turtle(size):
|
||||||
|
|
||||||
|
pencolor("green")
|
||||||
|
#draw an oval for the shell
|
||||||
|
height = size*2
|
||||||
|
width = size*1.5
|
||||||
|
|
||||||
|
steps = 100
|
||||||
|
for i in range(steps + 1):
|
||||||
|
angle = 2 * math.pi * i / steps
|
||||||
|
x = (width) * math.cos(angle)
|
||||||
|
y = (height) * math.sin(angle)
|
||||||
|
goto(x,y)
|
||||||
|
|
||||||
|
#move to top of for head
|
||||||
|
|
||||||
|
penup()
|
||||||
|
left(90)
|
||||||
|
forward(size*4)
|
||||||
|
left(90)
|
||||||
|
forward(size*1.5)
|
||||||
|
pendown()
|
||||||
|
|
||||||
|
#draw a circle for the head
|
||||||
|
circle(size)
|
||||||
|
|
||||||
|
#draw some lines on the shell
|
||||||
|
penup()
|
||||||
|
left(90)
|
||||||
|
forward(size*2)
|
||||||
|
pendown()
|
||||||
|
forward(size*4)
|
||||||
|
right(180)
|
||||||
|
forward(size*2)
|
||||||
|
left(90)
|
||||||
|
forward(size*1.5)
|
||||||
|
penup()
|
||||||
|
left(180)
|
||||||
|
forward(size*1.5)
|
||||||
|
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)
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user