I'm trying to figure out how to animate the tree now, because I want to

make the leaves change colors, so I made a new python program file to
test out just getting one leaf to change color.

I don't think I'm understanding how to get animate to work. I was
thinking that if I made a list of colors, I could iterate over the
colors in the list for different frames, but I couldn't get that to
work. Also, for some reason, I can't use superturtle directly, even
though I have it installed. I have been copying the source code into
the superturt.py file, but some of the source code isn't available
to do that for (the easings). Not sure if that is a factor in not being
able to do this though, because I was trying to switch between specific
colors, and not move through a spectrum (although that would be cool
to do eventually if I can). I was also thinking it would be cool to
make the leaves grow when they first appear, and then rotate and fall
down at the end, but that would definitely require the easing thing.
This commit is contained in:
root 2024-09-24 23:19:12 -04:00
parent 1ab20f18ff
commit 9ccad6051e
6 changed files with 319 additions and 24 deletions

Binary file not shown.

31
changingcolors.py Normal file
View File

@ -0,0 +1,31 @@
from turtle import *
from superturt import *
from shapes import *
# I'm trying to figure out how to get the leaves to change colors.
with no_delay():
fillcolor('green')
begin_fill()
draw_leaf(20)
end_fill()
with no_delay():
fillcolor('yellow')
begin_fill()
draw_leaf(20)
end_fill()
with no_delay():
fillcolor('orange')
begin_fill()
draw_leaf(20)
end_fill()
with no_delay():
fillcolor('red')
begin_fill()
draw_leaf(20)
end_fill()
input()

View File

@ -2,17 +2,15 @@
# ----------
# By Stacy S
#
# (Drew a tree.)
# (Drew a tree. Trying to make it change colors.)
from turtle import *
from math import sqrt
from shapes import *
from superturt import *
with no_delay():
with restore_state_when_finished():
draw_tree_nl(20)
with restore_state_when_finished():
draw_tree_wl(20)
input()
for frame in animate(frames=6, loop=True, debug=False):
with restore_state_when_finished():
draw_tree_nl(20)
with restore_state_when_finished():
draw_tree_wl(20)

View File

@ -4,13 +4,13 @@ from superturt import *
def draw_leaf(size):
pencolor('black')
fillcolor('green')
begin_fill()
# fillcolor('green')
# begin_fill()
circle(size,90)
right(270)
circle(.75*size,90)
circle(-.25*size,90)
end_fill()
# end_fill()
def draw_leaves():
with restore_state_when_finished():
@ -48,13 +48,7 @@ def tip_nl(size):
forward(size)
def tip_wl(size):
forward(size)
right(45)
forward(size/10)
right(90)
forward(size/10)
right(45)
forward(size)
tip_nl(size)
draw_leaves()
def branching_nl(ang1, d1, ang2, d2, ang3):
@ -65,11 +59,7 @@ def branching_nl(ang1, d1, ang2, d2, ang3):
right(ang3)
def branching_wl(ang1, d1, ang2, d2, ang3):
right(ang1)
forward(d1)
right(ang2)
forward(d2)
right(ang3)
branching_nl(ang1, d1, ang2, d2, ang3)
draw_leaves()
def branch_end_wl(size):

View File

@ -1,6 +1,32 @@
#from superturtle by Chris Proctor
from turtle import *
from itertools import chain, cycle
from pathlib import Path
from shutil import rmtree
from subprocess import run
from turtle import (
Turtle,
left,
right,
clear,
home,
heading,
setheading,
isdown,
hideturtle,
penup,
pendown,
forward,
getcanvas,
)
from itertools import cycle
from time import time, sleep
FRAMES_PATH = Path(".frames")
class no_delay:
"""A context manager which causes drawing code to run instantly.
@ -67,4 +93,254 @@ class restore_state_when_finished:
penup()
setposition(self.position)
setheading(self.heading)
pendown()
pendown()
def animate(frames=1, loop=False, debug=False, gif_filename=None):
"""Runs an animation, frame by frame, at a fixed frame rate of 20 fps.
An animation consists of a bunch of frames shown one after another.
Before creating an animation, create a static image and then think about
how you would like for it to move. The simplest way to use `animate` is
with no arguments; this produces a static image. (Not much of an animation!)::
for frame in animate():
draw_my_picture(frame)
Once you are happy with your static image, specify `frames` and `animate`
will run the provided code block over and over, drawing one frame at a time::
for frame in animate(frames=6, debug=True):
draw_my_picture(frame)
Because we set `debug` to `True`, you will see the following output in the Terminal.
Additionally, since we are in debug mode, you need to press enter to advance one
frame at a time.::
Drawing frame 0
Drawing frame 1
Drawing frame 2
Drawing frame 3
Drawing frame 4
Drawing frame 5
Arguments:
frames (int): The total number of frames in your animation.
loop (bool): When True, the animation will play in a loop.
debug (bool): When True, renders the animation in debug mode.
gif_filename (str): When provided, saves the animation as a gif, with 10 frames per second.
"""
start_time = time()
if frames <= 0:
raise AnimationError("frames must be a positive integer")
frame_delta = 0.05
hideturtle()
if debug:
frame_iterator = debug_iter(frames)
elif loop and not gif_filename:
frame_iterator = cycle(range(frames))
else:
frame_iterator = range(frames)
if gif_filename:
if FRAMES_PATH.exists():
if FRAMES_PATH.is_dir():
rmtree(FRAMES_PATH)
else:
FRAMES_PATH.unlink()
FRAMES_PATH.mkdir()
for frame_number in frame_iterator:
frame = Frame(frames, frame_number, debug=debug)
if time() < start_time + frame_number * frame_delta:
sleep(start_time + frame_number * frame_delta - time())
with no_delay():
home()
clear()
yield frame
if gif_filename:
filename = FRAMES_PATH / f"frame_{frame_number:03d}.eps"
canvas = getcanvas()
canvas.postscript(file=filename)
canvas.delete("all")
if gif_filename:
loopflag = "-loop 0 " if loop else ""
run(f"convert -delay 5 -dispose Background {loopflag}{FRAMES_PATH}/frame_*.eps {gif_filename}", shell=True, check=True)
rmtree(FRAMES_PATH)
class Frame:
""" Represents one frame in an animation.
When creating an animation, `animate` will yield one `Frame` for each
frame. The `Frame` can be used to check information, such as the current
frame index (the first frame's index is 0; the tenth frame's index is 9).
The `Frame` can also be used to create motion through the use of transformations.
A transformation is a change to the picture which gradually changes from frame
to frame. The three supported transformations are `rotate`, `scale`, and
`translate`.
"""
def __init__(self, num_frames, index=0, debug=False):
self.debug = debug
self.num_frames = num_frames
self.index = index
self.stack = []
self.log("Drawing frame {}".format(self.index))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
exit = self.stack.pop()
exit()
def rotate(self, start, stop=None, first_frame=None, last_frame=None, cycles=1, mirror=False, easing=None):
"""Runs the code block within a rotation::
for frame in animate(frames=30):
with frame.rotate(0, 90):
square(100)
Arguments:
start (int): the initial value.
stop (int): (optional) the final value. If not provided, this will be a static
rotation of `start` on every frame.
first_frame (int): (optional) The first frame at which this rotation should be
interpolated. If given, the rotation will be `start` at `first_frame`
and all prior frames. If not given, interpolation starts at the beginning
of the animation.
last_frame(int): (optional) The last frame at which this rotation should be
interpolated. If given, the rotation will be `stop` at `last_frame`
and all later frames. If not given, interpolation ends at the end of the
animation.
cycles (int): (optional) Number of times the animation should be run.
mirror (bool): (optional) When True, the animation runs forward and then backwards
between `first_frame` and `last_frame`.
easing (function): (optional) An easing function to use.
"""
value = self.interpolate(start, stop, first_frame, last_frame, cycles, mirror, easing)
left(value)
self.stack.append(lambda: right(value))
self.log("Rotating by {}".format(value))
return self
def scale(self, start, stop=None, first_frame=None, last_frame=None, cycles=1, mirror=False, easing=None):
"""Scales the code block. For this to work correctly, make sure you start from the
center of the drawing in the code block::
for frame in animate(frames=30):
with frame.scale(1, 2):
square(100)
Arguments:
start (int): the initial value.
stop (int): (optional) the final value. If not provided, this will be a static
scaling of `start` on every frame.
first_frame (int): (optional) The first frame at which this scaling should be
interpolated. If given, the scaling will be `start` at `first_frame`
and all prior frames. If not given, interpolation starts at the beginning
of the animation.
last_frame (int): (optional) The last frame at which this scaling should be
interpolated. If given, the scaling will be `stop` at `last_frame`
and all later frames. If not given, interpolation ends at the end of the
animation.
cycles (int): (optional) Number of times the animation should be run.
mirror (bool): (optional) When True, the animation runs forward and then backwards
between `first_frame` and `last_frame`.
easing (function): (optional) An easing function to use.
"""
value = self.interpolate(start, stop, first_frame, last_frame, cycles, mirror, easing)
repair = self._scale_turtle_go(value)
self.stack.append(repair)
self.log("Scaling by {}".format(value))
return self
def translate(self, start, stop=None, first_frame=None, last_frame=None, cycles=1, mirror=False, easing=None):
"""Translates (moves) the code block in the current coordinate space::
for frame in animate(frames=30):
with frame.translate([0, 0], [100, 100]):
square(100)
Arguments:
start (int): the initial value.
stop (int): (optional) the final value. If not provided, this will be a static
translation of `start` on every frame.
first_frame (int): (optional) The first frame at which this translation should be
interpolated. If given, the translation will be `start` at `first_frame`
and all prior frames. If not given, interpolation starts at the beginning
of the animation.
last_frame (int): (optional) The last frame at which this translation should be
interpolated. If given, the translation will be `stop` at `last_frame`
and all later frames. If not given, interpolation ends at the end of the
animation.
cycles (int): (optional) Number of times the animation should be run.
mirror (bool): (optional) When True, the animation runs forward and then backwards
between `first_frame` and `last_frame`.
easing (function): (optional) An easing function to use.
"""
if stop:
x0, y0 = start
x1, y1 = stop
dx = self.interpolate(x0, x1, first_frame, last_frame, cycles, mirror, easing)
dy = self.interpolate(y0, y1, first_frame, last_frame, cycles, mirror, easing)
else:
dx, dy = start
def scoot(x, y):
pd = isdown()
penup()
forward(x)
left(90)
forward(y)
right(90)
if pd:
pendown()
scoot(dx, dy)
self.stack.append(lambda: scoot(-dx, -dy))
self.log("Translating by ({}, {})".format(dx, dy))
return self
def _scale_turtle_go(self, scale_factor):
"""Patches `Turtle._go` with a version which scales all motion
by `scale_factor`. Returns a repair function which will restore
`Turtle._go` when called.
"""
prior_go = Turtle._go
def scaled_go(turtle_self, distance):
prior_go(turtle_self, distance * scale_factor)
Turtle._go = scaled_go
def repair():
Turtle._go = prior_go
return repair
def log(self, message):
if self.debug:
print(" " * len(self.stack) + message)
def debug_iter(max_val=None):
"An iterator which yields only when input is "
HELP = '?'
INCREMENT = ['', 'f']
DECREMENT = ['b']
value = 0
print("In debug mode. Enter {} for help.".format(HELP))
while True:
yield value % max_val if max_val else value
command = None
while command not in INCREMENT and command not in DECREMENT:
if command == HELP:
print("Debug mode moves one frame at a time.")
print("Enter 'f' or '' (blank) to move forward. Enter 'b' to move backward.")
command = input()
if command in INCREMENT:
value += 1
else:
value -= 1
class AnimationError(Exception):
pass