Source code for gon.core.polygon

from functools import partial
from itertools import chain
from typing import (Optional,
                    Sequence)

from clipping.planar import (complete_intersect_multisegment_with_polygon,
                             complete_intersect_polygons,
                             complete_intersect_regions,
                             complete_intersect_segment_with_polygon,
                             subtract_multipolygon_from_polygon,
                             subtract_polygon_from_multisegment,
                             subtract_polygon_from_segment,
                             subtract_polygons,
                             symmetric_subtract_polygon_from_multisegment,
                             symmetric_subtract_polygon_from_segment,
                             symmetric_subtract_polygons,
                             unite_multisegment_with_polygon,
                             unite_polygons,
                             unite_segment_with_polygon)
from ground.hints import Scalar
from locus import segmental
from orient.planar import (multisegment_in_polygon,
                           point_in_polygon,
                           polygon_in_polygon,
                           region_in_multiregion,
                           segment_in_polygon)
from reprit.base import generate_repr
from sect.decomposition import Graph
from sect.triangulation import Triangulation

from .angle import Angle
from .compound import (Compound,
                       Indexable,
                       Linear,
                       Location,
                       Relation,
                       Shaped)
from .contour import Contour
from .geometry import Geometry
from .iterable import (flatten,
                       non_negative_min)
from .multipoint import Multipoint
from .packing import pack_mix
from .point import Point
from .segment import Segment
from .utils import (to_point_nearest_segment,
                    to_segment_nearest_segment)

Triangulation = Triangulation


class Polygon(Indexable[Scalar], Shaped[Scalar]):
    __slots__ = ('_border', '_holes', '_holes_set', '_locate',
                 '_point_nearest_edge', '_segment_nearest_edge')

[docs] def __init__(self, border: Contour[Scalar], holes: Optional[Sequence[Contour[Scalar]]] = None ) -> None: """ Initializes polygon. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) """ if holes is None: holes = [] self._border, self._holes, self._holes_set = (border, holes, frozenset(holes)) context = self._context self._locate = partial(point_in_polygon, polygon=self, context=context) edges = self.edges self._point_nearest_edge, self._segment_nearest_edge = ( partial(to_point_nearest_segment, context, edges), partial(to_segment_nearest_segment, context, edges) )
__repr__ = generate_repr(__init__)
[docs] def __and__(self, other: Compound) -> Compound: """ Returns intersection of the polygon with the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon & polygon == polygon True """ if isinstance(other, Segment): return complete_intersect_segment_with_polygon( other, self, context=self._context ) elif isinstance(other, Linear): return complete_intersect_multisegment_with_polygon( other, self, context=self._context ) else: return ((complete_intersect_polygons(self, other, context=self._context) if self.holes or other.holes else complete_intersect_regions(self.border, other.border, context=self._context)) if isinstance(other, Polygon) else NotImplemented)
__rand__ = __and__
[docs] def __contains__(self, point: Point) -> bool: """ Checks if the polygon contains the point. Time complexity: ``O(log vertices_count)`` expected after indexing, ``O(vertices_count)`` worst after indexing or without it Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> Point(0, 0) in polygon True >>> Point(1, 1) in polygon True >>> Point(2, 2) in polygon True >>> Point(3, 3) in polygon False >>> Point(4, 3) in polygon True >>> Point(5, 2) in polygon True >>> Point(6, 1) in polygon True >>> Point(7, 0) in polygon False """ return bool(self.locate(point))
[docs] def __eq__(self, other: 'Polygon') -> bool: """ Checks if polygons are equal. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon == polygon True """ return self is other or (self.border == other.border and self._holes_set == other._holes_set if isinstance(other, Polygon) else NotImplemented)
[docs] def __ge__(self, other: Compound) -> bool: """ Checks if the polygon is a superset of the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon >= polygon True """ return (other is self._context.empty or self == other or (self.relate(other) in (Relation.EQUAL, Relation.COMPONENT, Relation.ENCLOSED, Relation.WITHIN) if isinstance(other, Compound) else NotImplemented))
[docs] def __gt__(self, other: Compound) -> bool: """ Checks if the polygon is a strict superset of the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon > polygon False """ return (other is self._context.empty or self != other and (self.relate(other) in (Relation.COMPONENT, Relation.ENCLOSED, Relation.WITHIN) if isinstance(other, Compound) else NotImplemented))
[docs] def __hash__(self) -> int: """ Returns hash value of the polygon. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> hash(polygon) == hash(polygon) True """ return hash((self.border, self._holes_set))
[docs] def __le__(self, other: Compound) -> bool: """ Checks if the polygon is a subset of the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon <= polygon True """ return (self == other or not isinstance(other, (Multipoint, Linear)) and (self.relate(other) in (Relation.COVER, Relation.ENCLOSES, Relation.COMPOSITE, Relation.EQUAL) if isinstance(other, Shaped) else NotImplemented))
[docs] def __lt__(self, other: Compound) -> bool: """ Checks if the polygon is a strict subset of the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon < polygon False """ return (self != other and not isinstance(other, (Multipoint, Linear)) and (self.relate(other) in (Relation.COVER, Relation.ENCLOSES, Relation.COMPOSITE) if isinstance(other, Shaped) else NotImplemented))
[docs] def __or__(self, other: Compound) -> Compound: """ Returns union of the polygon with the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Multipolygon >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon | polygon == polygon True """ return (self._unite_with_multipoint(other) if isinstance(other, Multipoint) else (unite_segment_with_polygon(other, self, context=self._context) if isinstance(other, Segment) else (unite_multisegment_with_polygon(other, self, context=self._context) if isinstance(other, Linear) else (unite_polygons(self, other, context=self._context) if isinstance(other, Polygon) else NotImplemented))))
__ror__ = __or__
[docs] def __rsub__(self, other: Compound) -> Compound: """ Returns difference of the other geometry with the polygon. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) """ return (subtract_polygon_from_segment(other, self, context=self._context) if isinstance(other, Segment) else (subtract_polygon_from_multisegment(other, self, context=self._context) if isinstance(other, Linear) else NotImplemented))
[docs] def __sub__(self, other: Compound) -> Compound: """ Returns difference of the polygon with the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import EMPTY, Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon - polygon is EMPTY True """ return (self if isinstance(other, (Linear, Multipoint)) else (subtract_polygons(self, other, context=self._context) if isinstance(other, Polygon) else NotImplemented))
[docs] def __xor__(self, other: Compound) -> Compound: """ Returns symmetric difference of the polygon with the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import EMPTY, Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon ^ polygon is EMPTY True """ if isinstance(other, Multipoint): return self._unite_with_multipoint(other) elif isinstance(other, Segment): return symmetric_subtract_polygon_from_segment( other, self, context=self._context ) elif isinstance(other, Linear): return symmetric_subtract_polygon_from_multisegment( other, self, context=self._context ) else: return (symmetric_subtract_polygons(self, other, context=self._context) if isinstance(other, Polygon) else NotImplemented)
__rxor__ = __xor__ @property def area(self) -> Scalar: """ Returns area of the polygon. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.area == 32 True """ region_signed_measure = self._context.region_signed_area return (abs(region_signed_measure(self.border)) - sum(abs(region_signed_measure(hole)) for hole in self.holes)) @property def border(self) -> Contour: """ Returns border of the polygon. Time complexity: ``O(1)`` Memory complexity: ``O(1)`` >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.border == Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]) True """ return self._border @property def centroid(self) -> Point: """ Returns centroid of the polygon. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.centroid == Point(3, 3) True """ return self._context.polygon_centroid(self) @property def convex_hull(self) -> 'Polygon': """ Returns convex hull of the polygon. Time complexity: ``O(border_vertices_count)`` if convex already, ``O(border_vertices_count * log border_vertices_count)`` -- otherwise Memory complexity: ``O(1)`` if convex already, ``O(border_vertices_count)`` -- otherwise where ``border_vertices_count = len(self.border.vertices)``. >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.convex_hull == Polygon(polygon.border, []) True """ if self.is_convex: return self else: context = self._context border = context.contour_cls(context.points_convex_hull( self.border.vertices )) return context.polygon_cls(border, []) @property def edges(self) -> Sequence[Segment]: """ Returns edges of the polygon. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon, Segment >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.edges == [Segment(Point(0, 6), Point(0, 0)), ... Segment(Point(0, 0), Point(6, 0)), ... Segment(Point(6, 0), Point(6, 6)), ... Segment(Point(6, 6), Point(0, 6)), ... Segment(Point(4, 2), Point(2, 2)), ... Segment(Point(2, 2), Point(2, 4)), ... Segment(Point(2, 4), Point(4, 4)), ... Segment(Point(4, 4), Point(4, 2))] True """ return list(chain(self.border.segments, flatten(hole.segments for hole in self.holes))) @property def holes(self) -> Sequence[Contour]: """ Returns holes of the polygon. Time complexity: ``O(holes_count)`` Memory complexity: ``O(holes_count)`` where ``holes_count = len(self.holes)``. >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.holes == [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])] True """ return list(self._holes) @property def is_convex(self) -> bool: """ Checks if the polygon is convex. Time complexity: ``O(len(self.border.vertices))`` Memory complexity: ``O(1)`` >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.is_convex False >>> polygon.convex_hull.is_convex True """ return not self.holes and self._context.is_region_convex(self.border) @property def perimeter(self) -> Scalar: """ Returns perimeter of the polygon. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.perimeter == 32 True """ return self.border.length + sum(hole.length for hole in self.holes)
[docs] def distance_to(self, other: Geometry) -> Scalar: """ Returns distance between the polygon and the other geometry. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.distance_to(polygon) == 0 True """ return (self._distance_to_point(other) if isinstance(other, Point) else (non_negative_min(self._distance_to_point(point) for point in other.points) if isinstance(other, Multipoint) else (self._distance_to_segment(other) if isinstance(other, Segment) else (non_negative_min(self._distance_to_segment(segment) for segment in other.segments) if isinstance(other, Linear) else ((non_negative_min(self._linear_distance_to_segment(edge) for edge in other.edges) if self.disjoint(other) else 0) if isinstance(other, Polygon) else other.distance_to(self))))))
[docs] def index(self) -> None: """ Pre-processes the polygon to potentially improve queries. Time complexity: ``O(vertices_count * log vertices_count)`` expected, ``O(vertices_count ** 2)`` worst Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.index() """ self._locate = Graph.from_polygon(self, context=self._context).locate tree = segmental.Tree(self.edges) self._point_nearest_edge, self._segment_nearest_edge = ( tree.nearest_to_point_segment, tree.nearest_segment)
[docs] def locate(self, point: Point) -> Location: """ Finds location of the point relative to the polygon. Time complexity: ``O(log vertices_count)`` expected after indexing, ``O(vertices_count)`` worst after indexing or without it Memory complexity: ``O(1)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.locate(Point(0, 0)) is Location.BOUNDARY True >>> polygon.locate(Point(1, 1)) is Location.INTERIOR True >>> polygon.locate(Point(2, 2)) is Location.BOUNDARY True >>> polygon.locate(Point(3, 3)) is Location.EXTERIOR True >>> polygon.locate(Point(4, 3)) is Location.BOUNDARY True >>> polygon.locate(Point(5, 2)) is Location.INTERIOR True >>> polygon.locate(Point(6, 1)) is Location.BOUNDARY True >>> polygon.locate(Point(7, 0)) is Location.EXTERIOR True """ return self._locate(point)
[docs] def relate(self, other: Compound) -> Relation: """ Finds relation between the polygon and the other geometry. Time complexity: ``O(vertices_count * log vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.relate(polygon) is Relation.EQUAL True """ return (segment_in_polygon(other, self) if isinstance(other, Segment) else (multisegment_in_polygon(other, self) if isinstance(other, Linear) else (polygon_in_polygon(other, self) if isinstance(other, Polygon) else other.relate(self).complement)))
[docs] def rotate(self, angle: Angle, point: Optional[Point] = None) -> 'Polygon': """ Rotates the polygon by given angle around given point. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Angle, Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.rotate(Angle(1, 0)) == polygon True >>> (polygon.rotate(Angle(0, 1), Point(1, 1)) ... == Polygon(Contour([Point(2, 0), Point(2, 6), Point(-4, 6), ... Point(-4, 0)]), ... [Contour([Point(0, 2), Point(-2, 2), Point(-2, 4), ... Point(0, 4)])])) True """ return (self._context.rotate_polygon_around_origin(self, angle.cosine, angle.sine) if point is None else self._context.rotate_polygon(self, angle.cosine, angle.sine, point))
[docs] def scale(self, factor_x: Scalar, factor_y: Optional[Scalar] = None) -> 'Polygon': """ Scales the polygon by given factor. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.scale(1) == polygon True >>> (polygon.scale(1, 2) ... == Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 12), ... Point(0, 12)]), ... [Contour([Point(2, 4), Point(2, 8), Point(4, 8), ... Point(4, 4)])])) True """ return self._context.scale_polygon( self, factor_x, factor_x if factor_y is None else factor_y )
[docs] def translate(self, step_x: Scalar, step_y: Scalar) -> 'Polygon[Scalar]': """ Translates the polygon by given step. Time complexity: ``O(vertices_count)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> (polygon.translate(1, 2) ... == Polygon(Contour([Point(1, 2), Point(7, 2), Point(7, 8), ... Point(1, 8)]), ... [Contour([Point(3, 4), Point(3, 6), Point(5, 6), ... Point(5, 4)])])) True """ return self._context.translate_polygon(self, step_x, step_y)
[docs] def triangulate(self) -> Triangulation: """ Returns triangulation of the polygon. Time complexity: ``O(vertices_count ** 2)`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> triangulation = polygon.triangulate() >>> (triangulation.triangles() ... == [Contour([Point(4, 4), Point(6, 0), Point(6, 6)]), ... Contour([Point(4, 2), Point(6, 0), Point(4, 4)]), ... Contour([Point(0, 6), Point(4, 4), Point(6, 6)]), ... Contour([Point(0, 0), Point(2, 2), Point(0, 6)]), ... Contour([Point(0, 0), Point(6, 0), Point(4, 2)]), ... Contour([Point(0, 6), Point(2, 4), Point(4, 4)]), ... Contour([Point(0, 6), Point(2, 2), Point(2, 4)]), ... Contour([Point(0, 0), Point(4, 2), Point(2, 2)])]) True """ return Triangulation.constrained_delaunay(self, context=self._context)
[docs] def validate(self) -> None: """ Checks if the polygon is valid. Time complexity: ``O(vertices_count * log (vertices_count))`` Memory complexity: ``O(vertices_count)`` where .. code-block:: python vertices_count = (len(self.border.vertices) + sum(len(hole.vertices)\ for hole in self.holes)) >>> from gon.base import Contour, Point, Polygon >>> polygon = Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6), ... Point(0, 6)]), ... [Contour([Point(2, 2), Point(2, 4), Point(4, 4), ... Point(4, 2)])]) >>> polygon.validate() """ self.border.validate() if self.holes: for hole in self.holes: hole.validate() context = self._context relation = region_in_multiregion(self.border, self.holes, context=context) if not (relation is Relation.COVER or relation is Relation.ENCLOSES): raise ValueError('Holes should lie inside the border.') border_minus_holes = ( subtract_multipolygon_from_polygon( context.polygon_cls(self.border, []), context.multipolygon_cls([context.polygon_cls(hole, []) for hole in self.holes]) ) if len(self.holes) > 1 else subtract_polygons( context.polygon_cls(self.border, []), context.polygon_cls(self.holes[0], []) ) ) if border_minus_holes != self: raise ValueError('Holes should not tear polygon apart.')
def _distance_to_point(self, other: Point) -> Scalar: return self._context.sqrt( self._squared_distance_to_exterior_point(other) if self._locate(other) is Location.EXTERIOR else 0 ) def _distance_to_segment(self, other: Segment) -> Scalar: return (self._linear_distance_to_segment(other) if (self._locate(other.start) is Location.EXTERIOR and self._locate(other.end) is Location.EXTERIOR) else 0) def _linear_distance_to_segment(self, other: Segment) -> Scalar: return self._context.segments_squared_distance( self._segment_nearest_edge(other), other ) def _squared_distance_to_exterior_point(self, other: Point) -> Scalar: return self._context.segment_point_squared_distance( self._point_nearest_edge(other), other ) def _unite_with_multipoint(self, other: Multipoint) -> Compound: return pack_mix(other - self, self._context.empty, self, self._context.empty, self._context.mix_cls)