BlackBeard.Spatial
1.0.0
dotnet add package BlackBeard.Spatial --version 1.0.0
NuGet\Install-Package BlackBeard.Spatial -Version 1.0.0
<PackageReference Include="BlackBeard.Spatial" Version="1.0.0" />
<PackageVersion Include="BlackBeard.Spatial" Version="1.0.0" />
<PackageReference Include="BlackBeard.Spatial" />
paket add BlackBeard.Spatial --version 1.0.0
#r "nuget: BlackBeard.Spatial, 1.0.0"
#:package BlackBeard.Spatial@1.0.0
#addin nuget:?package=BlackBeard.Spatial&version=1.0.0
#tool nuget:?package=BlackBeard.Spatial&version=1.0.0
BlackBeard.Spatial
Quaternion, vector and geodetic primitives for .NET, with compile-time frame
tagging. Hamilton convention, shortest-arc SLERP, and extension methods over
System.Numerics.Quaternion so the rotation maths can be used on its own,
outside any of the packages built on it.
Targets netstandard2.1 and net10.0. No dependencies.
Why this exists
Attitude composition is the part of a gimbaled-sensor or 6DOF-pose problem
that is cheapest to get right in isolation and most expensive to get wrong in
context — a bug here does not crash, it quietly draws a plausible, wrong
answer. BlackBeard.Spatial exists so that this maths is written once,
tested against an independent reference implementation, and reused by every
project in the family that needs to compose rotations between named frames,
rather than re-derived per project with per-project bugs.
Convention
- Hamilton, scalar-first: a quaternion is
(w, x, y, z). a * bappliesbfirst, thena.- Rotating a vector is
q ⊗ v ⊗ q*. - Unit quaternions are expected for rotation operations. The type does not
silently normalise — call
Normalized()or checkIsUnit().
System.Numerics.Quaternion is XNA-derived: it stores (x, y, z, w) and has
its own multiplication order. That mismatch has caused real field bugs, which
is why Quat is a distinct type with explicit, tested conversions rather than
a type alias. See QuaternionExtensions if you want to keep using
System.Numerics.Quaternion and just want a correct Slerp.
Frame-tagged rotations
The most expensive class of bug in this domain is composing rotations in the wrong order, or between the wrong frames. It compiles, it runs, and it produces a smooth, plausible, wrong answer.
Rotation<TFrom, TTo> wraps a Quat and carries two phantom frame markers.
Composition is only defined where the frames actually meet:
Rotation<Ned, Body> nedToBody = ...;
Rotation<Body, Gimbal> bodyToGimbal = ...;
Rotation<Gimbal, Camera> gimbalToCamera = ...;
// Reads in the same order as the physical pointing chain. Anything that
// skips a link or inverts one fails to compile, not to render correctly.
Rotation<Ned, Camera> nedToCamera =
nedToBody.Compose(bodyToGimbal).Compose(gimbalToCamera);
This is offered as a layer, not a tax: Rotation<,>.Value is the escape
hatch back to a raw Quat, and a caller who only wants quaternion arithmetic
never has to touch the frame types.
Testing
The core rotation and projection maths is tested against an independent
Python reference implementation across a dense sweep of azimuth, elevation
and sensor roll — see tests/BlackBeard.Spatial.Tests/data/ and
tests/BlackBeard.Spatial.Tests/GeometryOracleTests.cs. Two implementations
written independently, in different languages, agreeing to 1e-9 across a
sweep is a much stronger claim than either one passing its own author's
tests.
A frame-mismatched composition is asserted to fail at compile time — see
tests/compile-fail/.
License
Apache License 2.0. See LICENSE.
| 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 was computed. 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 is compatible. 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 | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | 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.1
- No dependencies.
-
net10.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.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 37 | 9/22/2026 |