- C# 100%
Phase 7 — Smooth Traversal - M35: Camera2.TranslateTo, ClampZoom, Lerp(Ratio), PixelScale.PerUnit, PixelScale.Clamp - M36: Angular dynamics SI-only <remarks> docs (DocSnippets-tested) - M37: EvaluateAtLength/TangentAtLength on BezierCurve2 and CatmullRomSpline2; LengthPath2.FromArc Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| .github | ||
| api | ||
| assets | ||
| benchmarks | ||
| docs | ||
| samples | ||
| src | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| docfx.json | ||
| LICENSE | ||
| README.md | ||
| Thunder.UnitsNET.Vectors.sln | ||
| toc.yml | ||
Thunder.UnitsNET.Vectors
A unit-aware 2D/3D vector library for physics-driven game loops. Built on UnitsNet, every vector component carries its physical unit — so LengthVector2 / Duration returns a SpeedVector2, not a dimensionless float.
📖 API docs
Packages
Quick start
// dotnet add package Thunder.UnitsNET.Vectors
// dotnet add package Thunder.UnitsNET.Vectors.Geometry (physics/collision)
using Thunder.UnitsNET.Vectors;
using Thunder.UnitsNET.Vectors.Geometry;
using UnitsNet;
using UnitsNet.Units;
// Create a position vector
var position = new LengthVector2(Length.FromMeters(3), Length.FromMeters(4));
// Kinematics: v = Δx / Δt
var velocity = position / Duration.FromSeconds(1); // SpeedVector2
// Dynamics: F = ma
var accel = new AccelerationVector2(
Acceleration.FromMetersPerSecondSquared(2),
Acceleration.FromMetersPerSecondSquared(0));
ForceVector2 force = accel * Mass.FromKilograms(10);
// Dot product: work W = F · d (returns Energy)
Energy work = force.Dot(position);
// Physics integration (Euler step)
var newPos = PhysicsIntegration.Euler(position, velocity, Duration.FromSeconds(1.0 / 60));
// Collision manifold — all 15 shape pairings, zero allocation
var a = Rectangle2.FromCenterAndSize(LengthPoint2.Origin, Length.FromMeters(2), Length.FromMeters(2));
var b = Rectangle2.FromCenterAndSize(LengthPoint2.FromMeters(1, 0), Length.FromMeters(2), Length.FromMeters(2));
if (a.TryGetManifold(b, out var manifold))
{
Length depth = manifold.PenetrationDepth;
Direction2 normal = manifold.ContactNormal;
}
// Raycasting
var ray = new LengthRay2(LengthPoint2.FromMeters(-10, 0), Direction2.East);
if (ray.TryIntersect(a, out var hit))
Console.WriteLine($"Hit at {hit.Distance} from origin");
// Path following
var path = new LengthPath2(new[] {
LengthPoint2.FromMeters(0, 0),
LengthPoint2.FromMeters(5, 0),
LengthPoint2.FromMeters(5, 5),
});
LengthPoint2 pos = path.Evaluate(Length.FromMeters(3));
// Convert to XNA Vector2 for rendering (pixels per metre = 64)
const double ppm = 64;
var posXna = position.ToXnaVector2(LengthUnit.Meter, ppm);
Architecture
Type model
All concrete vector types are readonly partial record struct values. Component types come from UnitsNet (Length, Speed, Force, …); DoubleVector2/DoubleVector3 hold bare Double components.
Each type is composed from three layers:
| Layer | Example file | What it provides |
|---|---|---|
| Stub | LengthVector2.cs |
Declares the type and its primary constructor |
| Generated | LengthVector2.g.cs (build output) |
Arithmetic, Zero, Magnitude, As, Normalize |
| Hand-written partials | LengthVector2.Kinematics.cs |
Cross-type physics operators (/ Duration → SpeedVector2) |
Source generator
Thunder.UnitsNET.Vectors.Generators is a Roslyn incremental source generator. It reads every [GenerateVector2] / [GenerateVector3] attribute and emits the arithmetic partial. Consumers never reference the generator directly — it is wired as an Analyzer reference and compiled into the core DLL.
Package structure
Thunder.UnitsNET.Vectors.sln
├── src/
│ ├── Thunder.UnitsNET.Vectors/ ← published (core vectors + physics integration)
│ │ ├── Vector2/ ← 2D quantity types + partials
│ │ └── Vector3/ ← 3D quantity types + partials
│ ├── Thunder.UnitsNET.Vectors.MonoGame/ ← published
│ │ └── Extensions/ ← ToXnaVector2 / ToXnaVector3
│ ├── Thunder.UnitsNET.Vectors.Geometry/ ← published (shapes, collision, raycasting, path)
│ ├── Thunder.UnitsNET.Vectors.Geometry.MonoGame/ ← published (DebugDraw, geometry → XNA)
│ ├── Thunder.UnitsNET.Vectors.Generators/ ← not published (Roslyn tooling)
│ └── Thunder.UnitsNET.Vectors.GoDot/ ← not published (incomplete scaffold)
├── tests/
│ ├── Thunder.UnitsNET.Vectors.Tests/
│ ├── Thunder.UnitsNET.Vectors.MonoGame.Tests/
│ └── Thunder.UnitsNET.Vectors.Geometry.Tests/
└── benchmarks/
└── Thunder.UnitsNET.Vectors.Benchmarks/ ← BDN benchmarks; results in docs/benchmarks/
Supported vector types
2D — DoubleVector2, LengthVector2, SpeedVector2, AccelerationVector2, ForceVector2, JerkVector2, ImpulseVector2, MagneticFieldVector2, MagnetizationVector2, LinearDensityVector2, ElectricFieldVector2
3D — DoubleVector3, LengthVector3, SpeedVector3, AccelerationVector3, ForceVector3, JerkVector3, ImpulseVector3, MagneticFieldVector3, MagnetizationVector3, MassFluxVector3, ElectricFieldVector3, ElectricCurrentDensityVector3, TemperatureGradientVector3
Building and testing
Requires .NET 10 SDK.
dotnet build
dotnet test
Milestone status
See docs/STATUS.md for the current implementation status.
The full roadmap and design decisions are in docs/v0/phase4-physics/roadmap.md. Future ideas are tracked in docs/ideas.md.
Contributing
- Read
docs/CLAUDE.mdfor the project conventions (branch naming, step progression, how decisions are surfaced). - Each feature follows TDD: RED commit (failing build or test) → GREEN commit (passing) → merge to
develop. - Open a PR against
develop;mainis reserved for release-tagged commits.
License
MIT — see LICENSE.