enable per-shape repetitions
This commit is contained in:
parent
ad6fa88e53
commit
629a6a9ba2
@ -1,14 +1,16 @@
|
|||||||
from typing import List, Tuple, Dict
|
from typing import List, Tuple, Dict, Optional
|
||||||
import copy
|
import copy
|
||||||
import numpy
|
import numpy
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
|
|
||||||
|
from .repetition import Repetition
|
||||||
from .error import PatternError, PatternLockedError
|
from .error import PatternError, PatternLockedError
|
||||||
from .utils import is_scalar, vector2, rotation_matrix_2d, layer_t, AutoSlots
|
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)
|
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,
|
string: str,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
object.__setattr__(self, 'locked', False)
|
object.__setattr__(self, 'locked', False)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
self.string = string
|
self.string = string
|
||||||
self.offset = numpy.array(offset, dtype=float, copy=True)
|
self.offset = numpy.array(offset, dtype=float, copy=True)
|
||||||
self.layer = layer
|
self.layer = layer
|
||||||
|
self.repetition = repetition
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __copy__(self) -> 'Label':
|
def __copy__(self) -> 'Label':
|
||||||
return Label(string=self.string,
|
return Label(string=self.string,
|
||||||
offset=self.offset.copy(),
|
offset=self.offset.copy(),
|
||||||
layer=self.layer,
|
layer=self.layer,
|
||||||
|
repetition=self.repetition,
|
||||||
locked=self.locked)
|
locked=self.locked)
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'Label':
|
def __deepcopy__(self, memo: Dict = None) -> 'Label':
|
||||||
|
@ -575,6 +575,52 @@ class Pattern:
|
|||||||
self.append(p)
|
self.append(p)
|
||||||
return self
|
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':
|
def translate_elements(self, offset: vector2) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Translates all shapes, label, and subpatterns by the given offset.
|
Translates all shapes, label, and subpatterns by the given offset.
|
||||||
|
@ -6,6 +6,7 @@ from numpy import pi
|
|||||||
|
|
||||||
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
|
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
|
||||||
from .. import PatternError
|
from .. import PatternError
|
||||||
|
from ..repetition import Repetition
|
||||||
from ..utils import is_scalar, vector2, layer_t, AutoSlots
|
from ..utils import is_scalar, vector2, layer_t, AutoSlots
|
||||||
|
|
||||||
|
|
||||||
@ -158,6 +159,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
object.__setattr__(self, 'locked', False)
|
object.__setattr__(self, 'locked', False)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
@ -171,6 +173,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
self.dose = dose
|
self.dose = dose
|
||||||
self.poly_num_points = poly_num_points
|
self.poly_num_points = poly_num_points
|
||||||
self.poly_max_arclen = poly_max_arclen
|
self.poly_max_arclen = poly_max_arclen
|
||||||
|
self.repetition = repetition
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'Arc':
|
def __deepcopy__(self, memo: Dict = None) -> 'Arc':
|
||||||
|
@ -5,6 +5,7 @@ from numpy import pi
|
|||||||
|
|
||||||
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
|
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
|
||||||
from .. import PatternError
|
from .. import PatternError
|
||||||
|
from ..repetition import Repetition
|
||||||
from ..utils import is_scalar, vector2, layer_t, AutoSlots
|
from ..utils import is_scalar, vector2, layer_t, AutoSlots
|
||||||
|
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ class Circle(Shape, metaclass=AutoSlots):
|
|||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
object.__setattr__(self, 'locked', False)
|
object.__setattr__(self, 'locked', False)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
@ -55,6 +57,7 @@ class Circle(Shape, metaclass=AutoSlots):
|
|||||||
self.radius = radius
|
self.radius = radius
|
||||||
self.poly_num_points = poly_num_points
|
self.poly_num_points = poly_num_points
|
||||||
self.poly_max_arclen = poly_max_arclen
|
self.poly_max_arclen = poly_max_arclen
|
||||||
|
self.repetition = repetition
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'Circle':
|
def __deepcopy__(self, memo: Dict = None) -> 'Circle':
|
||||||
|
@ -6,6 +6,7 @@ from numpy import pi
|
|||||||
|
|
||||||
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
|
from . import Shape, Polygon, normalized_shape_tuple, DEFAULT_POLY_NUM_POINTS
|
||||||
from .. import PatternError
|
from .. import PatternError
|
||||||
|
from ..repetition import Repetition
|
||||||
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
|
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),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
object.__setattr__(self, 'locked', False)
|
object.__setattr__(self, 'locked', False)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
@ -104,6 +106,7 @@ class Ellipse(Shape, metaclass=AutoSlots):
|
|||||||
self.dose = dose
|
self.dose = dose
|
||||||
self.poly_num_points = poly_num_points
|
self.poly_num_points = poly_num_points
|
||||||
self.poly_max_arclen = poly_max_arclen
|
self.poly_max_arclen = poly_max_arclen
|
||||||
|
self.repetition = repetition
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'Ellipse':
|
def __deepcopy__(self, memo: Dict = None) -> 'Ellipse':
|
||||||
|
@ -6,6 +6,7 @@ from numpy import pi, inf
|
|||||||
|
|
||||||
from . import Shape, normalized_shape_tuple, Polygon, Circle
|
from . import Shape, normalized_shape_tuple, Polygon, Circle
|
||||||
from .. import PatternError
|
from .. import PatternError
|
||||||
|
from ..repetition import Repetition
|
||||||
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
|
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
|
||||||
from ..utils import remove_colinear_vertices, remove_duplicate_vertices
|
from ..utils import remove_colinear_vertices, remove_duplicate_vertices
|
||||||
|
|
||||||
@ -147,6 +148,7 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
locked: bool = False,
|
||||||
):
|
):
|
||||||
object.__setattr__(self, 'locked', False)
|
object.__setattr__(self, 'locked', False)
|
||||||
@ -163,6 +165,7 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
self.cap_extensions = cap_extensions
|
self.cap_extensions = cap_extensions
|
||||||
self.rotate(rotation)
|
self.rotate(rotation)
|
||||||
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
||||||
|
self.repetition = repetition
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'Path':
|
def __deepcopy__(self, memo: Dict = None) -> 'Path':
|
||||||
|
@ -5,6 +5,7 @@ from numpy import pi
|
|||||||
|
|
||||||
from . import Shape, normalized_shape_tuple
|
from . import Shape, normalized_shape_tuple
|
||||||
from .. import PatternError
|
from .. import PatternError
|
||||||
|
from ..repetition import Repetition
|
||||||
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
|
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t, AutoSlots
|
||||||
from ..utils import remove_colinear_vertices, remove_duplicate_vertices
|
from ..utils import remove_colinear_vertices, remove_duplicate_vertices
|
||||||
|
|
||||||
@ -75,6 +76,7 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
locked: bool = False,
|
||||||
):
|
):
|
||||||
object.__setattr__(self, 'locked', False)
|
object.__setattr__(self, 'locked', False)
|
||||||
@ -85,6 +87,7 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
self.offset = offset
|
self.offset = offset
|
||||||
self.rotate(rotation)
|
self.rotate(rotation)
|
||||||
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
||||||
|
self.repetition = repetition
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Polygon':
|
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Polygon':
|
||||||
|
@ -7,7 +7,7 @@ from ..error import PatternError, PatternLockedError
|
|||||||
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t
|
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t
|
||||||
from ..traits import (PositionableImpl, LayerableImpl, DoseableImpl,
|
from ..traits import (PositionableImpl, LayerableImpl, DoseableImpl,
|
||||||
Rotatable, Mirrorable, Copyable, Scalable,
|
Rotatable, Mirrorable, Copyable, Scalable,
|
||||||
PivotableImpl, LockableImpl)
|
PivotableImpl, LockableImpl, RepeatableImpl)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import Polygon
|
from . import Polygon
|
||||||
@ -26,7 +26,8 @@ DEFAULT_POLY_NUM_POINTS = 24
|
|||||||
T = TypeVar('T', bound='Shape')
|
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.
|
Abstract class specifying functions common to all shapes.
|
||||||
"""
|
"""
|
||||||
|
@ -5,6 +5,7 @@ from numpy import pi, inf
|
|||||||
|
|
||||||
from . import Shape, Polygon, normalized_shape_tuple
|
from . import Shape, Polygon, normalized_shape_tuple
|
||||||
from .. import PatternError
|
from .. import PatternError
|
||||||
|
from ..repetition import Repetition
|
||||||
from ..traits import RotatableImpl
|
from ..traits import RotatableImpl
|
||||||
from ..utils import is_scalar, vector2, get_bit, normalize_mirror, layer_t, AutoSlots
|
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),
|
mirrored: Tuple[bool, bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
locked: bool = False,
|
||||||
):
|
):
|
||||||
object.__setattr__(self, 'locked', False)
|
object.__setattr__(self, 'locked', False)
|
||||||
@ -77,6 +79,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
|
|||||||
self.rotation = rotation
|
self.rotation = rotation
|
||||||
self.font_path = font_path
|
self.font_path = font_path
|
||||||
self.mirrored = mirrored
|
self.mirrored = mirrored
|
||||||
|
self.repetition = repetition
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'Text':
|
def __deepcopy__(self, memo: Dict = None) -> 'Text':
|
||||||
|
Loading…
Reference in New Issue
Block a user