fix copying

This commit is contained in:
James Turk 2024-04-22 00:11:55 -05:00
parent 802ba51875
commit aa82295182
5 changed files with 55 additions and 36 deletions

View File

@ -36,7 +36,11 @@ class Doodle(ABC):
# All references to _pos_vec are internal to the class, # All references to _pos_vec are internal to the class,
# so it will be trivial to swap this out later. # so it will be trivial to swap this out later.
self._pos_vec = (0, 0) self._pos_vec = (0, 0)
if parent: self._register()
def _register(self):
""" register with parent and world """
if self._parent:
# register with parent for updates # register with parent for updates
self._parent.add(self) self._parent.add(self)
world.add(self) world.add(self)
@ -63,20 +67,20 @@ class Doodle(ABC):
Additionally, while a shallow copy is enough for most Additionally, while a shallow copy is enough for most
cases, it will be possible for child classes to override cases, it will be possible for child classes to override
this to opt for a deepcopy or other logic. this.
""" """
new = copy.copy(self) new = copy.copy(self)
world.add(new) new._register()
return new return new
def color(self, r: int, g: int, b: int) -> "Doodle": def color(self, color: tuple[int, int, int]) -> "Doodle":
""" """
Color works as a kind of setter function. Color works as a kind of setter function.
The only unique part is that it returns self, accomodating the The only unique part is that it returns self, accomodating the
chained object pattern. chained object pattern.
""" """
self._color = (r, g, b) self._color = color
return self return self
def pos(self, x: float, y: float) -> "Doodle": def pos(self, x: float, y: float) -> "Doodle":
@ -111,11 +115,11 @@ class Doodle(ABC):
""" """
x = random.random() * world.WIDTH x = random.random() * world.WIDTH
y = random.random() * world.HEIGHT y = random.random() * world.HEIGHT
r, g, b = Color.random() color = Color.random()
# again here, we opt to use the setters so that # again here, we opt to use the setters so that
# future extensions to their behavior will be # future extensions to their behavior will be
# used by all downstream functions # used by all downstream functions
return self.pos(x, y).color(r, g, b) return self.pos(x, y).color(color)
@property @property
def x(self) -> float: def x(self) -> float:
@ -265,10 +269,13 @@ class Group(Doodle):
in some languages would be much trickier to pull off. in some languages would be much trickier to pull off.
""" """
def __init__(self): def __init__(self, parent=None):
super().__init__() super().__init__(parent)
self._doodles = [] self._doodles = []
def __repr__(self):
return f"Group(pos={self.pos_vec}, doodles={len(self._doodles)})"
def draw(self, screen): def draw(self, screen):
""" """
Groups, despite being an abstract concept, are drawable. Groups, despite being an abstract concept, are drawable.
@ -285,11 +292,16 @@ class Group(Doodle):
We are storing a list, so deep copies are necessary. We are storing a list, so deep copies are necessary.
""" """
new = copy.deepcopy(self) new = copy.copy(self)
world.add(new) new._register()
new._doodles = []
for child in self._doodles:
child = copy.copy(child)
child._parent = new
child._register()
return new return new
def color(self, r: int, g: int, b: int) -> "Doodle": def color(self, color: tuple[int, int, int]) -> "Doodle":
""" """
Another override. Another override.
@ -299,9 +311,9 @@ class Group(Doodle):
We don't cascade pos() calls, why not? We don't cascade pos() calls, why not?
""" """
super().color(r, g, b) super().color(color)
for d in self._doodles: for d in self._doodles:
d.color(r, g, b) d.color(color)
return self return self
def add(self, doodle: "Doodle") -> "Group": def add(self, doodle: "Doodle") -> "Group":
@ -338,7 +350,7 @@ class Circle(Doodle):
self._radius = 0 self._radius = 0
def __repr__(self): def __repr__(self):
return f"Circle(pos={self.pos_vec}, radius={self._radius}, {self._color})" return f"Circle(pos={self.pos_vec}, radius={self._radius}, {self._color}, parent={self._parent}))"
def draw(self, screen): def draw(self, screen):
pygame.draw.circle(screen, self._color, self.pos_vec, self._radius) pygame.draw.circle(screen, self._color, self.pos_vec, self._radius)
@ -359,4 +371,4 @@ class Circle(Doodle):
def random(self) -> "Doodle": def random(self) -> "Doodle":
super().random() super().random()
# constrain to 10-100 # constrain to 10-100
return self.radius(random.random*90 + 10) return self.radius(random.random()*90 + 10)

View File

@ -0,0 +1,16 @@
from doodles.doodles import Group, Line, Circle, Color
def original():
g = Group()
c = Circle(g).radius(80).color(Color.RED).pos(0, 0)
for _ in range(15):
c = c.copy().move(45, 45)
return g
r = original()
r.copy().move(200, 0).color(Color.GREEN)
r.copy().move(400, 0).color(Color.BLUE)
# from doodles.world import world
# for d in world._drawables:
# print(" >", d)

View File

@ -1,10 +1,13 @@
from doodles.doodles import Group, Line from doodles.doodles import Group, Line
from doodles.layouts import make_grid, copies from doodles.layouts import make_grid
# Create a group of lines all with same origin, different angles. def same_spiral():
g = Group() while True:
for d in range(0, 180, 10): # Create a group of lines all with same origin, different angles.
g = Group()
for d in range(0, 180, 10):
Line(g).vec(d, 200 - d) Line(g).vec(d, 200 - d)
yield g
# Make copies, moving each one and modifying the color # Make copies, moving each one and modifying the color
make_grid(copies(g), 3, 4, 250, 140, x_offset=70, y_offset=20) make_grid(same_spiral(), 3, 4, 250, 140, x_offset=70, y_offset=20)

View File

@ -5,21 +5,9 @@ def make_grid(iterable, cols, rows, width, height, *, x_offset=0, y_offset=0):
Arranges the objects in iterable in a grid with the given parameters. Arranges the objects in iterable in a grid with the given parameters.
""" """
try: try:
doodle = next(iterable)
for c in range(cols): for c in range(cols):
for r in range(rows): for r in range(rows):
doodle.pos(width * c + x_offset, height * r + y_offset)
doodle = next(iterable) doodle = next(iterable)
doodle.pos(width * c + x_offset, height * r + y_offset)
except StopIteration: except StopIteration:
pass pass
def copies(doodle):
"""
Lazily makes an infinite number of copies of a given doodle.
Can be combined with things like `make_grid` that require
an iterable of doodles to repeat.
"""
while True:
yield doodle.copy()

View File

@ -29,7 +29,7 @@ def main(modname: str):
pygame.quit() pygame.quit()
sys.exit() sys.exit()
world.render() world.render()
# print(f"world contains {world._drawables}") #print(f"world contains {len(world._drawables)}")
pygame.display.flip() pygame.display.flip()