add Polygon
This commit is contained in:
parent
e8aff3394e
commit
8f5b03f34a
@ -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"]
|
||||
|
@ -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 #######################
|
||||
|
||||
|
19
src/doodles/examples/polygons.py
Normal file
19
src/doodles/examples/polygons.py
Normal 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)
|
@ -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
|
||||
|
@ -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"""
|
||||
|
Loading…
Reference in New Issue
Block a user