fixup! [Mirrorable / Flippable] Bifurcate mirror into flip (relative to line) vs mirror (relative to own offset/origin)
This commit is contained in:
parent
51ced2fe83
commit
2d63e72802
6 changed files with 41 additions and 17 deletions
|
|
@ -8,13 +8,13 @@ from numpy.typing import ArrayLike
|
||||||
from .ref import Ref
|
from .ref import Ref
|
||||||
from .ports import PortList, Port
|
from .ports import PortList, Port
|
||||||
from .utils import rotation_matrix_2d
|
from .utils import rotation_matrix_2d
|
||||||
from .traits import Flippable
|
from .traits import Mirrorable
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Abstract(PortList, Flippable):
|
class Abstract(PortList, Mirrorable):
|
||||||
"""
|
"""
|
||||||
An `Abstract` is a container for a name and associated ports.
|
An `Abstract` is a container for a name and associated ports.
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ class Abstract(PortList, Flippable):
|
||||||
Mirror the Abstract across an axis through its origin.
|
Mirror the Abstract across an axis through its origin.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
axis: Axis to mirror across (0: mirror across x axis, 1: mirror across y axis)
|
axis: Axis to mirror across (0: x-axis, 1: y-axis).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,12 @@ class Label(PositionableImpl, RepeatableImpl, AnnotatableImpl, Bounded, Pivotabl
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
"""
|
"""
|
||||||
|
axis, pivot = self._check_flip_args(axis=axis, x=x, y=y)
|
||||||
|
self.translate(-pivot)
|
||||||
if self.repetition is not None:
|
if self.repetition is not None:
|
||||||
self.repetition.flip_across(axis=axis, x=x, y=y)
|
self.repetition.mirror(axis)
|
||||||
|
self.offset[1 - axis] *= -1
|
||||||
|
self.translate(+pivot)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_bounds_single(self) -> NDArray[numpy.float64]:
|
def get_bounds_single(self) -> NDArray[numpy.float64]:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import numpy
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
from numpy.typing import ArrayLike, NDArray
|
from numpy.typing import ArrayLike, NDArray
|
||||||
|
|
||||||
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, Flippable, FlippableImpl
|
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, Flippable
|
||||||
from .utils import rotate_offsets_around, rotation_matrix_2d
|
from .utils import rotate_offsets_around, rotation_matrix_2d
|
||||||
from .error import PortError, format_stacktrace
|
from .error import PortError, format_stacktrace
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@functools.total_ordering
|
@functools.total_ordering
|
||||||
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, FlippableImpl):
|
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Flippable):
|
||||||
"""
|
"""
|
||||||
A point at which a `Device` can be snapped to another `Device`.
|
A point at which a `Device` can be snapped to another `Device`.
|
||||||
|
|
||||||
|
|
@ -99,6 +99,25 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, FlippableImpl):
|
||||||
self.ptype = ptype
|
self.ptype = ptype
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def flip_across(self, axis: int | None = None, *, x: float | None = None, y: float | None = None) -> Self:
|
||||||
|
"""
|
||||||
|
Mirror the object across a line.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
axis: Axis to mirror across. 0 mirrors across y=0. 1 mirrors across x=0.
|
||||||
|
x: Vertical line x=val to mirror across.
|
||||||
|
y: Horizontal line y=val to mirror across.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
"""
|
||||||
|
axis, pivot = self._check_flip_args(axis=axis, x=x, y=y)
|
||||||
|
self.translate(-pivot)
|
||||||
|
self.mirror(axis)
|
||||||
|
self.offset[1 - axis] *= -1
|
||||||
|
self.translate(+pivot)
|
||||||
|
return self
|
||||||
|
|
||||||
def mirror(self, axis: int = 0) -> Self:
|
def mirror(self, axis: int = 0) -> Self:
|
||||||
if self.rotation is not None:
|
if self.rotation is not None:
|
||||||
self.rotation *= -1
|
self.rotation *= -1
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
@functools.total_ordering
|
@functools.total_ordering
|
||||||
class Ref(
|
class Ref(
|
||||||
FlippableImpl, PositionableImpl, RotatableImpl, ScalableImpl,
|
PositionableImpl, RotatableImpl, ScalableImpl, Mirrorable,
|
||||||
PivotableImpl, Copyable, RepeatableImpl, AnnotatableImpl,
|
PivotableImpl, Copyable, RepeatableImpl, AnnotatableImpl,
|
||||||
|
FlippableImpl, Flippable,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
`Ref` provides basic support for nesting Pattern objects within each other.
|
`Ref` provides basic support for nesting Pattern objects within each other.
|
||||||
|
|
@ -169,8 +170,6 @@ class Ref(
|
||||||
def mirror(self, axis: int = 0) -> Self:
|
def mirror(self, axis: int = 0) -> Self:
|
||||||
self.mirror_target(axis)
|
self.mirror_target(axis)
|
||||||
self.rotation *= -1
|
self.rotation *= -1
|
||||||
if self.repetition is not None:
|
|
||||||
self.repetition.mirror(axis)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def mirror_target(self, axis: int = 0) -> Self:
|
def mirror_target(self, axis: int = 0) -> Self:
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,6 @@ class Circle(PositionableImpl, Shape):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def mirror(self, axis: int = 0) -> 'Circle': # noqa: ARG002 (axis unused)
|
def mirror(self, axis: int = 0) -> 'Circle': # noqa: ARG002 (axis unused)
|
||||||
self.offset[axis - 1] *= -1
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def scale_by(self, c: float) -> 'Circle':
|
def scale_by(self, c: float) -> 'Circle':
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ from numpy.typing import ArrayLike, NDArray
|
||||||
|
|
||||||
from ..error import MasqueError
|
from ..error import MasqueError
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .positionable import Positionable
|
from .positionable import Positionable
|
||||||
|
from .repeatable import Repeatable
|
||||||
|
|
||||||
|
|
||||||
class Mirrorable(metaclass=ABCMeta):
|
class Mirrorable(metaclass=ABCMeta):
|
||||||
|
|
@ -54,7 +54,7 @@ class Flippable(metaclass=ABCMeta):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_flip_args(axis: int | None = None, *, x: float | None = None, y: float | None = None) -> tuple[int, float]:
|
def _check_flip_args(axis: int | None = None, *, x: float | None = None, y: float | None = None) -> tuple[int, NDArray[numpy.float64]]:
|
||||||
pivot = numpy.zeros(2)
|
pivot = numpy.zeros(2)
|
||||||
if axis is not None:
|
if axis is not None:
|
||||||
if x is not None or y is not None:
|
if x is not None or y is not None:
|
||||||
|
|
@ -63,9 +63,9 @@ class Flippable(metaclass=ABCMeta):
|
||||||
if x is not None:
|
if x is not None:
|
||||||
if y is not None:
|
if y is not None:
|
||||||
raise MasqueError('Cannot specify both x and y')
|
raise MasqueError('Cannot specify both x and y')
|
||||||
return 0, pivot + (x, 0)
|
return 1, pivot + (x, 0)
|
||||||
if y is not None:
|
if y is not None:
|
||||||
return 1, pivot + (0, y)
|
return 0, pivot + (0, y)
|
||||||
raise MasqueError('Must specify one of axis, x, or y')
|
raise MasqueError('Must specify one of axis, x, or y')
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|
@ -84,9 +84,10 @@ class Flippable(metaclass=ABCMeta):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FlippableImpl(Flippable, metaclass=ABCMeta):
|
class FlippableImpl(Flippable, Repeatable, metaclass=ABCMeta):
|
||||||
"""
|
"""
|
||||||
Implementation of `Flippable` for objects which are `Mirrorable` and `Positionable`.
|
Implementation of `Flippable` for objects which are `Mirrorable`, `Positionable`,
|
||||||
|
and `Repeatable`.
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
|
@ -97,6 +98,8 @@ class FlippableImpl(Flippable, metaclass=ABCMeta):
|
||||||
axis, pivot = self._check_flip_args(axis=axis, x=x, y=y)
|
axis, pivot = self._check_flip_args(axis=axis, x=x, y=y)
|
||||||
cast('Positionable', self).translate(-pivot)
|
cast('Positionable', self).translate(-pivot)
|
||||||
cast('Mirrorable', self).mirror(axis)
|
cast('Mirrorable', self).mirror(axis)
|
||||||
|
if self.repetition is not None:
|
||||||
|
self.repetition.mirror(axis)
|
||||||
self.offset[1 - axis] *= -1
|
self.offset[1 - axis] *= -1
|
||||||
cast('Positionable', self).translate(+pivot)
|
cast('Positionable', self).translate(+pivot)
|
||||||
return self
|
return self
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue