Skip to content

Instantly share code, notes, and snippets.

@adrientetar
Created December 17, 2018 18:45
Show Gist options
  • Save adrientetar/c687caa8c02d604c942d24d4eb9c8746 to your computer and use it in GitHub Desktop.
Save adrientetar/c687caa8c02d604c942d24d4eb9c8746 to your computer and use it in GitHub Desktop.
from collections.abc import Collection
from dataclasses import dataclass, field
from typing import Dict, List, Tuple
@dataclass
class Point:
_x: int
_y: int
_type: str
_smooth: bool
@property
def x(self) -> int:
return self._x
@property
def y(self) -> int:
return self._y
@property
def type(self) -> str:
return self._type
@property
def smooth(self) -> bool:
return self._smooth
def __repr__(self):
if self.type is not None:
more = ", %r" % self.type
if self.smooth:
more += ", smooth=%r" % self.smooth
else:
more = ""
return "%s(%r, %r%s)" % (
self.__class__.__name__, self.x, self.y, more)
@dataclass
class Change:
op: str
path: str
value: Dict[str, object]
@dataclass
class Path:
_points: List[Point]
_undoStack: List[Change] = field(default_factory=list)
_redoStack: List[Change] = field(default_factory=list)
@property
def points(self) -> List[Point]:
return self._points
def move(self, dx):
changes = []
for ix, point in enumerate(self._points):
changes.append(Change(
"replace", f"/points/{ix}", {"x": point.x + dx}
))
self.apply(changes)
# Actuators
def apply(self, changes, record=True):
for change in changes:
if change.op == "replace":
# starting / prob not needed
elements = change.path.lstrip("/").split("/")
Path._replace(self, elements, change)
else:
raise NotImplementedError(
f"{change.op} is not supported")
self._undoStack.append(changes)
def undo(self):
if self._undoStack:
changes = self._undoStack.pop()
self.apply(changes, record=False)
self._redoStack.append(changes)
def redo(self):
if self._redoStack:
changes = self._redoStack.pop()
self.apply(changes)
# Effectors
@staticmethod
def _replace(obj, elements, change):
if elements:
target = getattr(obj, elements[0])
if isinstance(target, Collection):
Path._replace(target[int(elements[1])], elements[2:], change)
else:
for k, v in change.value.items():
ov = getattr(obj, k)
setattr(obj, "_" + k, v)
change.value[k] = ov
if __name__ == "__main__":
pts = [Point(0, 0, "line", False), Point(20, 20, "line", False)]
p = Path(pts)
dx = 10
print(pts)
p.move(dx)
print(f"move points by {dx}")
print(pts)
p.undo()
print("undo")
print(pts)
p.redo()
print("redo")
print(pts)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment