Module datatap.geometry.polygon

Expand source code
from __future__ import annotations

from typing import Any, Generator, Sequence, Tuple, Union

from .point import Point, PointJson
from ..utils import basic_repr

PolygonJson = Sequence[PointJson]

class Polygon:
        """
        A polygon in 2D space.
        """

        points: Sequence[Point]
        """
        The vertices of this polygon.
        """

        @staticmethod
        def from_json(json: PolygonJson) -> Polygon:
                """
                Creates a `Polygon` from a `PolygonJson`.
                """
                return Polygon([Point.from_json(pt) for pt in json])

        def __init__(self, points: Sequence[Point]):
                self.points = points

                if len(self.points) < 3:
                        raise ValueError(f"A polygon must have at least three points; failed on polygon {repr(self)}")

        def scale(self, factor: Union[float, int, Tuple[float, float], Point]) -> Polygon:
                """
                Resizes the polygon according to `factor`. The scaling factor can either
                be a scalar (`int` or `float`), in which case the polygon will be scaled
                by the same factor on both axes, or a point-like (`Tuple[float, float]`
                or `Point`), in which case the polygon will be scaled independently on
                each axis.
                """
                return Polygon([p.scale(factor) for p in self.points])

        def to_json(self) -> PolygonJson:
                """
                Serializes this object as a `PolygonJson`.
                """
                return [point.to_json() for point in self.points]

        def assert_valid(self) -> None:
                """
                Ensures that this polygon is valid on the unit plane.
                """
                for point in self.points:
                        point.assert_valid()
                # TODO(mdsavage): check for self-intersection?

        def __repr__(self) -> str:
                return basic_repr("Polygon", self.points)

        def __eq__(self, other: Any) -> bool:
                # TODO(mdsavage): currently, this requires the points to be in the same order, not just represent the same polygon
                if not isinstance(other, Polygon):
                        return NotImplemented
                return self.points == other.points

        def __mul__(self, o: Union[int, float]) -> Polygon:
                if not isinstance(o, (int, float)): # type: ignore - pyright complains about the isinstance check being redundant
                        return NotImplemented
                return Polygon([p * o for p in self.points])

        def __iter__(self) -> Generator[Point, None, None]:
                yield from self.points

Classes

class Polygon (points: Sequence[Point])

A polygon in 2D space.

Expand source code
class Polygon:
        """
        A polygon in 2D space.
        """

        points: Sequence[Point]
        """
        The vertices of this polygon.
        """

        @staticmethod
        def from_json(json: PolygonJson) -> Polygon:
                """
                Creates a `Polygon` from a `PolygonJson`.
                """
                return Polygon([Point.from_json(pt) for pt in json])

        def __init__(self, points: Sequence[Point]):
                self.points = points

                if len(self.points) < 3:
                        raise ValueError(f"A polygon must have at least three points; failed on polygon {repr(self)}")

        def scale(self, factor: Union[float, int, Tuple[float, float], Point]) -> Polygon:
                """
                Resizes the polygon according to `factor`. The scaling factor can either
                be a scalar (`int` or `float`), in which case the polygon will be scaled
                by the same factor on both axes, or a point-like (`Tuple[float, float]`
                or `Point`), in which case the polygon will be scaled independently on
                each axis.
                """
                return Polygon([p.scale(factor) for p in self.points])

        def to_json(self) -> PolygonJson:
                """
                Serializes this object as a `PolygonJson`.
                """
                return [point.to_json() for point in self.points]

        def assert_valid(self) -> None:
                """
                Ensures that this polygon is valid on the unit plane.
                """
                for point in self.points:
                        point.assert_valid()
                # TODO(mdsavage): check for self-intersection?

        def __repr__(self) -> str:
                return basic_repr("Polygon", self.points)

        def __eq__(self, other: Any) -> bool:
                # TODO(mdsavage): currently, this requires the points to be in the same order, not just represent the same polygon
                if not isinstance(other, Polygon):
                        return NotImplemented
                return self.points == other.points

        def __mul__(self, o: Union[int, float]) -> Polygon:
                if not isinstance(o, (int, float)): # type: ignore - pyright complains about the isinstance check being redundant
                        return NotImplemented
                return Polygon([p * o for p in self.points])

        def __iter__(self) -> Generator[Point, None, None]:
                yield from self.points

Class variables

var points : Sequence[Point]

The vertices of this polygon.

Static methods

def from_json(json: PolygonJson) ‑> Polygon

Creates a Polygon from a PolygonJson.

Expand source code
@staticmethod
def from_json(json: PolygonJson) -> Polygon:
        """
        Creates a `Polygon` from a `PolygonJson`.
        """
        return Polygon([Point.from_json(pt) for pt in json])

Methods

def assert_valid(self) ‑> None

Ensures that this polygon is valid on the unit plane.

Expand source code
def assert_valid(self) -> None:
        """
        Ensures that this polygon is valid on the unit plane.
        """
        for point in self.points:
                point.assert_valid()
        # TODO(mdsavage): check for self-intersection?
def scale(self, factor: Union[float, int, Tuple[float, float], Point]) ‑> Polygon

Resizes the polygon according to factor. The scaling factor can either be a scalar (int or float), in which case the polygon will be scaled by the same factor on both axes, or a point-like (Tuple[float, float] or Point), in which case the polygon will be scaled independently on each axis.

Expand source code
def scale(self, factor: Union[float, int, Tuple[float, float], Point]) -> Polygon:
        """
        Resizes the polygon according to `factor`. The scaling factor can either
        be a scalar (`int` or `float`), in which case the polygon will be scaled
        by the same factor on both axes, or a point-like (`Tuple[float, float]`
        or `Point`), in which case the polygon will be scaled independently on
        each axis.
        """
        return Polygon([p.scale(factor) for p in self.points])
def to_json(self) ‑> Sequence[Tuple[float, float]]

Serializes this object as a PolygonJson.

Expand source code
def to_json(self) -> PolygonJson:
        """
        Serializes this object as a `PolygonJson`.
        """
        return [point.to_json() for point in self.points]