Skip to main content

Fast sweep-hull (S-hull) Delaunay triangulation in 2D and 3D, implemented in Rust

Project description

shull

S-hull: a fast sweep-hull routine for Delaunay triangulation by David Sinclair (see http://www.s-hull.org/) implemented in Rust with Python bindings.

The 2D case implements S-hull proper: a seed triangle with the smallest circumcircle, a radial sweep over points sorted by distance from its circumcenter, hull attachment via a linked ring with a pseudo-angle hash, and in-circle edge flipping to restore the Delaunay condition.

The 3D case generalizes Sinclair's Newton Apple Wrapper sweep-hull algorithm (arXiv 1602.04707) one dimension up: points are lifted onto a 4D paraboloid (w = x² + y² + z²), the 4D convex hull is computed by incremental insertion along a Morton (Z-order) space-filling curve — every lifted point is extreme on the paraboloid, so any insertion order is valid, and the spatially local one keeps the hull walk cache-hot — and the downward-facing facets are exactly the Delaunay tetrahedra.

In both cases all combinatorial decisions (visibility, in-circle/in-sphere tests) fall back to Shewchuk's exact adaptive predicates (the robust crate) — so the triangulation stays valid even on adversarial inputs (cospherical grids, tight clusters far apart) where plain floating point corrupts the result.

Install

We provide prebuilt wheels for Linux, macOS, and Windows on PyPI:

pip install shull

Usage

2D

>>> import shull
>>> import numpy as np
>>> pts = np.random.default_rng(12345).random((10000, 2))
>>> d = shull.Delaunay(pts)
>>> d.triangles          # alias: d.simplices
array([[5790, 4665, 8764],
       [4665, 9599, 8764],
       [7711,   64, 4665],
       ...,
       [2821, 8418, 1189],
       [1500, 9364, 8681],
       [5462, 1500, 8681]], dtype=int32)

3D

>>> pts = np.random.default_rng(12345).random((10000, 3))
>>> d = shull.Delaunay(pts)  # dispatches on the number of columns;
>>> d.simplices              # Delaunay3d is kept as an explicit alias
array([[3661, 6693, 7492, 1937],
       ...], dtype=int32)

scipy compatibility

shull.Delaunay aims to be a drop-in replacement for scipy.spatial.Delaunay. Beyond points and simplices (int32, like scipy) it provides the derived structures. neighbors comes straight out of the triangulation (the hull construction maintains facet adjacency anyway, so exporting it is essentially free, like qhull) and vertex_neighbor_vertices is built in Rust on first access (~2x faster than scipy's); the rest are computed lazily in numpy and cached:

  • neighbors — neighboring simplex opposite each vertex, -1 at the boundary
  • convex_hull — facets of the convex hull
  • vertex_to_simplex — a simplex containing each vertex
  • vertex_neighbor_vertices — CSR (indptr, indices) vertex adjacency
  • coplanar — points not in the triangulation (dropped exact duplicates), mapped to their kept representative; recorded by the Rust core during its dedup, not reconstructed after the fact
  • transform — barycentric transforms, same layout as scipy
  • find_simplex(xi, bruteforce=False, tol=None) — point location via a vectorized visibility walk (brute force as option/fallback)
  • npoints, nsimplex, ndim, min_bound, max_bound, furthest_site, close()

Not implemented: equations (and paraboloid_scale/paraboloid_shift, plane_distance, lift_points), incremental mode (add_points), furthest_site=True and qhull_options — the constructor accepts scipy's keyword arguments but raises NotImplementedError for non-default values. Unlike scipy, float32 points are kept as float32 (see below).

Notes (both dimensions):

  • Output simplices are positively oriented (counterclockwise triangles in 2D, positive-volume tetrahedra in 3D) and index into points.
  • float32 input is supported natively: no upcast copy is made (d.points keeps the float32 dtype). Coordinates widen to float64 exactly internally, so the result is identical to passing points.astype(np.float64). Other dtypes are converted to float64.
  • Exact duplicate points are dropped, keeping the first occurrence as the representative. scipy/Qhull likewise never includes duplicate indices in simplices (though which copy Qhull keeps is arbitrary, while shull's choice is deterministic). Dropped points are reported in coplanar (scipy's convention); the raw calculate_shull_* functions return the (dropped, kept) index pairs as a third array.
  • Degenerate input (too few distinct points, all points collinear in 2D, all points coplanar/cospherical in 3D) raises ValueError — a full-dimensional triangulation does not exist in those cases.
  • Points are triangulated after centering on their centroid (a ≤1-ulp perturbation of the coordinates), which makes the result robust to clouds positioned far from the origin.

Use from Rust

The crate is also usable as a plain Rust library: the Python bindings sit behind an off-by-default python cargo feature, so depending on shull pulls in only ndarray and robust — no pyo3, no Python at build time, no libpython in your binary.

[dependencies]
shull = { git = "https://github.com/schlegelp/shull" }
ndarray = "0.17"
use ndarray::Array2;
use shull::{delaunay2d, delaunay4d, csr_adjacency};

let pts: Array2<f64> = /* (n, 2) array */;
// triangles (ccw), neighbor triangle opposite each vertex (-1 on the hull),
// and (dropped, kept) index pairs for exact duplicate points
let (triangles, neighbors, duplicates) = delaunay2d(pts.view())?;
// 3D points (n, 3) -> tetrahedra, same return layout:
let (tetrahedra, neighbors, duplicates) = delaunay4d(pts3.view())?;

csr_adjacency builds the scipy-style (indptr, indices) vertex adjacency from a simplex array. Degenerate or oversized input is reported as a DelaunayError rather than a panic.

TODOs

  • implementation for the 2d case
  • generalize from 2 to N dimensions: 3D Delaunay (tetrahedra) via 4D sweep-hull
  • improve 2d performance: ~10X faster than scipy's Qhull-based Delaunay
  • improve 3d performance: ~3–5.5X faster than scipy
  • scipy-compatible API: neighbors, convex_hull, vertex_neighbor_vertices, transform, find_simplex, …

Build

  1. cd into directory
  2. Activate virtual environment: source .venv/bin/activate
  3. Run maturin develop (use maturin build --release to build wheel)

Test / benchmark

cargo test                      # Rust unit tests (incl. hull invariant checks)
python -m pytest tests/        # property tests + comparison against scipy
python bench.py                # benchmark against scipy.spatial.Delaunay

Benchmark on an Apple-silicon laptop (random uniform points, release build):

2D (Delaunay vs scipy.spatial.Delaunay)

n shull scipy (Qhull) speedup
10 000 0.002 s 0.018 s 9.8×
100 000 0.029 s 0.30 s 10.7×
1 000 000 0.46 s 5.0 s 10.9×

3D (Delaunay3d vs scipy.spatial.Delaunay)

n shull scipy (Qhull) speedup
10 000 0.031 s 0.098 s 3.2×
100 000 0.32 s 1.69 s 5.3×
1 000 000 3.8 s 20.8 s 5.5×

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

shull-0.3.0.tar.gz (52.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

shull-0.3.0-cp310-abi3-win_amd64.whl (260.5 kB view details)

Uploaded CPython 3.10+Windows x86-64

shull-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl (613.4 kB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

shull-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl (576.6 kB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

shull-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (411.0 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

shull-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (400.9 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

shull-0.3.0-cp310-abi3-macosx_11_0_arm64.whl (359.7 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

shull-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl (372.0 kB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file shull-0.3.0.tar.gz.

File metadata

  • Download URL: shull-0.3.0.tar.gz
  • Upload date:
  • Size: 52.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0.tar.gz
Algorithm Hash digest
SHA256 211ee23048d7af27da77c0b7300aa496dfb92e6379aa20935a1ee16607b9bbcc
MD5 5624f7a8f7cdd0c11c175e33c3834553
BLAKE2b-256 cbef3df19503b15f5f11203babb0302cf9e4ea51d9506bee9e0a11f83dd04f5a

See more details on using hashes here.

File details

Details for the file shull-0.3.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: shull-0.3.0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 260.5 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 171463a2198489f1c99ae70bb54cc59875d990e19453e130bd19d62cfab07ba9
MD5 103bc95a36b70d96e13f796df459557c
BLAKE2b-256 ceb1bd6fc6fdf97f358172b7b4cfd3dc3620e2845a7721e236c059fb64709def

See more details on using hashes here.

File details

Details for the file shull-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: shull-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 613.4 kB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 f21f57a48e2561158eb0a769e9b8502f8b9a2da87b45f9a43089b6776123a319
MD5 588afc7eb42fdfc8208ae1b026e951c7
BLAKE2b-256 a8377e65b8d15e8c1b093a9362fbefc58c1fbdedc1cae6e70a6bb68c69adf140

See more details on using hashes here.

File details

Details for the file shull-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

  • Download URL: shull-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 576.6 kB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 fa88f6c1195dacf4e7f48b8495789954bdb019cf39485bb1db3f15b592310f8f
MD5 7c724451c3d47988e516156b64e10372
BLAKE2b-256 2f3053c896ba3072835801b24f615ee289f0054929889640515a0cf0d7a8aeff

See more details on using hashes here.

File details

Details for the file shull-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: shull-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 411.0 kB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b65041d34b40d3592ec1aa2bf3dc32bfd027b128dfc0e9ea05c9f2331e49d598
MD5 ae61ceda9bcc3e713db27153214d667d
BLAKE2b-256 53e3145378fb1b0743247cb7898ba564e52778c2704e293d1f7e8e748c77f979

See more details on using hashes here.

File details

Details for the file shull-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

  • Download URL: shull-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 400.9 kB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 faf7de0a85e3c722940a2dd029561fce833a4aeb2f31641e4f19dbfe0cb98fcc
MD5 90965bf716c74f382b2fc8921313c118
BLAKE2b-256 981c33e17ad34d48437fd074a3c3bfe52d42211421ebea3e717d761aaff042a2

See more details on using hashes here.

File details

Details for the file shull-0.3.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: shull-0.3.0-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 359.7 kB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d195edb742747066017250dd6127df3be80fa57da81105a881b8a0877187c499
MD5 badbbec9d7bc46987c5a83f21cc57a10
BLAKE2b-256 324fa8d94eb7bb1c502111b4f4481ddc14bdd6a2c0228641cb2a4c3a286cdbc0

See more details on using hashes here.

File details

Details for the file shull-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: shull-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 372.0 kB
  • Tags: CPython 3.10+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for shull-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 c243606aaa1f266fab385f984313e8cc33c0b043527a51a410517040968e6be1
MD5 c7f3250d34699fb4af9412ff915dd5c7
BLAKE2b-256 203d8e22ef88fecebc9710da6b7cf86309dbf805b23ca46a5387ec1155574a85

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page