GeomPP 0.8.0
See the version list below for details.
dotnet add package GeomPP --version 0.8.0
NuGet\Install-Package GeomPP -Version 0.8.0
<PackageReference Include="GeomPP" Version="0.8.0" />
<PackageVersion Include="GeomPP" Version="0.8.0" />
<PackageReference Include="GeomPP" />
paket add GeomPP --version 0.8.0
#r "nuget: GeomPP, 0.8.0"
#:package GeomPP@0.8.0
#addin nuget:?package=GeomPP&version=0.8.0
#tool nuget:?package=GeomPP&version=0.8.0
GeomPP — C# Bindings
C++/CLI bindings for geompp, targeting Windows x64.
Available on NuGet as GeomPP.
Supports .NET 8 (and later) and .NET Framework 4.8.
Changelog — full release notes for every version.
Install
dotnet add package GeomPP
or in your .csproj:
<PackageReference Include="GeomPP" Version="0.8.0" />
Quick start
Example 1 — LineSegment3D intersection
The code:
using G = GeomPP;
G.Precision.DecimalPrecision = G.Precision.DP_THREE;
var s1 = G.LineSegment3D.Make(new G.Point3D(1, 0, 0), new G.Point3D(-1, 0, 2));
var s2 = G.LineSegment3D.Make(new G.Point3D(0, 1, 0), new G.Point3D(0, -1, 2));
Console.WriteLine($"s1 = {s1}");
Console.WriteLine($"s2 = {s2}");
var result = s1.Intersection(s2);
if (result is G.Point3D p) {
Console.WriteLine("OK: " + p.ToWkt()); // expects POINT (0 0 1)
} else {
Console.WriteLine("FAIL: no intersection");
}
Output:
s1 = LINESTRING (1 0 0, -1 0 2)
s2 = LINESTRING (0 1 0, 0 -1 2)
intersection found: POINT (0 0 1)
intersection written to intersection.wkt
Example 2 — Load geometries from an .lsv file
An .lsv file is a plain-text list of WKT geometries, one per line:
POINT (1 2 3)
POINT (4 5 6)
LINESTRING (0 0 0, 1 1 1)
LINESTRING (2 0 0, 2 3 4)
LINE (0 0 0, 1 0 0)
RAY (0 0 0, 0 1 0)
using G = GeomPP;
G.Precision.DecimalPrecision = G.Precision.DP_THREE;
var parser = G.WktParser.Open("sample_geometries.lsv");
if (!parser.HasNext()) {
Console.WriteLine("no geometries found");
return;
}
while (parser.HasNext()) {
var item = parser.Next();
if (item == null) {
Console.WriteLine("skipped unrecognised line");
continue;
}
Console.WriteLine(G.WktParser.ToWkt(item));
}
Output:
POINT (1 2 3)
POINT (4 5 6)
LINESTRING (0 0 0, 1 1 1)
LINESTRING (2 0 0, 2 3 4)
LINE (0 0 0, 1 0 0)
RAY (0 0 0, 0 1 0)
Example 3 — are_coplanar, winding order, and polygon with holes
using GeomPP;
using Geompp.Extensions;
Precision.DecimalPrecision = Precision.DP_THREE;
var flat = new List<Point3D> {
new Point3D(0,0,0), new Point3D(1,0,0),
new Point3D(0,1,0), new Point3D(1,1,0)
};
var skew = new List<Point3D> {
new Point3D(0,0,0), new Point3D(1,0,0),
new Point3D(0,1,0), new Point3D(0,0,1)
};
Console.WriteLine(flat.AreCoplanar()); // True — all on the XY plane
Console.WriteLine(skew.AreCoplanar()); // False — spans 3D space
// Closest world-axis plane and winding check
var plane = flat.ClosestWorldPlaneTo();
Console.WriteLine(plane.Normal()); // (0, 0, 1) → XY plane
Console.WriteLine(flat.AreCCW()); // True — CCW on the XY plane
Console.WriteLine(flat.AreCW()); // False
// Polygon3D requires CCW outer ring and CW holes
var outer = new List<Point3D> {
new Point3D(0,0,0), new Point3D(4,0,0),
new Point3D(4,4,0), new Point3D(0,4,0)
};
var hole = new List<Point3D> {
new Point3D(1,3,0), new Point3D(3,3,0),
new Point3D(3,1,0), new Point3D(1,1,0)
};
var poly = Polygon3D.Make(outer, new List<List<Point3D>> { hole });
Console.WriteLine(poly.Size()); // 4
Output:
True
False
VECTOR (0 0 1)
True
False
4
Precision
All floating-point comparisons go through a thread-local precision setting:
G.Precision.DecimalPrecision = G.Precision.DP_THREE; // 3 decimal places (default)
G.Precision.DecimalPrecision = G.Precision.DP_SIX; // 6 decimal places
G.Precision.DecimalPrecision = G.Precision.DP_NINE; // 9 decimal places
double eps = G.Precision.Epsilon; // current epsilon (10^-N)
Supported types
| Type | 2D | 3D |
|---|---|---|
Point |
✓ | ✓ |
Vector |
✓ | ✓ |
Line |
✓ | ✓ |
Ray |
✓ | ✓ |
LineSegment |
✓ | ✓ |
Polyline |
✓ | ✓ |
Triangle |
✓ | ✓ |
Polygon |
✓ | ✓ |
BBox |
✓ | ✓ |
Plane |
— | ✓ |
GeometryCollection |
✓ | ✓ |
WktParser |
✓ | ✓ |
All types expose ToWkt(), FromWkt(), ToFile(), FromFile(), AlmostEquals(), and the same
operators available in the C++ library.
Intersection methods return object (null when there is no intersection); use C# pattern matching
to extract the result type:
var result = line.Intersection(segment);
if (result is G.Point2D p) { /* point intersection */ }
if (result is G.LineSegment2D s) { /* overlap */ }
Plane exposes intersections, parallel / coplanar checks, and convenience constructors:
var pl = G.Plane.XY();
var ray = G.Ray3D.Make(new G.Point3D(5, 3, 4), new G.Vector3D(0, 0, -1));
var hit = pl.Intersection(ray); // Point3D(5, 3, 0) or null
var axisY = pl.Intersection(G.Plane.YZ()); // Line3D along the Y-axis or null
pl.IsParallel(ray); // false (ray crosses the plane)
pl.IsCoplanar(G.Line3D.Make(new G.Point3D(0,0,0), new G.Point3D(1,1,0))); // true
// Implicit Vector → Point construction
var pFromV = new G.Point3D(new G.Vector3D(1, 2, 3)); // = Point3D(1, 2, 3)
// Triangle3D intersection with Line / Ray / Segment / Plane / Triangle
var tri = G.Triangle3D.Make(new G.Point3D(0,0,0), new G.Point3D(4,0,0), new G.Point3D(0,4,0));
var hitL = tri.Intersection(G.Line3D.Make(new G.Point3D(1, 1, -1), new G.Point3D(1, 1, 1))) as G.Point3D;
var hitR = tri.Intersection(G.Ray3D.Make(new G.Point3D(1, 1, 4), new G.Vector3D(0, 0, -1))) as G.Point3D;
var hitS = tri.Intersection(G.LineSegment3D.Make(new G.Point3D(1, 1, -2), new G.Point3D(1, 1, 3))) as G.Point3D;
var y1 = G.Plane.FromOriginAndNormal(new G.Point3D(0, 1, 0), new G.Vector3D(0, 1, 0));
var hitP = tri.Intersection(y1) as G.LineSegment3D; // (0,1,0)→(3,1,0)
var other = G.Triangle3D.Make(new G.Point3D(1,1,-1), new G.Point3D(1,1,1), new G.Point3D(3,1,0));
var hitT = tri.Intersection(other) as G.LineSegment3D; // (1,1,0)→(3,1,0)
Platform note
GeomPP is built with C++/CLI and is Windows x64 only. It will not run on Linux, macOS, or 32-bit processes.
Build on Windows x64 manually
If you want to built from this source files, use these commands
Build the C# DLL
# from the main directory, geompp
# if you want to build for .Net 8
msbuild geompp_csharp\GeomPP.vcxproj /p:Platform=x64 /p:GeomppBuildRoot="$PWD\build_win" [/p:Configuration=Release]
# if you want to build for .Net Framework 4.8
msbuild geompp_csharp\GeomPP_Net48.vcxproj /p:Platform=x64 /p:GeomppBuildRoot="$PWD\build_win" [/p:Configuration=Release]
# run smoke tests, after build from the main directory geompp
dotnet test geompp_csharp\tests\GeomPPTests.csproj [-p:GeomPPConfiguration=Release]
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-windows7.0 is compatible. |
| .NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- No dependencies.
-
net10.0-windows7.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 |
|---|---|---|
| 0.17.3 | 98 | 8/15/2026 |
| 0.17.2 | 92 | 8/14/2026 |
| 0.16.2 | 87 | 8/6/2026 |
| 0.15.1 | 119 | 7/24/2026 |
| 0.14.0 | 104 | 7/21/2026 |
| 0.13.0 | 117 | 7/9/2026 |
| 0.12.0 | 103 | 7/8/2026 |
| 0.11.0 | 110 | 7/4/2026 |
| 0.10.1 | 124 | 6/23/2026 |
| 0.9.1 | 222 | 6/17/2026 |
| 0.8.2 | 117 | 5/18/2026 |
| 0.8.0 | 111 | 5/18/2026 |
| 0.7.0 | 104 | 5/6/2026 |
| 0.6.0 | 104 | 5/4/2026 |
| 0.5.0 | 103 | 5/1/2026 |
| 0.4.0 | 115 | 4/27/2026 |
| 0.1.2 | 124 | 4/13/2026 |
| 0.1.1 | 123 | 4/13/2026 |
| 0.1.0 | 125 | 4/9/2026 |
> C++ library — tagged `v0.8.0` · C# / NuGet — tagged `csharp-v0.8.0` · Python / PyPI — tagged `python-v0.8.0`
> Touches `Plane`, `Triangle3D`, `Point2D`, `Point3D`, `Line3D`, `Ray3D`, `LineSegment3D`. Adds the `Distance` / `DistanceTo` family across every pair of 3D linear primitives, factors the closest-points-of-two-lines math into a new `calc_utils3d` header, and introduces a Doxygen-driven API documentation pipeline. Closes 6 stub methods (19 → 13).
### Added
**C++ core**
- `Plane::Intersects(Ray3D)` / `Plane::Intersection(Ray3D)` — delegates to the line case, then keeps the hit only if it is ahead of the ray's origin.
- `Plane::Intersects(LineSegment3D)` / `Plane::Intersection(LineSegment3D)` — delegates to the line case, then keeps the hit only if it lies within the segment.
- `Plane::Intersects(Plane)` / `Plane::Intersection(Plane)` — closed-form line of intersection from the cross product of the two normals and a point in the span of those normals; returns a `Line3D` (direction = `N1 × N2`).
- `Plane::Intersects(Triangle3D)` / `Plane::Intersection(Triangle3D)` — delegates to the new `Triangle3D::Intersection(Plane)` (unwraps/rewraps because the two `ReturnSet` variants don't share alternatives).
- `Plane::IsParallel(Line3D / Ray3D / LineSegment3D)` — tests `direction · normal == 0`. Returns `true` for lines/rays/segments lying *in* the plane (coplanar ⊂ parallel by this definition).
- `Plane::IsCoplanar(Line3D / Ray3D / LineSegment3D)` — strict-subset of parallel: requires the first point to also lie on the plane.
- `Triangle3D::Intersection(Line3D)` — intersects the line with the triangle's plane, then runs a 3D barycentric inside-test (`U·U`, `V·V`, `U·V`, `W·U`, `W·V`) using `within_axis_boundary(sc, tc)`. No 2D projection.
- `Triangle3D::Intersection(Ray3D)` — line-intersection result, kept only if `ray.IsAhead(hit)`.
- `Triangle3D::Intersection(LineSegment3D)` — line-intersection result, kept only if `segment.Contains(hit)`.
- `Triangle3D::Intersection(Plane)` — plane-plane intersection line, then projects both the line and the triangle vertices into the triangle's 2D frame and delegates to `Triangle2D::Intersection(Line2D)`; lifts the resulting `Point2D`/`LineSegment2D` back to 3D via `Plane::Evaluate`.
- `Triangle3D::Intersection(Triangle3D)` — computes each triangle's intersection with the other's plane (both yield collinear `LineSegment3D`s on the planes' common line) and returns the segment overlap.
- `Triangle3D::Intersects(Plane)` — `bool` overload.
- `Point2D(Vector2D const&)` — implicit construction from a vector. Mirrors the new `Point3D(Vector3D const&)` constructor.
- `Point3D(Vector3D const&)` — implicit construction from a vector. Lets `Vector3D` arithmetic results flow directly into `Point3D`-typed APIs (e.g., `Line3D::Make(Vector3D-result, …)`).
- `calc_utils3d.hpp` / `.cpp` — new header exposing two free functions:
- `distance_line_to_line(L1_P0, L1_P1, L2_P0, L2_P1, sc, tc)` — solves the 2×2 perpendicular-distance system between two parameterized lines using Cramer's rule; `sc=0` / `tc=largest-denominator-projection` in the parallel branch (gives the "magic zero" when lines are collinear).
- `intersection_line_to_line(L1_P0, L1_P1, L2_P0, L2_P1, sc, tc) -> std::optional<Point3D>` — same setup, but returns the unique intersection point when the closest-approach distance is zero and the lines aren't parallel.
- `Line3D::Distance(Line3D|Ray3D|LineSegment3D)` — directed `LineSegment3D` from this line's closest point to the other primitive's closest point; `nullopt` when they intersect or overlap.
- `Line3D::DistanceTo(Line3D|Ray3D|LineSegment3D)` — scalar distance; 0 when they intersect or overlap.
- `Ray3D::Distance(Line3D|Ray3D|LineSegment3D)` / `Ray3D::DistanceTo(...)` — same shape for rays, with the ray-parameter clamp `sc >= 0`.
- `LineSegment3D::Distance(Line3D|Ray3D|LineSegment3D)` / `LineSegment3D::DistanceTo(...)` — same shape for segments, with the segment-parameter clamp `sc ∈ [0, 1]`.
- `LineSegment3D::Flip()` — returns a segment with endpoints swapped.
**Python / PyPI**
- `Plane.intersects(ray|segment|plane|triangle)` / `Plane.intersection(...)` overloads added.
- `Plane.is_parallel(line|ray|segment)` and `Plane.is_coplanar(line|ray|segment)` added.
- `Triangle3D.intersects(plane)` and `Triangle3D.intersection(plane)` overloads added.
- `Triangle3D.intersection(line|ray|segment|triangle)` now return real results (the bindings existed before but routed to stubs that threw).
- `Point2D(Vector2D)` and `Point3D(Vector3D)` constructors exposed via `py::init<const Vector2D&>()` / `py::init<const Vector3D&>()`.
- `Line3D.distance(other|ray|segment)` / `Line3D.distance_to(other|ray|segment)`.
- `Ray3D.distance(line|other|segment)` / `Ray3D.distance_to(line|other|segment)`.
- `LineSegment3D.distance(line|ray|other)` / `LineSegment3D.distance_to(line|ray|other)`.
- `LineSegment3D.flip()`.
**C# / NuGet**
- `Plane.Intersects(Ray3D^|LineSegment3D^|Plane^|Triangle3D^)` and matching `Plane.Intersection(...)` overloads. Plane∩Plane returns a `Line3D^` (or `null`); Plane∩Triangle returns a `Point3D^` / `LineSegment3D^` (or `null`).
- `Plane.IsParallel(Line3D^|Ray3D^|LineSegment3D^)` and `Plane.IsCoplanar(Line3D^|Ray3D^|LineSegment3D^)`.
- `Triangle3D.Intersects(Plane^)` and `Triangle3D.Intersection(Plane^)` overloads.
- `Triangle3D.Intersection(Line3D^|Ray3D^|LineSegment3D^|Triangle3D^)` now return real results (the bindings existed but the native side threw).
- `Point2D(Vector2D^)` and `Point3D(Vector3D^)` constructors.
- `Line3D.Distance(Line3D^|Ray3D^|LineSegment3D^)` / `Line3D.DistanceTo(...)`.
- `Ray3D.Distance(Line3D^|Ray3D^|LineSegment3D^)` / `Ray3D.DistanceTo(...)`.
- `LineSegment3D.Distance(Line3D^|Ray3D^|LineSegment3D^)` / `LineSegment3D.DistanceTo(...)`.
**Tooling / Documentation**
- `docs/Doxyfile` — Doxygen config: reads `geompp/include/*.hpp`, emits HTML reference under `docs/api/cpp/html/` and machine-readable XML under `docs/api/cpp/xml/`.
- `docs/gen_bindings_md.py` — transforms the Doxygen XML into per-class Markdown for three languages: `docs/api/cpp/md/<Class>.md`, `docs/api/python/<Class>.md`, `docs/api/csharp/<Class>.md`. Maps types and names per language (e.g., `LineSegment3D const&` → `LineSegment3D` in Python, `LineSegment3D^` in C#; `std::optional<X>` → `X | None` in Python, `X^ (nullable)` in C#; PascalCase → `snake_case` for Python only). Cross-links every class-name occurrence in signatures, parameter columns, and prose, and appends a `**See also:** …` footer per file.
- Doxygen `@brief` / `@param` / `@return` comments added to ~155 public methods across `line3d`, `line_segment3d`, `ray3d` (3D linear primitives), their 2D counterparts (`line2d`, `line_segment2d`, `ray2d`), `triangle2d/3d`, `polyline2d/3d`, `polygon2d/3d`, `bbox2d/3d`, `plane`, and `wkt_parser`. Coverage targets methods in `#pragma region line operations` and `#pragma region Geometrical Operations` (plus Plane's `Geometrial Operations` / `Collection Operations`), plus the typical ray helpers (`IsAhead`, `IsBehind`, `ToLine`, `ProjectOnto`) and `WktParser::Open` / `Next` / `HasNext`.
### Changed
**C++ core**
- `Plane::SignedDistanceTo(Point3D)` — removed the `round()` call. The default precision was 0 decimal places, so the function used to snap the signed distance to the nearest integer. This silently broke `Plane::ProjectOnto` (which multiplies the signed distance by the normal) for points within ±0.5 of the plane.
- `Triangle3D::Intersects(Triangle3D)` — was a throwing stub; now `return Intersection(other).has_value()` like every other `Intersects` overload.
- `Plane::Intersection(Triangle3D)` — was a throwing stub; now delegates to `Triangle3D::Intersection(Plane)`, unwrapping its variant into `Plane::ReturnSet`'s alternatives (`Point3D` or `LineSegment3D`).
- `Line3D::Intersection(other, sc, tc)` — the internal three-argument overload (used as a shared kernel by `Ray3D` / `LineSegment3D` intersection) was removed from `Line3D`'s public surface. The math now lives in the free function `intersection_line_to_line` in `calc_utils3d`. The single-argument `Line3D::Intersection(Line3D)` is unchanged.
### Fixed
**C++ core**
- `Plane` header had a duplicate `bool Intersects(Line3D const&) const;` declaration; removed.
- `Ray3D::DistanceTo(Line3D)` — when a ray hit the line at a non-origin point (`Distance` returned `nullopt`), the function fell through to `other.DistanceTo(ORIGIN)` (perpendicular distance from the ray's origin to the line) instead of returning 0. Now returns 0 whenever `Distance` is `nullopt` (intersect or overlap), matching the docstring.
- `Ray3D::Distance(Ray3D)`, `LineSegment3D::Distance(Ray3D)`, `LineSegment3D::Distance(LineSegment3D)` — collinear-overlap cases (two primitives sharing a region of the same infinite line) returned a non-zero segment because the ray / segment parameter clamping ran *before* the closest-points equality check, corrupting `distance_line_to_line`'s "magic zero" in the parallel branch. Each function now detects collinearity (unclamped points coincide) and short-circuits to `nullopt` when the two primitives genuinely share a region (verified via `Contains` on endpoints/origins).
### Notes / known limitations
- `Triangle2D::Intersection(Line2D)` returns `nullopt` when *all* intersection points coincide with triangle vertices (the deliberate "touch along an edge ≠ intersection" rule at `triangle2d.cpp:144`). This propagates through `Triangle3D::Intersection(Plane)` and `Triangle3D::Intersection(Triangle3D)`: a plane that cuts the triangle exactly along an edge will report no intersection. Tests document this behavior rather than work around it.
- `Plane::Intersection(Plane)`, `Plane::Intersection(Line3D/Ray3D/LineSegment3D)`, and the propagated triangle variants still collapse the coplanar case to `nullopt` — the `ReturnSet` variant can't represent "infinite intersections." `IsCoplanar` is the workaround.
- `Triangle3D::Intersection(Triangle3D)`: when the two triangles' plane-intersection segments are collinear but disjoint, the segment-overlap branch falls through and throws `"unexpected type of intersection result"` instead of returning `nullopt`. The test `Triangle3DTest.IntersectionWTriangle` asserts this with `EXPECT_ANY_THROW` so it's documented.
### Tests
**C++ (`geompp_tests`)**
- `test_plane.cpp`: `IntersectionWRay`, `IntersectionWLineSegment`, `IntersectionWPlane`, `IntersectionWTriangle`, `IsParallelWLine/Ray/LineSegment`, `IsCoplanarWLine/Ray/LineSegment` added.
- `test_triangle3d.cpp`: `IntersectionWLine` replaced with a real test (interior hit, vertex hit, edge-midpoint, miss, parallel-above, coplanar). `IntersectionWRay`, `IntersectionWLineSegment`, `IntersectionWPlane`, `IntersectionWPlane_Symmetric`, `IntersectionWTriangle` added (including the documented disjoint-segments throw).
- `test_point2d.cpp` / `test_point3d.cpp`: `FromVector` — explicit construction, implicit conversion, and round-trip via `ToVector`.
- `test_calc_utils3d.cpp` (new file): `DistanceLineToLine_{Intersecting,Skew,ParallelDistinct,Overlap,ZeroLengthInputs}` and `IntersectionLineToLine_{Intersecting,SkewLinesNoIntersection,ParallelDistinctNoIntersection,OverlapNoIntersection,IntersectAtEndpointParams}`.
- `test_line3d.cpp`: `DistanceToLine3D`, `DistanceToRay3D`, `DistanceToLineSegment3D` — each covers crossing → 0, parallel-distinct → perp dist, collinear overlap → 0, and skew where applicable.
- `test_ray3d.cpp`: `DistanceToLine3D`, `DistanceToRay3D`, `DistanceToLineSegment3D` — same shape (crossing, parallel, skew, collinear overlap, plus back-to-back rays).
- `test_line_segment3d.cpp`: `Flip`, `DistanceToLine3D`, `DistanceToRay3D`, `DistanceToLineSegment3D` — including the 2D-top-cross / 3D-separated skew case (`LineSegment3D ↔ Ray3D` where the ray crosses the segment in XY but sits at z=5).
**Python (`geompp_python/tests`)**
- `TestPoint2D.test_construction_from_vector` and `TestPoint3D.test_construction_from_vector`.
- `TestPlane`: `test_intersection_with_ray`, `test_intersection_with_line_segment`, `test_intersection_with_plane`, `test_is_parallel`, `test_is_coplanar`.
- `TestTriangle3D`: `test_intersection_with_line`, `test_intersection_with_ray`, `test_intersection_with_line_segment`, `test_intersection_with_plane`, `test_intersection_with_triangle`.
- `TestLine3D` / `TestRay3D` / `TestLineSegment3D`: `test_distance_to_line3d`, `test_distance_to_ray3d`, `test_distance_to_segment3d` — covering crossing → 0, parallel → perp dist, and collinear-overlap → 0 (and `Distance(...)` returning `None`). `TestLineSegment3D.test_flip` added.
**C# (`geompp_csharp/tests`)**
- `Point2D` / `Point3D` — `CreateFromVector_CopiesComponents`, `CreateFromVector_RoundtripViaToVector`.
- `Plane` — `Intersects_*`/`Intersection_*` for `Ray3D`, `LineSegment3D`, `Plane`, `Triangle3D`; `IsParallel_*` and `IsCoplanar_*` for line/ray/segment.
- `Triangle3D` — `Intersects_Line3D_*`/`Intersection_Line3D_*` and the same for `Ray3D`, `LineSegment3D`, `Plane`, `Triangle3D` (12+ new tests, including parallel-above, coplanar, and the documented disjoint-segments throw).
- `Line3D` / `Ray3D` / `LineSegment3D` — `*_DistanceTo_*_{Crossing_IsZero,ParallelDistinct,Skew,Overlap_IsZero}` for every pair of 3D linear primitives (~29 new tests). `LineSegment3D_DistanceTo_Ray3D_SkewTopCross` covers the 2D-top-cross / 3D-separated case.
---