"""Result containers for mathematicskit.geometry.
Every ``scipy.spatial``-backed algorithm in this domain (convex hull,
Delaunay triangulation, Voronoi diagram) returns one of the small
dataclasses below, extracting the fields mathematicskit's examples/tests
actually use from scipy's own (already rich) result objects, for a
consistent interface alongside this domain's hand-rolled algorithms
(segment intersection, point-in-polygon, the Frenet-Serret frame).
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional
import numpy as np
__all__ = [
"ConvexHullResult",
"TriangulationResult",
"VoronoiResult",
"CurveFrameResult",
"SurfaceCurvatureResult",
"PolyhedronResult",
"CircleResult",
"LatticePolygonResult",
"ClosestPairResult",
]
[docs]
@dataclass
class ConvexHullResult:
"""Container for a convex hull."""
points: np.ndarray
"""ndarray, shape (n, d): The input points."""
vertices: np.ndarray
"""ndarray, int: Indices into `points` of the hull's vertices (in
counterclockwise order for 2D, via ``scipy.spatial.ConvexHull``)."""
simplices: np.ndarray
"""ndarray, int, shape (n_facets, d): Indices forming each facet
(edges in 2D, triangles in 3D)."""
volume: float
"""float: The hull's enclosed volume (*area*, in 2D -- scipy's own
naming convention)."""
area: float
"""float: The hull's surface area (*perimeter*, in 2D)."""
method: str = ""
"""str: ``"qhull"`` (via scipy) or ``"graham_scan"`` (hand-rolled, 2D only)."""
[docs]
@dataclass
class TriangulationResult:
"""Container for a Delaunay triangulation."""
points: np.ndarray
simplices: np.ndarray
"""ndarray, int, shape (n_triangles, d + 1): Indices of each simplex's vertices."""
[docs]
@dataclass
class VoronoiResult:
"""Container for a Voronoi diagram (the Delaunay triangulation's dual)."""
points: np.ndarray
vertices: np.ndarray
"""ndarray, shape (n_vertices, d): Voronoi vertices (circumcenters of
the dual Delaunay simplices)."""
regions: list
"""list of list of int: Each input point's Voronoi region, as
indices into `vertices` (``-1`` marks an unbounded region)."""
ridge_points: np.ndarray
"""ndarray, int, shape (n_ridges, 2): The two input points each
Voronoi ridge separates."""
[docs]
@dataclass
class CurveFrameResult:
r"""Container for a parametric curve's Frenet-Serret frame."""
t: np.ndarray
position: np.ndarray
tangent: np.ndarray
"""ndarray: Unit tangent vector :math:`T` at each ``t``."""
normal: np.ndarray
"""ndarray: Unit (principal) normal vector :math:`N` at each ``t``."""
binormal: Optional[np.ndarray] = None
"""ndarray, optional: Unit binormal :math:`B = T \\times N` (3D curves only)."""
curvature: np.ndarray = field(default_factory=lambda: np.array([]))
torsion: Optional[np.ndarray] = None
"""ndarray, optional: Torsion (3D curves only)."""
arc_length: np.ndarray = field(default_factory=lambda: np.array([]))
"""ndarray: Cumulative arc length from ``t[0]`` to each ``t[k]``."""
[docs]
@dataclass
class SurfaceCurvatureResult:
r"""Container for the curvatures of a parametric surface :math:`\mathbf{r}(u, v)` on a grid."""
u: np.ndarray
v: np.ndarray
points: np.ndarray
r"""ndarray, shape (3, nu, nv): Surface points :math:`\mathbf{r}(u, v)`."""
gaussian: np.ndarray
"""ndarray, shape (nu, nv): Gaussian curvature :math:`K = (LN - M^2)/(EG - F^2)`."""
mean: np.ndarray
"""ndarray, shape (nu, nv): Mean curvature :math:`H`."""
area_element: np.ndarray
r"""ndarray, shape (nu, nv): :math:`\sqrt{EG - F^2}`, so that :math:`dA = \sqrt{EG - F^2}\,du\,dv`."""
[docs]
@dataclass
class PolyhedronResult:
"""Container for the vertex, edge, and face counts of a convex polyhedron."""
vertices: int
edges: int
faces: int
@property
def euler_characteristic(self) -> int:
"""int: :math:`V - E + F`, equal to 2 for every convex polyhedron."""
return self.vertices - self.edges + self.faces
[docs]
@dataclass
class CircleResult:
"""Container for a circle in the plane."""
center: np.ndarray
radius: float
[docs]
@dataclass
class LatticePolygonResult:
"""Container for the lattice-point counts of a polygon with integer vertices."""
interior: int
"""int: Lattice points strictly inside the polygon."""
boundary: int
"""int: Lattice points on the polygon's boundary."""
area: float
"""float: The polygon's area, from the shoelace formula."""
@property
def pick_area(self) -> float:
"""float: Pick's formula :math:`I + B/2 - 1`."""
return self.interior + self.boundary / 2 - 1
[docs]
@dataclass
class ClosestPairResult:
"""Container for the closest pair of points in a point set."""
indices: tuple
"""tuple of int: Indices of the two closest points."""
distance: float