Source code for pdkmaster.technology.edge

# SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-or-later OR CERN-OHL-S-2.0+ OR Apache-2.0
import abc
from typing import Union

from ..typing import MultiT, cast_MultiT
from . import property_ as _prp, mask as _msk


__all__ = [
    "EdgeT", "MaskOrEdgeT",
    "MaskEdge", "Join", "Intersect",
]


class _EdgeProperty(_prp._Property):
    """`_EdgeProperty` is a `Property` for a `_Edge` object.
    """
    def __init__(self, *, edge: "_Edge", name: str):
        super().__init__(name=(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: "EdgeT", edge2: "MaskOrEdgeT", name: str, commutative: bool, allow_mask2: bool,
    ):
        assert (
            isinstance(edge2, _Edge) or (isinstance(edge2, _msk.MaskT) 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__(name=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: _prp.PropertyT = _EdgeProperty(edge=self, name="length")

    def __str__(self):
        return self.name

    def enclosed_by(self, other: "MaskOrEdgeT") -> _prp.PropertyT:
        return _DualEdgeProperty(
            edge1=self, edge2=other, name="enclosed_by",
            commutative=False, allow_mask2=True,
        )

    def interact_with(self, other: "MaskOrEdgeT") -> "EdgeT":
        return _DualEdgeOperation(
            edge1=self, edge2=other, name="interact_with", allow_mask2=True,
        )
EdgeT = _Edge
MaskOrEdgeT = Union[_msk.MaskT, EdgeT]


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: MaskOrEdgeT, name: str, allow_mask2: bool=False,
    ):
        assert (
            isinstance(edge2, _Edge) or (allow_mask2 and isinstance(edge2, _msk.MaskT))
        ), "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.MaskT): 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[EdgeT]): 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[MaskOrEdgeT]): 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)))