"""alpha_shape_concave_hull — Concave hull via Delaunay alpha-shape filtering.""" from __future__ import annotations def alpha_shape_concave_hull( points: list[tuple[float, float]], alpha: float, ) -> "shapely.geometry.base.BaseGeometry | None": """Compute the alpha-shape (concave hull) of a 2-D point set. Performs a Delaunay triangulation over the input points, then keeps only those triangles whose circumscribed circle radius is <= alpha. The remaining triangles are merged via unary_union. Args: points: List of (x, y) coordinate pairs. Must have >= 4 elements. alpha: Alpha parameter controlling concavity (smaller = more concave). Triangles with circumradius > alpha are discarded. Returns: A shapely geometry (Polygon, MultiPolygon, or GeometryCollection) representing the alpha-shape, or None if len(points) < 4 or no triangles survive the alpha filter (shapely is required). """ if len(points) < 4: return None try: import numpy as np from shapely.geometry import MultiPoint from shapely.ops import triangulate, unary_union except ImportError: return None mp = MultiPoint(points) triangles = triangulate(mp) valid = [] for tri in triangles: coords = list(tri.exterior.coords) a_pt = np.array(coords[0]) b_pt = np.array(coords[1]) c_pt = np.array(coords[2]) # Circumradius via the formula R = (abc) / (4 * Area) ab = np.linalg.norm(b_pt - a_pt) bc = np.linalg.norm(c_pt - b_pt) ca = np.linalg.norm(a_pt - c_pt) # Area via cross product area = abs( (b_pt[0] - a_pt[0]) * (c_pt[1] - a_pt[1]) - (c_pt[0] - a_pt[0]) * (b_pt[1] - a_pt[1]) ) / 2.0 if area == 0: continue circumradius = (ab * bc * ca) / (4.0 * area) if circumradius <= alpha: valid.append(tri) if not valid: return None return unary_union(valid)