Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions lapy/polygon.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""
import logging
import sys
from typing import Optional

import numpy as np
from scipy import sparse
Expand All @@ -22,15 +23,21 @@ class Polygon:
points : np.ndarray
Array of shape (n, d) containing coordinates of polygon vertices
in order, where d is 2 or 3 for 2D (x, y) or 3D (x, y, z) paths.
For closed polygons, the last point should not duplicate the first point.
closed : bool, default=False
If True, treats the path as a closed polygon. If False, treats it as
an open polyline.
If the last point duplicates the first point and closed is None,
the duplicate endpoint will be removed and the polygon will be
marked as closed automatically.
closed : bool or None, default=None
- If None (default): Auto-detect by checking if first and last points
are identical. If they are, removes duplicate endpoint and sets closed=True.
- If True: Treats the path as a closed polygon. If the last point duplicates
the first, it will be removed.
- If False: Treats the path as an open polyline regardless of endpoints.

Attributes
----------
points : np.ndarray
Polygon vertex coordinates, shape (n_points, d).
Polygon vertex coordinates, shape (n_points, d). For closed polygons,
the last point does not duplicate the first.
closed : bool
Whether the polygon is closed or open.
_is_2d : bool
Expand All @@ -46,23 +53,22 @@ class Polygon:
--------
>>> import numpy as np
>>> from lapy.polygon import Polygon
>>> # Create a 2D closed polygon (square)
>>> square = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
>>> poly = Polygon(square, closed=True)
>>> poly.is_2d()
>>> # Create a 2D closed polygon (square) - auto-detected
>>> square = np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
>>> poly = Polygon(square) # closed=None, will auto-detect and remove duplicate
>>> poly.closed
True
>>> poly.length()
4.0
>>> # Create a 3D open path
>>> path_3d = np.array([[0, 0, 0], [1, 0, 0], [1, 1, 0], [1, 1, 1]])
>>> poly3d = Polygon(path_3d, closed=False)
>>> poly3d.is_2d()
>>> poly.points.shape[0]
4
>>> # Explicitly open polygon
>>> path = np.array([[0, 0, 0], [1, 0, 0], [1, 1, 0], [1, 1, 1]])
>>> poly3d = Polygon(path, closed=False)
>>> poly3d.closed
False
"""

def __init__(self, points: np.ndarray, closed: bool = False):
def __init__(self, points: np.ndarray, closed: Optional[bool] = None):
self.points = np.array(points)
self.closed = closed

# Validate non-empty polygon
if self.points.size == 0:
Expand Down Expand Up @@ -93,6 +99,25 @@ def __init__(self, points: np.ndarray, closed: bool = False):
else:
raise ValueError("Points should have 2 or 3 coordinates")

# Auto-detect closed polygons or handle explicit closed parameter
if closed is None:
# Auto-detect: check if first and last points are identical
if len(self.points) > 1 and np.allclose(self.points[0], self.points[-1]):
# Remove duplicate endpoint
self.points = self.points[:-1]
self.closed = True
else:
# Not closed (endpoints differ)
self.closed = False
elif closed:
# Explicitly closed: remove duplicate endpoint if present
if len(self.points) > 1 and np.allclose(self.points[0], self.points[-1]):
self.points = self.points[:-1]
self.closed = True
else:
# Explicitly open
self.closed = False

def is_2d(self) -> bool:
"""Check if the polygon is 2D.

Expand Down
Loading