enable per-shape repetitions

This commit is contained in:
Jan Petykiewicz 2020-07-22 21:50:39 -07:00
parent ad6fa88e53
commit 629a6a9ba2
9 changed files with 75 additions and 5 deletions

View File

@ -1,14 +1,16 @@
from typing import List, Tuple, Dict
from typing import List, Tuple, Dict, Optional
import copy
import numpy
from numpy import pi
from .repetition import Repetition
from .error import PatternError, PatternLockedError
from .utils import is_scalar, vector2, rotation_matrix_2d, layer_t, AutoSlots
from .traits import PositionableImpl, LayerableImpl, Copyable, Pivotable, LockableImpl
from .traits import PositionableImpl, LayerableImpl, Copyable, Pivotable, LockableImpl, RepeatableImpl
class Label(PositionableImpl, LayerableImpl, LockableImpl, Pivotable, Copyable, metaclass=AutoSlots):
class Label(PositionableImpl, LayerableImpl, LockableImpl, RepeatableImpl,
Pivotable, Copyable, metaclass=AutoSlots):
"""
A text annotation with a position and layer (but no size; it is not drawn)
"""
@ -39,18 +41,21 @@ class Label(PositionableImpl, LayerableImpl, LockableImpl, Pivotable, Copyable,
string: str,
offset: vector2 = (0.0, 0.0),
layer: layer_t = 0,
repetition: Optional[Repetition] = None,
locked: bool = False):
object.__setattr__(self, 'locked', False)
self.identifier = ()
self.string = string
self.offset = numpy.array(offset, dtype=float, copy=True)
self.layer = layer
self.repetition = repetition
self.locked = locked
def __copy__(self) -> 'Label':
return Label(string=self.string,
offset=self.offset.copy(),
layer=self.layer,
repetition=self.repetition,
locked=self.locked)
def __deepcopy__(self, memo: Dict = None) -> 'Label':

View File

@ -575,6 +575,52 @@ class Pattern:
self.append(p)
return self
def wrap_repeated_shapes(self,
name_func: Callable[['Pattern', Union[Shape, Label]], str] = lambda p, s: '_repetition',
recursive: bool = True,
) -> 'Pattern':
"""
Wraps all shapes and labels with a non-`None` `repetition` attribute
into a `SubPattern`/`Pattern` combination, and applies the `repetition`
to each `SubPattern` instead of its contained shape.
Args:
name_func: Function f(this_pattern, shape) which generates a name for the
wrapping pattern. Default always returns '_repetition'.
recursive: If `True`, this function is also applied to all referenced patterns
recursively. Default `True`.
Returns:
self
"""
def do_wrap(pat: Optional[Pattern]) -> Optional[Pattern]:
if pat is None:
return pat
new_subpatterns = []
for shape in pat.shapes:
if shape.repetition is None:
continue
new_subpatterns.append(SubPattern(Pattern(name_func(pat, shape), shapes=[shape])))
shape.repetition = None
for label in self.labels:
if label.repetition is None:
continue
new_subpatterns.append(SubPattern(Pattern(name_func(pat, shape), labels=[label])))
label.repetition = None
pat.subpatterns += new_subpatterns
return pat
if recursive:
self.apply(do_wrap)
else:
do_wrap(self)
return self
def translate_elements(self, offset: vector2) -> 'Pattern':
"""
Translates all shapes, label, and subpatterns by the given offset.

View File

@ -6,6 +6,7 @@ from numpy import pi
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
from .. import PatternError
from ..repetition import Repetition
from ..utils import is_scalar, vector2, layer_t, AutoSlots
@ -158,6 +159,7 @@ class Arc(Shape, metaclass=AutoSlots):
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
locked: bool = False):
object.__setattr__(self, 'locked', False)
self.identifier = ()
@ -171,6 +173,7 @@ class Arc(Shape, metaclass=AutoSlots):
self.dose = dose
self.poly_num_points = poly_num_points
self.poly_max_arclen = poly_max_arclen
self.repetition = repetition
self.locked = locked
def __deepcopy__(self, memo: Dict = None) -> 'Arc':

View File

@ -5,6 +5,7 @@ from numpy import pi
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
from .. import PatternError
from ..repetition import Repetition
from ..utils import is_scalar, vector2, layer_t, AutoSlots
@ -46,6 +47,7 @@ class Circle(Shape, metaclass=AutoSlots):
offset: vector2 = (0.0, 0.0),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
locked: bool = False):
object.__setattr__(self, 'locked', False)
self.identifier = ()
@ -55,6 +57,7 @@ class Circle(Shape, metaclass=AutoSlots):
self.radius = radius
self.poly_num_points = poly_num_points
self.poly_max_arclen = poly_max_arclen
self.repetition = repetition
self.locked = locked
def __deepcopy__(self, memo: Dict = None) -> 'Circle':

View File

@ -6,6 +6,7 @@ from numpy import pi
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
from .. import PatternError
from ..repetition import Repetition
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
@ -93,6 +94,7 @@ class Ellipse(Shape, metaclass=AutoSlots):
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
locked: bool = False):
object.__setattr__(self, 'locked', False)
self.identifier = ()
@ -104,6 +106,7 @@ class Ellipse(Shape, metaclass=AutoSlots):
self.dose = dose
self.poly_num_points = poly_num_points
self.poly_max_arclen = poly_max_arclen
self.repetition = repetition
self.locked = locked
def __deepcopy__(self, memo: Dict = None) -> 'Ellipse':

View File

@ -6,6 +6,7 @@ from numpy import pi, inf
from . import Shape, normalized_shape_tuple, Polygon, Circle
from .. import PatternError
from ..repetition import Repetition
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
from ..utils import remove_colinear_vertices, remove_duplicate_vertices
@ -147,6 +148,7 @@ class Path(Shape, metaclass=AutoSlots):
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
locked: bool = False,
):
object.__setattr__(self, 'locked', False)
@ -163,6 +165,7 @@ class Path(Shape, metaclass=AutoSlots):
self.cap_extensions = cap_extensions
self.rotate(rotation)
[self.mirror(a) for a, do in enumerate(mirrored) if do]
self.repetition = repetition
self.locked = locked
def __deepcopy__(self, memo: Dict = None) -> 'Path':

View File

@ -5,6 +5,7 @@ from numpy import pi
from . import Shape, normalized_shape_tuple
from .. import PatternError
from ..repetition import Repetition
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
from ..utils import remove_colinear_vertices, remove_duplicate_vertices
@ -75,6 +76,7 @@ class Polygon(Shape, metaclass=AutoSlots):
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
locked: bool = False,
):
object.__setattr__(self, 'locked', False)
@ -85,6 +87,7 @@ class Polygon(Shape, metaclass=AutoSlots):
self.offset = offset
self.rotate(rotation)
[self.mirror(a) for a, do in enumerate(mirrored) if do]
self.repetition = repetition
self.locked = locked
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Polygon':

View File

@ -7,7 +7,7 @@ from ..error import PatternError, PatternLockedError
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t
from ..traits import (PositionableImpl, LayerableImpl, DoseableImpl,
Rotatable, Mirrorable, Copyable, Scalable,
PivotableImpl, LockableImpl)
PivotableImpl, LockableImpl, RepeatableImpl)
if TYPE_CHECKING:
from . import Polygon
@ -26,7 +26,8 @@ DEFAULT_POLY_NUM_POINTS = 24
T = TypeVar('T', bound='Shape')
class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable, Copyable, Scalable, PivotableImpl, LockableImpl, metaclass=ABCMeta):
class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable, Copyable, Scalable,
PivotableImpl, RepeatableImpl, LockableImpl, metaclass=ABCMeta):
"""
Abstract class specifying functions common to all shapes.
"""

View File

@ -5,6 +5,7 @@ from numpy import pi, inf
from . import Shape, Polygon, normalized_shape_tuple
from .. import PatternError
from ..repetition import Repetition
from ..traits import RotatableImpl
from ..utils import is_scalar, vector2, get_bit, normalize_mirror, layer_t, AutoSlots
@ -65,6 +66,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
mirrored: Tuple[bool, bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
locked: bool = False,
):
object.__setattr__(self, 'locked', False)
@ -77,6 +79,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
self.rotation = rotation
self.font_path = font_path
self.mirrored = mirrored
self.repetition = repetition
self.locked = locked
def __deepcopy__(self, memo: Dict = None) -> 'Text':