Skip to content

Add PointCloudHashGrid: uniform-grid counterpart to PointCloudBVH#108

Draft
rmrsk wants to merge 3 commits into
particle_soa_pr3from
pointcloud-hashgrid
Draft

Add PointCloudHashGrid: uniform-grid counterpart to PointCloudBVH#108
rmrsk wants to merge 3 commits into
particle_soa_pr3from
pointcloud-hashgrid

Conversation

@rmrsk

@rmrsk rmrsk commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #107

Background

PointCloudBVH (on the particle_soa_pr3 branch, PR #106) gives turnkey nearest-neighbor /
closest-point queries over a particle cloud, backed by a BVH. While finishing that work we prototyped
a uniform spatial grid as an alternative that circumvents the tree entirely, and found it clearly
faster on near-uniform clouds (~7× build, ~1.4× query). Issue #107 proposed promoting that prototype
into a first-class class mirroring PointCloudBVH. This PR does so.

Stacked on top of #106 (base branch particle_soa_pr3), since it mirrors PointCloudBVH and the new
example/docs reference it.

Solution

Adds PointCloudHashGrid<T, Meta>, which answers the same queries as PointCloudBVH and exposes
the same public interface — same Hit, same closestPoint / closestPoints / nearestNeighbor
/ nearestNeighbors / allNearestNeighbors, same O(N) brute-force reference queries, same
position() / metadata() / numParticles() accessors — so the two are drop-in interchangeable.

  • Build: points are counting-sorted into a dense CSR bucket array keyed by integer cell
    coordinates (O(N), no tree). Cell size is derived from a target average occupancy (~1
    particle/cell by default).
  • Query: an expanding-shell search outward from the query point's cell with an exact stopping
    rule — after searching Chebyshev radius R, the world region within the covered cell faces is fully
    searched, so once the k-th best distance is inside that bound no neighbor can be missed. Handles
    external queries and queries outside the grid box (clamped cell); k > 1 carries a sorted k-best
    set through the shells.

On a near-uniform 500k cloud the grid builds ~5× faster and queries ~2× faster than PointCloudBVH
(Examples/NearestNeighborHashGrid: build ~16 ms, query ~0.29 µs/pt, vs the BVH's ~76 ms / ~0.55).

Also included: EBGeometry.hpp include, Tests/InstantiateAll.cpp coverage, TestPointCloudHashGrid
(checked against an independent brute-force oracle in both precisions, including the outside-the-box
case), the Examples/NearestNeighborHashGrid example, and documentation (a PointCloudHashGrid
section in ImplemBVH.rst and full doxygen).

The two point-cloud examples were also renamed for clarity now that there are two backing structures:
ClosestPointClosestPointBVH, NearestNeighborNearestNeighborBVH (that rename is part of
the base particle_soa_pr3 branch).

Side-effects

  • The class is not a BVH: unlike PointCloudBVH it cannot be composed as a primitive inside an
    outer BVH/CSG union. It serves only point queries.
  • The grid is bounded-domain: dense cells sized to the cloud bounding box, O(bboxVolume / h³)
    memory — fine for a compact cloud, wasteful for one sparse but spread over a large box.
  • The performance win is density-dependent: a single global cell size only suits near-uniform
    clouds. For clustered / multi-scale clouds PointCloudBVH's density adaptivity is the better choice
    — documented in the class comments, ImplemBVH.rst, and the example README so callers can pick.

Alternative solutions

  • Shared abstract base / concept for PointCloudBVH and PointCloudHashGrid so callers can
    select a structure polymorphically. Deferred — the two share an interface by convention here; a
    formal shared interface is a separate refactor (noted in Add a PointCloudHashGrid class: uniform-grid counterpart to PointCloudBVH for nearest-neighbor queries #107).
  • True spatial hashing (unordered_map<cellId, bucket>) instead of a dense array, to bound memory
    to O(N) on unbounded/sparse domains. Deferred; the dense grid is the fast bounded-domain case and
    the natural first cut. Same query logic would apply.
  • k > 1 fast path and a k=1 specialization mirroring the BVH's — not needed yet; the general
    k-best path is already fast for k=1.

@rmrsk rmrsk changed the title Add PointCloudHashGrid: uniform-grid counterpart to PointCloudBVH (#107) Add PointCloudHashGrid: uniform-grid counterpart to PointCloudBVH Jul 11, 2026
rmrsk added a commit that referenced this pull request Jul 11, 2026
Dev/ held the four superseded point-cloud examples (replaced by
ClosestPointBVH/NearestNeighborBVH on this branch) and the GridNN prototype
(promoted to the PointCloudHashGrid class, PR #108). It was always marked
DELETE_BEFORE_MERGE; removing it now, along with the temporary Dev/** annotation
block in REUSE.toml. reuse lint stays compliant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS5LFQihKoMrE6nbPaQW9R
rmrsk and others added 2 commits July 11, 2026 09:42
…issue #107)

PointCloudHashGrid answers the same nearest-neighbor / closest-point queries over
a particle cloud as PointCloudBVH and exposes the same public interface (same Hit,
same closestPoint/closestPoints/nearestNeighbor/nearestNeighbors/allNearestNeighbors,
same O(N) brute-force references and position()/metadata() accessors), so the two
are drop-in interchangeable -- but it circumvents the tree entirely.

Points are counting-sorted into a dense CSR bucket array keyed by integer cell
coordinates (an O(N) build, no tree). Queries are an expanding-shell search from the
query point's cell with an exact stopping rule -- after searching Chebyshev radius R
the world region within the covered faces is fully searched, so once the k-th best
distance is inside that bound no neighbor can be missed. Handles external queries and
queries outside the grid box (clamped cell). k>1 carries a sorted k-best set through
the shells.

For a near-uniform 500k cloud this builds ~5x faster and queries ~2x faster than
PointCloudBVH (Examples/NearestNeighborHashGrid: build ~16 ms, query ~0.29 us/pt vs
the BVH's ~76 ms / ~0.55). The class docs and ImplemBVH.rst spell out the trade-off:
a global cell size only suits near-uniform density, the dense grid is bounded-domain
(O(bboxVol/h^3) cells), and it is not a BVH (no composition into an outer BVH/CSG) --
prefer PointCloudBVH for clustered clouds, external-heavy workloads, and composition.

Adds the class (header + implem), EBGeometry.hpp include, InstantiateAll coverage,
TestPointCloudHashGrid (independent brute-force oracle, both precisions, incl. the
outside-the-box case), the NearestNeighborHashGrid example, and docs. Follow-up to
PointCloudBVH; the Dev/GridNN prototype this grew out of is superseded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS5LFQihKoMrE6nbPaQW9R
Match the terse, intrinsic style of the sibling example READMEs: drop the
speedup-extrapolation and the detailed grid-vs-tree performance breakdown, keeping
the functional description, the brief intrinsic guidance on when the grid vs the
BVH fits, and the brute-force correctness check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS5LFQihKoMrE6nbPaQW9R
@rmrsk rmrsk force-pushed the pointcloud-hashgrid branch from a1f0b7b to 2309355 Compare July 11, 2026 07:43
Both TestPointCloudBVH.cpp and TestPointCloudHashGrid.cpp declared the comparison
margin as `const T tol = tightMargin<T>()`, but tightMargin<T>() returns double and
withinAbsT()'s margin parameter is double -- so with T=float every withinAbsT(target,
tol) promoted tol float->double at the call site, which clang's -Wdouble-promotion
flags under -Werror (the Sanitizers CI job, EBGEOMETRY_TEST_BOTH_PRECISIONS). Declare
tol as double in both. Verified: both build clean under clang++-18 with both precisions
and -Wdouble-promotion -Werror, and still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS5LFQihKoMrE6nbPaQW9R
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant