add Polygon

This commit is contained in:
James Turk 2024-04-30 20:56:37 -05:00
parent e8aff3394e
commit 8f5b03f34a
5 changed files with 81 additions and 8 deletions

View File

@ -19,9 +19,9 @@ then there are public/non-public files.)
"""
from .doodles import Doodle, Group
from .lines import Line
from .shapes import Circle, Rectangle
from .shapes import Circle, Rectangle, Polygon
from .color import Color
from .text import Text
__all__ = ["Doodle", "Group", "Line", "Circle", "Rectangle", "Color", "Text"]
__all__ = ["Doodle", "Group", "Line", "Circle", "Rectangle", "Color", "Text", "Polygon"]

View File

@ -40,7 +40,7 @@ class Doodle(ABC):
# annotations for instance attributes
_parent: Self | None
_updates: list[tuple[str, UpdateCallable]]
_updates: list[tuple[str, UpdateCallable, dict[str, Any]]]
_color: tuple[int, int, int]
_alpha: int
_z_index: float
@ -114,8 +114,8 @@ class Doodle(ABC):
# animate #######################
def animate(self, prop_name: str, update_func: UpdateCallable) -> Self:
self._updates.append((prop_name, update_func))
def animate(self, prop_name: str, update_func: UpdateCallable, **kwargs) -> Self:
self._updates.append((prop_name, update_func, kwargs))
return self
def update(self) -> None:
@ -128,14 +128,17 @@ class Doodle(ABC):
"""
cur_time = time.time()
for prop, anim_func in self._updates:
for prop, anim_func, kwargs 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)
# kwargs (if set) are passed through directly to anim_func
# allowing constant arguments to be passed as well as
# the variable function-based argument (anim_func)
setter = getattr(self, prop)
new_val = anim_func(cur_time)
setter(new_val)
setter(new_val, **kwargs)
# Setters #######################

View File

@ -0,0 +1,19 @@
from doodles import Polygon, Color
import random
def new_point(t):
return (random.random() * 100 - 50, random.random() * 100 - 50)
def create():
for _ in range(5):
p = Polygon().random(3)
for pt in range(3):
p.animate("point", new_point, to_modify=pt)
p = Polygon().random(8)
for pt in range(8):
p.animate("point", new_point, to_modify=pt)
p = Polygon().random(100)
for pt in range(100):
p.animate("point", new_point, to_modify=pt)

View File

@ -5,7 +5,7 @@ these classes only differ from `Line` in implementation.
The interface & decisions are the same but specific
to `Circle` and `Rectangle`.
"""
from typing import Self
from typing import Self, Optional
import random
from .doodles import Doodle
from .world import world
@ -86,3 +86,48 @@ class Rectangle(Doodle):
return self.width(random.random() * size + 10).height(
random.random() * size + 10
)
class Polygon(Doodle):
"""
All points are *relative* to the center point.
That is to say, if you had a triangle with coordinates:
(100, 100)
(0, 100)
(-100, 0)
And the object was moved to (50, 50), the actual triangle drawn
on screen would be:
(150, 150)
(50, 150)
(50, 50)
"""
_points: list[tuple[float, float]]
def __init__(self, parent=None):
super().__init__(parent)
self._points = []
def __repr__(self):
return f"Polygon(pos={self.world_vec}, points={self._points})"
def draw(self):
world.draw_engine.polygon_draw(self)
def point(
self, point: tuple[float, float], to_modify: Optional[int] = None
) -> Self:
if to_modify is not None:
self._points[to_modify] = point
else:
self._points.append(point)
return self
def random(self, n_points: int) -> Self:
super().random()
for _ in range(n_points):
self.point((random.random() * 100 - 50, random.random() * 100 - 50))
return self

View File

@ -90,6 +90,12 @@ class PygameDrawEngine(DrawEngine):
def line_draw(self, ll: "Line"):
pygame.draw.aaline(self.buffer, ll.rgba, ll.world_vec, ll.end_vec)
def polygon_draw(self, p: "Polygon"):
# calculate offset points from center of world
offset_points = [(x + p.world_x, y + p.world_y) for (x, y) in p._points]
# draw using anti-aliased lines
pygame.draw.aalines(self.buffer, p.rgba, closed=True, points=offset_points)
# TODO: hard to type, revisit
def text_render(self, text: str, font, color: tuple[int, int, int]):
"""returns an intermediated RenderedText"""