# SPDX-License-Identifier: GPL-2.0-or-later OR AGPL-3.0-or-later OR CERN-OHL-S-2.0+
import abc
from typing import Union
from ..typing import MultiT, cast_MultiT
from . import property_ as prp, mask as msk
__all__ = ["MaskEdge", "Join", "Intersect"]
_MaskOrEdge = Union[msk._Mask, "_Edge"]
class _EdgeProperty(prp.Property):
"""`_EdgeProperty` is a `Property` for a `_Edge` object.
"""
def __init__(self, *, edge: "_Edge", name: str):
super().__init__(str(edge) + "." + name)
self.edge = edge
self.prop_name = name
class _DualEdgeProperty(prp.Property):
"""`_EdgeProperty` is a `Property` related to an `_Edge` object
and a second `_Edge` or `Mask` object.
"""
def __init__(self, *,
edge1: "_Edge", edge2: _MaskOrEdge, name: str, commutative: bool, allow_mask2: bool,
):
assert (
isinstance(edge2, _Edge) or (isinstance(edge2, msk._Mask) and allow_mask2)
), "Internal error"
if commutative:
full_name = f"{name}({edge1.name},{edge2.name})"
else:
full_name = f"{edge1.name}.{name}({edge2.name})"
super().__init__(full_name)
self.edge1 = edge1
self.edge2 = edge2
self.prop_name = name
class _Edge(abc.ABC):
"""_Edge is a base class representing the edges of shape drawn on a mask.
It is used to define it's own operation with their own semantics used in
rules. For example intersection of edges has a different meaning than the
intersecton of shapes on a mask.
"""
@abc.abstractmethod
def __init__(self, *, name: str):
self.name = name
self.length = _EdgeProperty(edge=self, name="length")
def __str__(self):
return self.name
def enclosed_by(self, other: _MaskOrEdge) -> prp.Property:
return _DualEdgeProperty(
edge1=self, edge2=other, name="enclosed_by",
commutative=False, allow_mask2=True,
)
def interact_with(self, other: _MaskOrEdge) -> "_Edge":
return _DualEdgeOperation(
edge1=self, edge2=other, name="interact_with", allow_mask2=True,
)
class _DualEdgeOperation(_Edge):
"""`_DualEdgeOperation` represents the resulting `_Edge` from an
operation performed on a `_Edge` object and a `_Edge` or `Mask` object.
"""
def __init__(self, *,
edge1: _Edge, edge2: _MaskOrEdge, name: str, allow_mask2: bool=False,
):
assert (
isinstance(edge2, _Edge) or (allow_mask2 and isinstance(edge2, msk._Mask))
), "Internal error"
super().__init__(name=f"{edge1.name}.{name}({edge2.name})")
self.edge1 = edge1
self.edge2 = edge2
self.operation = name
[docs]class MaskEdge(_Edge):
"""Objects of this class represent the edges of shapes present on a `Mask`
object.
"""
def __init__(self, mask: msk._Mask):
self.mask = mask
super().__init__(name=f"edge({mask.name})")
[docs]class Join(_Edge):
"""`Joim` represent the resulting `_Edge` object from joining
the edges from two or more `_Edge` objects.
"""
def __init__(self, edges: MultiT[_Edge]):
self.edges = edges = cast_MultiT(edges)
super().__init__(name="join({})".format(",".join(str(edge) for edge in edges)))
[docs]class Intersect(_Edge):
"""`Joim` is the resulting `_Edge` object representing the overlapping
parts of the edges from two or more `_Edge` objects.
Crossing edges can result in points but the handling of this is application
dependent.
"""
def __init__(self, edges: MultiT[_MaskOrEdge]):
edges = cast_MultiT(edges)
if not any(isinstance(edge, _Edge) for edge in edges):
raise TypeError("at least one element of edges has to be of type 'Edge'")
self.edges = edges
super().__init__(name="intersect({})".format(",".join(str(edge) for edge in edges)))