SpatialMapping.Core
0.1.0
See the version list below for details.
dotnet add package SpatialMapping.Core --version 0.1.0
NuGet\Install-Package SpatialMapping.Core -Version 0.1.0
<PackageReference Include="SpatialMapping.Core" Version="0.1.0" />
<PackageVersion Include="SpatialMapping.Core" Version="0.1.0" />
<PackageReference Include="SpatialMapping.Core" />
paket add SpatialMapping.Core --version 0.1.0
#r "nuget: SpatialMapping.Core, 0.1.0"
#:package SpatialMapping.Core@0.1.0
#addin nuget:?package=SpatialMapping.Core&version=0.1.0
#tool nuget:?package=SpatialMapping.Core&version=0.1.0
SpatialMapping.Core
3D spatial indices for points and AABB-shaped items (line segments, boxes, mesh primitives). Six structures, a unified set of queries (nearest / k-NN / radius / segment / ray / box), and an empirical benchmark harness that measures every combination on your data so you can pick by reading numbers, not guessing.
Multi-targets netstandard2.0 + net8.0. Works in modern .NET, .NET Framework 4.7.2+, Mono, Unity.
Quick start
Points
using SpatialMapping.Core;
using System.Numerics;
var positions = new[] { new Vector3(0,0,0), new Vector3(1,0,0), new Vector3(0,5,0) };
var bvh = new BvhIndex3D<Vector3, Vector3Distance>();
bvh.Build(positions);
var nearest = bvh.FindNearestToPoint(new Vector3(0.4f, 0, 0)); // .Index = 0
var topK = new List<NearestResult<Vector3>>();
bvh.FindKNearestToPoint(new Vector3(0,0,0), k: 2, topK);
var withinR = new List<NearestResult<Vector3>>();
bvh.FindWithinRadiusOfPoint(Vector3.Zero, radius: 3f, withinR);
Segments (or any AABB-shaped item)
var segments = new[]
{
new Segment3D(new Vector3(0,0,0), new Vector3(1000, 0, 0)), // long
new Segment3D(new Vector3(0,5,0), new Vector3(0, 5, 1)), // short
};
var bvh = new BvhIndex3D<Segment3D, Segment3DDistance>();
bvh.Build(segments);
// Endpoints are 300 and 700 units from this query, but distance to the segment is 0.
var hit = bvh.FindNearestToPoint(new Vector3(300, 0, 0)); // .Index = 0, d² = 0
var clash = bvh.FindNearestToSegment(new Segment3D(new Vector3(0, -1, 0), new Vector3(0, 1, 0)));
var inBox = new List<NearestResult<Segment3D>>();
bvh.QueryBox(new Bounds3D(new Vector3(-1,-1,-1), new Vector3(2,2,2)), inBox);
var firstHit = bvh.FindFirstRayHit(new Vector3(-5, 0, 0), new Vector3(1, 0, 0));
Empirical "which structure?" — SpatialBenchmark
[Fact]
public void PickIndexForMyData()
{
var report = SpatialBenchmark.RunForSegments(myActualData);
_out.WriteLine(report.ToMarkdown());
Assert.Empty(report.CorrectnessIssues);
}
The harness times every applicable structure on every supported query. Naive O(N) fallbacks fill in queries a structure doesn't natively support (so every cell has a number — the timing tells you the cost of forcing the wrong tool). A per-query-type timeout lets you cap infeasible combinations.
Pick a structure
| Item type | Density / extent | Pick |
|---|---|---|
| Points, uniform density, known box | moving every frame | UniformGridIndex3D |
| Points, non-uniform / clumpy | static-ish | KdTreeIndex3D or OctreeIndex3D |
| Points, k-NN / radius queries | mixed | BvhIndex3D (single API for everything) |
| AABB items (segments, boxes, meshes) | mixed sizes | BvhIndex3D |
| AABB items, uniform extent | known box | AabbGridIndex3D |
| Anything, N < ~500 | n/a | BruteForceIndex3D |
If unsure, run SpatialBenchmark on your data and read the table.
Query × structure matrix
Native = the structure's own implementation. Naive = O(N) fallback (still correct, just slow). "—" = not supported by this structure (use a different one).
| Structure | Nearest pt | k-NN pt | Radius pt | Nearest seg | Box | Ray | Refit |
|---|---|---|---|---|---|---|---|
| BruteForce | native | naive | naive | naive | — | — | — |
| UniformGrid | native | naive | naive | naive | — | — | — |
| KdTree | native | naive | naive | naive | — | — | — |
| Octree | native | naive | naive | naive | — | — | — |
| BVH | native | native | native | native | native | native | native |
| AabbGrid | native | native | native | naive | native | — | — |
All six expose batch + parallel variants of FindNearest* for read-only-after-build query workloads.
Adapting custom item types
Implement IItemDistance3D<T> as a readonly struct for the JIT-monomorphized fast path:
public readonly struct SphereDistance : IItemDistance3D<Sphere>
{
public Bounds3D GetAabb(in Sphere s) => new(s.Center - new Vector3(s.Radius), s.Center + new Vector3(s.Radius));
public float DistanceSquaredToPoint(in Sphere s, Vector3 p)
{
float d = Vector3.Distance(s.Center, p) - s.Radius;
return d <= 0 ? 0 : d * d;
}
public float DistanceSquaredToSegment(in Sphere s, in Segment3D q)
{
float d = MathF.Sqrt(q.DistanceSquaredTo(s.Center)) - s.Radius;
return d <= 0 ? 0 : d * d;
}
}
var bvh = new BvhIndex3D<Sphere, SphereDistance>();
bvh.Build(spheres);
Or use the delegate-flavored convenience overload (BvhIndex3D<Sphere>(getAabb, distToPoint, distToSegment)) for ad-hoc work — one extra delegate dispatch per leaf hit.
Out of scope (today)
- No serialization. In-process use only; rebuild from source data on load.
- No frustum query. Same traversal as ray; trivial to add when needed.
- No parallel build. Rebuild is single-threaded; query is parallel.
- Float-only (
System.Numerics.Vector3). For city-scale-mm precision, work in a local coordinate frame.
Versioning
0.x — early. API may change between minor versions.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Memory (>= 4.5.5)
- System.Numerics.Vectors (>= 4.5.0)
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.