[Mirrorable / Flippable] Bifurcate mirror into flip (relative to line) vs mirror (relative to own offset/origin)
This commit is contained in:
parent
accad3db9f
commit
44986bac67
11 changed files with 115 additions and 111 deletions
|
|
@ -26,7 +26,11 @@ from .scalable import (
|
|||
Scalable as Scalable,
|
||||
ScalableImpl as ScalableImpl,
|
||||
)
|
||||
from .mirrorable import Mirrorable as Mirrorable
|
||||
from .mirrorable import (
|
||||
Mirrorable as Mirrorable,
|
||||
Flippable as Flippable,
|
||||
FlippableImpl as FlippableImpl,
|
||||
)
|
||||
from .copyable import Copyable as Copyable
|
||||
from .annotatable import (
|
||||
Annotatable as Annotatable,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
from typing import Self
|
||||
from typing import Self, Any, TYPE_CHECKING, cast
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import numpy
|
||||
from numpy.typing import ArrayLike, NDArray
|
||||
|
||||
from ..error import MasqueError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .positionable import Positionable
|
||||
|
||||
|
||||
class Mirrorable(metaclass=ABCMeta):
|
||||
"""
|
||||
|
|
@ -11,10 +19,10 @@ class Mirrorable(metaclass=ABCMeta):
|
|||
@abstractmethod
|
||||
def mirror(self, axis: int = 0) -> Self:
|
||||
"""
|
||||
Mirror the entity across an axis.
|
||||
Mirror the entity across an axis through its origin, ignoring its offset.
|
||||
|
||||
Args:
|
||||
axis: Axis to mirror across.
|
||||
axis: Axis to mirror across (0: x-axis, 1: y-axis).
|
||||
|
||||
Returns:
|
||||
self
|
||||
|
|
@ -23,10 +31,11 @@ class Mirrorable(metaclass=ABCMeta):
|
|||
|
||||
def mirror2d(self, across_x: bool = False, across_y: bool = False) -> Self:
|
||||
"""
|
||||
Optionally mirror the entity across both axes
|
||||
Optionally mirror the entity across both axes through its origin.
|
||||
|
||||
Args:
|
||||
axes: (mirror_across_x, mirror_across_y)
|
||||
across_x: Mirror across x axis (flip y)
|
||||
across_y: Mirror across y axis (flip x)
|
||||
|
||||
Returns:
|
||||
self
|
||||
|
|
@ -38,30 +47,56 @@ class Mirrorable(metaclass=ABCMeta):
|
|||
return self
|
||||
|
||||
|
||||
#class MirrorableImpl(Mirrorable, metaclass=ABCMeta):
|
||||
# """
|
||||
# Simple implementation of `Mirrorable`
|
||||
# """
|
||||
# __slots__ = ()
|
||||
#
|
||||
# _mirrored: NDArray[numpy.bool]
|
||||
# """ Whether to mirror the instance across the x and/or y axes. """
|
||||
#
|
||||
# #
|
||||
# # Properties
|
||||
# #
|
||||
# # Mirrored property
|
||||
# @property
|
||||
# def mirrored(self) -> NDArray[numpy.bool]:
|
||||
# """ Whether to mirror across the [x, y] axes, respectively """
|
||||
# return self._mirrored
|
||||
#
|
||||
# @mirrored.setter
|
||||
# def mirrored(self, val: Sequence[bool]) -> None:
|
||||
# if is_scalar(val):
|
||||
# raise MasqueError('Mirrored must be a 2-element list of booleans')
|
||||
# self._mirrored = numpy.array(val, dtype=bool)
|
||||
#
|
||||
# #
|
||||
# # Methods
|
||||
# #
|
||||
class Flippable(metaclass=ABCMeta):
|
||||
"""
|
||||
Trait class for entities which can be mirrored relative to an external line.
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
@staticmethod
|
||||
def _check_flip_args(axis: int | None = None, *, x: float | None = None, y: float | None = None) -> tuple[int, float]:
|
||||
pivot = numpy.zeros(2)
|
||||
if axis is not None:
|
||||
if x is not None or y is not None:
|
||||
raise MasqueError('Cannot specify both axis and x or y')
|
||||
return axis, pivot
|
||||
if x is not None:
|
||||
if y is not None:
|
||||
raise MasqueError('Cannot specify both x and y')
|
||||
return 0, pivot + (x, 0)
|
||||
if y is not None:
|
||||
return 1, pivot + (0, y)
|
||||
raise MasqueError('Must specify one of axis, x, or y')
|
||||
|
||||
@abstractmethod
|
||||
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 x=0. 1 mirrors across y=0.
|
||||
x: Vertical line x=val to mirror across.
|
||||
y: Horizontal line y=val to mirror across.
|
||||
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class FlippableImpl(Flippable, metaclass=ABCMeta):
|
||||
"""
|
||||
Implementation of `Flippable` for objects which are `Mirrorable` and `Positionable`.
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
offset: NDArray[numpy.float64]
|
||||
""" `[x_offset, y_offset]` """
|
||||
|
||||
def flip_across(self, axis: int | None = None, *, x: float | None = None, y: float | None = None) -> Self:
|
||||
axis, pivot = self._check_flip_args(axis=axis, x=x, y=y)
|
||||
cast('Positionable', self).translate(-pivot)
|
||||
cast('Mirrorable', self).mirror(axis)
|
||||
self.offset[1 - axis] *= -1
|
||||
cast('Positionable', self).translate(+pivot)
|
||||
return self
|
||||
|
|
|
|||
Loading…
Reference in a new issue