Source code for cgeom.algorithms._convexhull

import math

import numpy as np


[docs] class ConvexHull: """Class to find the convex hull of a set of points using the Gift Wrapping algorithm"" Reference: R.A. Jarvis, On the identification of the convex hull of a finite set of points in the plane, Information Processing Letters, Volume 2, Issue 1, 1973, Pages 18-21, ISSN 0020-0190, https://doi.org/10.1016/0020-0190(73)90020-3. Attributes: points (np.array): Array of points """
[docs] def __init__(self, points): """Initialize the convex hull with a set of 2D points. Args: points: Collection of 2D points as a list, tuple, or numpy array with shape (n, 2). Raises: pydantic.ValidationError: If fewer than 3 points, all collinear, non-numeric, or wrong shape. """ from cgeom.elements.models import ConvexHullInput validated = ConvexHullInput(points=points) self.points = np.array(validated.points)
[docs] def find_low_right(self): """Find the point with the lowest y-coordinate (rightmost if tied). Returns: list: The [x, y] coordinates of the lowest-rightmost point. """ lower_y = float("inf") for point in self.points: if point[1] < lower_y: lower_y = point[1] lower = point return list(lower)
[docs] def low_angle(self, b, c): """Find the point that makes the smallest angle from vector c->b. Used by the Gift Wrapping algorithm to select the next hull vertex. Args: b: Current point on the hull boundary. c: Previous point (defines the reference direction). Returns: list: The [x, y] coordinates of the point with the lowest angle. """ low_angle = float("inf") for a in self.points: if a[0] != b[0] or a[1] != b[1]: if a[0] != c[0] or a[1] != c[1]: u = [c[0] - b[0], c[1] - b[1]] v = [a[0] - b[0], a[1] - b[1]] mag_u = math.sqrt((u[0]) ** 2 + (u[1]) ** 2) mag_v = math.sqrt((v[0]) ** 2 + (v[1]) ** 2) u = [u[0] / mag_u, u[1] / mag_u] v = [v[0] / mag_v, v[1] / mag_v] angle = math.acos(u[0] * v[0] + u[1] * v[1]) orient = ( b[0] * c[1] + b[1] * a[0] + c[0] * a[1] - a[0] * c[1] - a[1] * b[0] - c[0] * b[1] ) if orient < 0: angle = 2 * math.pi - angle if angle < low_angle: low_angle = angle low_point = a return list(low_point)
[docs] def convex_hull(self): """Compute the convex hull using the Gift Wrapping (Jarvis march) algorithm. Returns: list[list]: Ordered list of [x, y] vertices forming the convex hull. """ a = self.find_low_right() b = self.low_angle(a, [1, a[1]]) init = a ch = [a] while b[0] != init[0] or b[1] != init[1]: ch.append(b) a = b b = self.low_angle(a, ch[-2]) return ch
[docs] def plot(self, title="Convex Hull for a set of points"): """Deprecated: use cgeom.visualization.plot_convex_hull() instead.""" import warnings warnings.warn( "ConvexHull.plot() is deprecated. " "Use cgeom.visualization.plot_convex_hull(hull_obj, title) instead.", DeprecationWarning, stacklevel=2, ) from cgeom.visualization import plot_convex_hull plot_convex_hull(self, title)
[docs] def get_indexes(self): """Get the indices of the convex hull vertices in the original points array. Returns: list[int]: Indices into ``self.points`` for each hull vertex. """ indexes = [] for element in self.convex_hull(): j = 0 while element[0] != self.points[j][0] or element[1] != self.points[j][1]: j += 1 indexes.append(j) return indexes