GeomPP 0.11.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package GeomPP --version 0.11.0
                    
NuGet\Install-Package GeomPP -Version 0.11.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GeomPP" Version="0.11.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GeomPP" Version="0.11.0" />
                    
Directory.Packages.props
<PackageReference Include="GeomPP" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GeomPP --version 0.11.0
                    
#r "nuget: GeomPP, 0.11.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GeomPP@0.11.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GeomPP&version=0.11.0
                    
Install as a Cake Addin
#tool nuget:?package=GeomPP&version=0.11.0
                    
Install as a Cake Tool

← back

GeomPP — C# Bindings

C++/CLI bindings for geompp, targeting Windows x64.

Available on NuGet as GeomPP.

Supports .NET 8, .NET 9, .NET 10, 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" />

How to use it

You can look at the test suite to see detailed usage. There also is a whole set of code examples in the next page.


Supported types

Type 2D 3D
Point
Vector
Line
Ray
LineSegment
Polyline
Triangle
Polygon
BBox
BBall
BRect2D
BPrism3D
Plane
View2D
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 */ }

Planar operations

View2D projects 3D points into 2D coordinates via .X(point) / .Y(point). It is particularly useful for streaming large containers of Point3D without allocating an intermediate list of Point2D — each call reads one or two scalar coordinates directly.

using G = GeomPP;

// axis-aligned views (fastest path)
var vXY = G.View2D.XY();   // x→x, y→y (drops z)
var vYZ = G.View2D.YZ();   // y→x, z→y (drops x)
var vZX = G.View2D.ZX();   // z→x, x→y (drops y)

// custom view onto any plane
var plane = G.Plane.FromOriginAndNormal(new G.Point3D(0, 0, 5), new G.Vector3D(0, 0, 1));
var vCustom = G.View2D.OnPlane(plane);

var pts3d = new[] { new G.Point3D(1, 2, 5), new G.Point3D(3, 4, 5), new G.Point3D(5, 6, 5) };

// stream 3D points to 2D without building a Point2D array
var xs = pts3d.Select(p => vXY.X(p)).ToArray();  // [1, 3, 5]
var ys = pts3d.Select(p => vXY.Y(p)).ToArray();  // [2, 4, 6]

Console.WriteLine(vXY.Type());  // XY

Bounding containers

BRect2D — minimum oriented bounding rectangle (rotating calipers; requires ≥ 3 non-collinear points):

using G = GeomPP;

var pts = new[] {
    new G.Point2D(0, 0), new G.Point2D(4, 0), new G.Point2D(4, 3),
    new G.Point2D(2, 4), new G.Point2D(0, 3),
};
var rect = new G.BRect2D(pts);
Console.WriteLine(rect.Center());                   // roughly (2.0, 1.75)
Console.WriteLine(rect.AxisU() + " " + rect.AxisV()); // orthonormal 2D frame
Console.WriteLine($"{rect.Width()} × {rect.Height()}  area={rect.Area()}");
var corners = rect.Corners();                        // array of 4 Point2D
Console.WriteLine(rect.Contains(new G.Point2D(2, 1))); // True
Console.WriteLine(rect.AlmostEquals(new G.BRect2D(pts))); // True

BPrism3D — minimum oriented bounding prism (PCA + rotating calipers; requires ≥ 3 non-collinear points):

using G = GeomPP;

var pts = new[] {
    new G.Point3D(0, 0, 0), new G.Point3D(4, 0, 0),
    new G.Point3D(4, 3, 0), new G.Point3D(0, 3, 0),
    new G.Point3D(0, 0, 2), new G.Point3D(4, 0, 2),
    new G.Point3D(4, 3, 2), new G.Point3D(0, 3, 2),
};
var prism = new G.BPrism3D(pts);
Console.WriteLine(prism.Center());                  // roughly (2, 1.5, 1)
Console.WriteLine($"U={prism.AxisU()} V={prism.AxisV()} W={prism.AxisW()}");
Console.WriteLine($"{prism.Width()} × {prism.Height()} × {prism.Depth()}");
Console.WriteLine($"volume={prism.Volume()}");      // ~24.0
var corners = prism.Corners();                      // array of 8 Point3D
Console.WriteLine(prism.Contains(prism.Center()));  // True
Console.WriteLine(prism.AlmostEquals(new G.BPrism3D(pts))); // True

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

Build the C# DLL

# from the main directory, geompp

# .NET 8 (LTS, supported until Nov 2026)
msbuild geompp_csharp\GeomPP_Net8.vcxproj /p:Platform=x64 /p:GeomppBuildRoot="$PWD\build_win" [/p:Configuration=Release]

# .NET 9 (STS)
msbuild geompp_csharp\GeomPP_Net9.vcxproj /p:Platform=x64 /p:GeomppBuildRoot="$PWD\build_win" [/p:Configuration=Release]

# .NET 10 (LTS)
msbuild geompp_csharp\GeomPP.vcxproj /p:Platform=x64 /p:GeomppBuildRoot="$PWD\build_win" [/p:Configuration=Release]

# .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 Compatible and additional computed target framework versions.
.NET net8.0-windows7.0 is compatible.  net9.0-windows was computed.  net9.0-windows7.0 is compatible.  net10.0-windows was computed.  net10.0-windows7.0 is compatible. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.8

    • No dependencies.
  • net10.0-windows7.0

    • No dependencies.
  • net8.0-windows7.0

    • No dependencies.
  • net9.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 100 8/15/2026
0.17.2 93 8/14/2026
0.16.2 89 8/6/2026
0.15.1 120 7/24/2026
0.14.0 105 7/21/2026
0.13.0 118 7/9/2026
0.12.0 105 7/8/2026
0.11.0 112 7/4/2026
0.10.1 126 6/23/2026
0.9.1 224 6/17/2026
0.8.2 118 5/18/2026
0.8.0 111 5/18/2026
0.7.0 104 5/6/2026
0.6.0 105 5/4/2026
0.5.0 104 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.11.0` · C# / NuGet — tagged `csharp-v0.11.0` · Python / PyPI — tagged `python-v0.11.0`

> Polygon2D line/ray/segment intersection and simplification; four bounding-shape classes (BRect2D, BPrism3D, BBall2D, BBall3D); convexity predicates for Polygon2D/3D and Polyline3D; planar Polyline3D operations (IsPlanar, IsSimple, ConvexHull, ToPolygon); PCA-based principal axes; Link-Time Optimization in Release builds; Python wheels extended to 3.8–3.14.

### Added

**C++ core**
- `Polygon2D::Intersection(Line2D)` / `Intersection(Ray2D)` / `Intersection(LineSegment2D)` — computes the chord(s) where a line, ray, or segment crosses a 2D polygon. Returns `std::optional<std::vector<LineSegment2D>>`: `std::nullopt` on miss, or one or more chord segments. Convex polygons use the fast Cyrus-Beck parametric clip (outward-normal convention, D < 0 entering); non-convex polygons use the Jordan-curve parity approach. Results are clipped to the ray's or segment's domain. Implemented via `detail::compute_intersection_intervals_2d`.
- `Polygon2D::Intersects(Line2D)` / `Intersects(Ray2D)` / `Intersects(LineSegment2D)` — boolean wrappers delegating to `Intersection`.
- `Polygon2D::Simplify()` / `Polygon3D::Simplify()` — decomposes a self-intersecting polygon into a `vector` of simple polygons via Bentley–Ottmann intersection detection followed by planar-graph half-edge face tracing. Returns `{*this}` when already simple. Handles all three dominant-axis projections (X, Y, Z) including the Y-axis chirality flip case.
- `Polygon2D::IsConvex()` — returns false if the polygon has holes or any concave turn; true otherwise.
- `Polygon3D::IsConvex()` — same, using the stored plane normal for the 3D left-turn test.
- `Polyline3D::IsPlanar()` — true if all knots are coplanar (degenerate cases: <3 points or all collinear also return true).
- `Polyline3D::IsSimple()` — no self-intersections; uses Shamos–Hoey for planar polylines, Bentley–Ottmann + 3D verification for non-planar.
- `Polyline3D::IsConvex()` — throws `std::logic_error` if not planar; checks all consecutive triples make a left turn relative to the plane normal.
- `Polyline3D::ConvexHull()` — throws if not planar; Melkman's deque algorithm; returns `Polyline3D` (open hull path, not a closed polygon).
- `Polyline3D::ToPolygon()` — throws if not planar; closes the open path into a `Polygon3D`.
- `CoordinateFrame` struct (`calc_utils3d.hpp`) — `Vector3D X` (primary/largest variance), `Y` (secondary), `Z` (normal/least variance).
- `principal_axes(vector<Point3D>)` — PCA via Jacobi eigendecomposition on the 3×3 covariance matrix; returns `CoordinateFrame`; stable for any point distribution including non-planar clouds and helices.
- `principal_normal(vector<Point3D>)` — best-fit plane normal; delegates to `principal_axes().Z`.
- `principal_direction(vector<Point3D>)` — dominant spread direction; delegates to `principal_axes().X`.
- `BRect2D` (`brect2d.hpp`) — minimum oriented bounding rectangle via Andrew's monotone-chain convex hull followed by rotating calipers (Freeman & Shapira 1975 / Toussaint 1983). Stores `center`, `axis_u`, `axis_v` (unit vectors), `half_len_u`, `half_len_v`. Methods: `Corners()` (4 `Point2D`), `Contains(Point2D)`, `area()`, `width()`, `height()`, `AlmostEquals()`. Throws `std::invalid_argument` for fewer than 3 points or a collinear/coincident cloud.
- `BPrism3D` (`bprism3d.hpp`) — oriented bounding prism via PCA + rotating calipers. Stores `center`, `axis_u`, `axis_v`, `axis_w` (unit vectors), `half_len_u`, `half_len_v`, `half_len_w`. Methods: `Corners()` (8 `Point3D`), `Contains(Point3D)`, `volume()`, `width()`, `height()`, `depth()`, `AlmostEquals()`. Coplanar inputs produce `half_len_w == DOUBLE_EPSILON`. Throws for fewer than 3 points or a collinear/coincident cloud.
- `BBall2D` (`bball2d.hpp`) — minimum bounding ball in 2D; Ritter's two-pass O(N) algorithm. Stores `center` (`Point2D`) and `radius`. Degenerate inputs: 1 point → zero-radius ball; 2 points → midpoint center, half-distance radius. Throws for empty input.
- `BBall3D` (`bball3d.hpp`) — same Ritter algorithm in 3D. Throws for empty input.

**Python / PyPI**
- `Polygon2D.intersection(line|ray|segment)` — returns `None` on miss or `list[LineSegment2D]` for chord(s).
- `Polygon2D.intersects(line|ray|segment)` — boolean check.
- `Polygon2D.simplify()` / `Polygon3D.simplify()` — returns `list[Polygon2D]` or `list[Polygon3D]`.
- `Polygon2D.is_convex()`, `Polygon3D.is_convex()`.
- `Polyline3D.is_planar()`, `is_simple()`, `is_convex()`, `convex_hull()` → `Polyline3D`, `to_polygon()` → `Polygon3D`.
- `CoordinateFrame` class with `x`, `y`, `z` attributes (all `Vector3D`).
- `principal_axes(points)` → `CoordinateFrame`, `principal_normal(points)` → `Vector3D`, `principal_direction(points)` → `Vector3D`.
- `BBall2D(center, radius)` / `BBall2D(points)` — `center`, `radius`, `contains(p)`, `almost_equals(other)`.
- `BBall3D(center, radius)` / `BBall3D(points)` — same in 3D.
- `BRect2D(points)` — `center`, `axis_u`, `axis_v`, `half_len_u`, `half_len_v`, `width()`, `height()`, `area()`, `corners()`, `contains(p)`, `almost_equals(other)`.
- `BPrism3D(points)` — `center`, `axis_u`, `axis_v`, `axis_w`, `half_len_u`, `half_len_v`, `half_len_w`, `width()`, `height()`, `depth()`, `volume()`, `corners()`, `contains(p)`, `almost_equals(other)`.

**C# / NuGet**
- `Polygon2D.Intersection(Line2D|Ray2D|LineSegment2D)` — returns `null` on miss or `LineSegment2D[]` for chord(s). Use `is LineSegment2D[] segs` pattern matching.
- `Polygon2D.Intersects(Line2D|Ray2D|LineSegment2D)` — boolean wrappers.
- `Polygon2D.Simplify()` / `Polygon3D.Simplify()` — returns `Polygon2D[]` or `Polygon3D[]`.
- `Polygon2D.IsConvex()`, `Polygon3D.IsConvex()`.
- `Polyline3D.IsPlanar()`, `IsSimple()`, `IsConvex()`, `ConvexHull()` → `Polyline3D^`, `ToPolygon()` → `Polygon3D^`.
- `CoordinateFrame` ref class with `X`, `Y`, `Z` properties (`Vector3D^`).
- `GeomUtil.PrincipalAxes()` → `CoordinateFrame^`, `PrincipalNormal()` → `Vector3D^`, `PrincipalDirection()` → `Vector3D^`.
- `BBall2D(Point2D^, double)` / `BBall2D(array<Point2D^>^)` — `Center`, `Radius`, `Contains(Point2D^)`, `AlmostEquals(BBall2D^)`.
- `BBall3D(Point3D^, double)` / `BBall3D(array<Point3D^>^)` — same in 3D.
- `BRect2D(array<Point2D^>^)` — `Center`, `AxisU`, `AxisV`, `HalfLenU`, `HalfLenV`, `Width()`, `Height()`, `Area()`, `Corners()`, `Contains(Point2D^)`, `AlmostEquals(BRect2D^)`.
- `BPrism3D(array<Point3D^>^)` — `Center`, `AxisU`, `AxisV`, `AxisW`, `HalfLenU`, `HalfLenV`, `HalfLenW`, `Width()`, `Height()`, `Depth()`, `Volume()`, `Corners()`, `Contains(Point3D^)`, `AlmostEquals(BPrism3D^)`.
- `.NET 8` build target (`GeomPP_Net8.vcxproj`) and `.NET 9` build target (`GeomPP_Net9.vcxproj`). The NuGet package ships four C++/CLI DLLs: `net8.0-windows7.0`, `net9.0-windows7.0`, `net10.0-windows7.0`, and `net48`.

### Performance

**C++ core**
- **Link-Time Optimization (LTO)** enabled for Release builds on the `geompp` static library and `_geompp` Python extension (`/GL` + `/LTCG` on MSVC; `-flto` on GCC/Clang). Uses `CheckIPOSupported` with a graceful `STATUS` fallback when LTO is unavailable.
- **Extern template for all bounding-shape constructors**: `vector<PointN>` template constructors of `BBox2D`, `BBox3D`, `BBall2D`, `BBall3D`, `BRect2D`, and `BPrism3D` moved from header to `.cpp` (same pattern as `convex_hull_monotone_chain` and `min_bounding_rect`), reducing per-TU instantiation cost and binary size.
- **`View2D` 3D-to-2D projection**: `View2D::x(Point3D)` / `y(Point3D)` project without allocating an intermediate `Point2D`. Axis-aligned views (`XY`, `YZ`, `ZX`) read a single coordinate at zero arithmetic cost; `Custom` computes `(p − ORIGIN).Dot(AXIS_U/V)` in-place.
- `simplify_rings_impl`: adjacency-list duplicate check changed from O(degree) `std::find` per edge to a single `std::sort` + `std::unique` pass after insertion.
- `simplify_rings_impl`: half-edge walk neighbor lookup changed from O(degree) linear scan to O(log degree) `std::upper_bound` on a precomputed angle array.
- `Polygon2D/3D::Simplify()`: hole-assignment polygon construction reduced from O(nc²) repeated `Polygon2D::Make` calls to O(nc) pre-built polygons with reuse.

**Python / PyPI**
- Wheel targets extended to **3.8–3.14** (was 3.8–3.12). Wheels for CPython 3.13 and 3.14 published on PyPI for Linux x86_64 and Windows AMD64.

### Fixed

**C++ core**
- `Plane(origin, normal)` private constructor now normalizes the normal. Previously the raw non-unit vector was stored, causing `SignedDistanceTo` and other distance operations to return scaled results. `From3Points` and `FromOriginAndAxes` were not affected.
- `Polygon2D/3D::Simplify()`: hole-assignment test point changed from midpoint of first edge (can land on a boundary) to centroid of the ring (always interior for convex decomposition faces).
- `Polygon2D/3D::Simplify()`: silent `catch(...)` blocks replaced with `catch(std::runtime_error const&)` and `GEOMPP_LOG(WARNING)` so degenerate-ring failures are visible.
- `simplify_rings_impl`: half-edge walk `delta <= 0.0` comparison replaced with `compare(delta, 0.0, 1e-9) <= 0` to prevent floating-point noise from selecting the reverse edge.

### Tests

**C++ (`geompp_tests`)**
- `test_polygon2d.cpp`: `Intersection_Line_*`, `Intersection_Ray_*`, `Intersection_Segment_*`, `Intersects_*`; `IsConvex_Square_True`, `IsConvex_ConcavePolygon_False`, `IsConvex_WithHole_False`, `IsConvex_Triangle_True`; Simplify suite.
- `test_polygon3d.cpp`: `IsConvex_Square_XYPlane_True`, `IsConvex_ConcavePolygon_False`, `IsConvex_WithHole_False`, `IsConvex_YZPlane_True`; Simplify suite.
- `test_polyline3d.cpp`: `IsPlanar_XYPlane_True`, `IsPlanar_NonPlanar_False`, `IsPlanar_Collinear_True`, `IsSimple_PlanarNoSelfIntersect_True`, `IsSimple_PlanarSelfIntersecting_False`, `IsConvex_PlanarConvex_True`, `IsConvex_PlanarConcave_False`, `IsConvex_NotPlanar_Throws`, `ConvexHull_PlanarPolyline_ReturnsPolyline`, `ConvexHull_NotPlanar_Throws`, `ConvexHull_ThenToPolygon_ValidPolygon`, `ToPolygon_PlanarPolyline_Valid`, `ToPolygon_NotPlanar_Throws`.
- `test_calc_utils3d.cpp`: `PrincipalAxes_PlanarXYCloud_ZIsNormal`, `PrincipalAxes_ElongatedAlongX_XIsLongest`, `PrincipalAxes_AxesAreOrthogonal`, `PrincipalAxes_AxesAreUnitVectors`, `PrincipalNormal_PlanarCloud_MatchesBasisZ`, `PrincipalDirection_ElongatedAlongX_MatchesBasisX`, `PrincipalAxes_TooFewPoints_Throws`.
- `test_bball2d.cpp`: `ConstructorCenterRadius`, `ConstructorFromSinglePoint`, `ConstructorFromTwoPoints`, `ConstructorFromPointsAllContained`, `ConstructorEmptyThrows`, `CopyConstructor`, `Assignment`, `AlmostEquals`, `Contains`.
- `test_bball3d.cpp`: same suite plus `ConstructorFromTwoPointsAlongZ`.
- `test_brect2d.cpp`: `ConstructorEmpty_Throws`, `ConstructorSinglePoint_ZeroExtent`, `ConstructorTwoPoints_DegenerateLine`, `ConstructorAxisAlignedSquare`, `ConstructorAxisAlignedRectangle`, `ConstructorNonConvex_SmallArea`, `ConstructorAllPointsContained`, `Accessors_AxisesAreUnitVectors`, `Accessors_AxesOrthogonal`, `Accessors_WidthHeightArea`, `Corners_FourDistinctPoints`, `Contains_Center_True`, `Contains_Interior_True`, `Contains_Boundary_True`, `Contains_Outside_False`, `AlmostEquals_SameRect`, `AlmostEquals_DifferentRect`, `CopyConstructor`, `Assignment`.
- `test_bprism3d.cpp`: `ConstructorEmpty_Throws`, `ConstructorSinglePoint_Throws`, `ConstructorTwoPoints_Throws`, `ConstructorAxisAlignedBox`, `ConstructorFlatCloud_WIsEpsilon`, `ConstructorNonConvex_AllPointsContained`, `Accessors_AxesAreUnitVectors`, `Accessors_AxesOrthogonal`, `Accessors_WidthHeightDepthVolume`, `Corners_EightDistinctPoints`, `Contains_Center_True`, `Contains_Interior_True`, `Contains_Outside_False`, `Contains_Boundary_True`, `AlmostEquals_Same`, `AlmostEquals_Different`, `CopyConstructor`, `Assignment`.

**Python (`geompp_python/tests`)**
- `TestPolygon2DIntersection`: intersection and intersects suites for line, ray, and segment.
- `TestPolygon2DSimplify`, `TestPolygon3DSimplify`: Simplify suites.
- `TestPolygon2DIsConvex`: `test_square_is_convex`, `test_concave_not_convex`, `test_with_hole_not_convex`.
- `TestPolygon3DIsConvex`: `test_square_xy_plane_is_convex`, `test_concave_not_convex`, `test_with_hole_not_convex`.
- `TestPolyline3DPlanarConvex`: `test_is_planar_xy`, `test_is_planar_nonplanar`, `test_is_simple_planar`, `test_is_convex_planar`, `test_is_convex_not_planar_throws`, `test_convex_hull_returns_polyline`, `test_convex_hull_to_polygon`, `test_to_polygon_not_planar_throws`.
- `TestPrincipalAxes`: `test_coordinate_frame_attributes`, `test_z_is_normal_for_flat_xy_cloud`, `test_axes_are_orthogonal`, `test_axes_are_unit_vectors`, `test_principal_normal_matches_z`, `test_principal_direction_matches_x`, `test_too_few_points_throws`.
- `TestBBall2D` / `TestBBall3D`: constructor, contains, almost_equals suites.
- `TestBRect2D` / `TestBPrism3D`: constructor, accessors, corners, contains, almost_equals suites.

**C# (`geompp_csharp/tests`)**
- `Polygon2D`: Intersection/Intersects suites for Line2D, Ray2D, LineSegment2D; Simplify suite; `IsConvex_Square_True`, `IsConvex_Concave_False`, `IsConvex_WithHole_False`.
- `Polygon3D`: `IsConvex_Square_True`, `IsConvex_Concave_False`; Simplify suite.
- `Polyline3D`: `IsPlanar_XY_True`, `IsPlanar_NonPlanar_False`, `IsSimple_True`, `IsConvex_Planar_True`, `IsConvex_NotPlanar_Throws`, `ConvexHull_ReturnsPolyline`, `ConvexHull_ThenToPolygon`, `ToPolygon_Valid`, `ToPolygon_NotPlanar_Throws`.
- `GeomUtil`: `PrincipalAxes_NotNull`, `PrincipalAxes_Z_IsNormal`, `PrincipalNormal_NotNull`, `PrincipalDirection_NotNull`, `PrincipalDirection_AlongX`.
- `BBall2D` / `BBall3D`: `ConstructorCenterRadius`, `ConstructorFromSinglePoint`, `ConstructorFromTwoPoints`, `ConstructorFromPointsAllContained`, `Contains_Inside_True`, `Contains_Outside_False`, `AlmostEquals_Same`, `AlmostEquals_Different`.
- `BRect2D`: `ConstructorEmpty_Throws`, `ConstructorFromPoints_AllContained`, `Accessors_AxesUnitAndOrthogonal`, `Contains_Center_True`, `Contains_Outside_False`, `AlmostEquals_Same`.
- `BPrism3D`: `ConstructorEmpty_Throws`, `ConstructorSinglePoint_Throws`, `ConstructorTwoPoints_Throws`, `ConstructorAxisAlignedBox`, `Accessors_AxesUnitAndOrthogonal`, `Contains_Center_True`, `Contains_Outside_False`, `AlmostEquals_Same`.

---