moved animation code to more general interface, and updated clock example

This commit is contained in:
James Turk 2024-04-30 19:56:54 -05:00
parent fdd7ee24d3
commit ec7bbad372
3 changed files with 42 additions and 24 deletions

View File

@ -12,11 +12,13 @@ This is a reasonable example of when two classes might reasonable share a file.
"""
import random
import copy
import time
from abc import ABC, abstractmethod
from typing import Callable, Self
from typing import Callable, Self, Any
from .color import Color
from .world import world
UpdateCallable = Callable[[float], Any]
class Doodle(ABC):
"""
@ -38,7 +40,7 @@ class Doodle(ABC):
# annotations for instance attributes
_parent: Self | None
_updates: list[Callable]
_updates: list[tuple[str, UpdateCallable]]
_color: tuple[int, int, int]
_alpha: int
_z_index: float
@ -110,17 +112,11 @@ class Doodle(ABC):
new._register()
return new
# Dynamic Update Logic ############
# animate #######################
# These methods relate to a WIP feature
# designed to demonstrate a functional hybrid
# approach to having objects update themselves.
#
# This feature isn't complete, or documented yet.
# TODO
def register_update(self, method, *args):
self._updates.append((method, args))
def animate(self, prop_name: str, update_func: UpdateCallable) -> Self:
self._updates.append((prop_name, update_func))
return self
def update(self) -> None:
"""
@ -130,9 +126,16 @@ class Doodle(ABC):
Can be overriden (see examples.balls)
to provide per-object update behavior.
"""
for method, args in self._updates:
evaled_args = [arg() for arg in args]
method(*evaled_args)
cur_time = time.time()
for prop, anim_func in self._updates:
# attributes on Doodle are set via setter functions
# prop is the name of a setter function, which we
# retrieve here, and then populate with the result
# of anim_func(time)
setter = getattr(self, prop)
new_val = anim_func(cur_time)
setter(new_val)
# Setters #######################

View File

@ -1,9 +1,22 @@
import time
from doodles import Circle, Color, Line, Group
def color_func(t):
print("updating color")
cycle = [Color.RED, Color.ORANGE, Color.GREEN, Color.BLUE]
return cycle[int(t) % 4]
def create():
g = Group().pos(400, 300)
Circle(g).radius(300).color(Color.BLACK).z(1)
Circle(g).radius(290).color(Color.BROWN).z(10)
Circle(g).radius(20).color(Color.BLACK).z(50)
Line(g).vec(lambda: time.time() % 60 / 60 * 360, 200).z(100)
# Line(g).vec(
# lambda: time.time() % 60 / 60 * 360,
# 200
# ).z(100)
l = Line(g).vec(0, 200).z(100).animate("degrees", lambda t: t % 60 / 60 * 360)
l.animate("color", color_func)

View File

@ -63,7 +63,7 @@ class Line(Doodle):
self._offset_vec = (x, y)
return self
def vec(self, degrees: float | Callable, magnitude: float):
def vec(self, degrees: float, magnitude: float):
"""
Alternate setter, to create offset vector from angle & length.
@ -72,14 +72,16 @@ class Line(Doodle):
directly (`to`), but there is also an alternate option
that handles commonly used case.
"""
if callable(degrees):
self.register_update(
self.to,
lambda: magnitude * math.cos(math.radians(degrees())),
lambda: magnitude * math.sin(math.radians(degrees())),
)
return self
return self.to(
magnitude * math.cos(math.radians(degrees)),
magnitude * math.sin(math.radians(degrees)),
)
def degrees(self, degrees: float):
"""
Alternate setter, like calling vec(new_degrees, old_magnitude).
"""
magnitude = math.sqrt(self._offset_vec[0]**2 + self._offset_vec[1]**2)
return self.to(
magnitude * math.cos(math.radians(degrees)),
magnitude * math.sin(math.radians(degrees)),