GeomPP 0.11.0
See the version list below for details.
dotnet add package GeomPP --version 0.11.0
NuGet\Install-Package GeomPP -Version 0.11.0
<PackageReference Include="GeomPP" Version="0.11.0" />
<PackageVersion Include="GeomPP" Version="0.11.0" />
<PackageReference Include="GeomPP" />
paket add GeomPP --version 0.11.0
#r "nuget: GeomPP, 0.11.0"
#:package GeomPP@0.11.0
#addin nuget:?package=GeomPP&version=0.11.0
#tool nuget:?package=GeomPP&version=0.11.0
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 | Versions 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. |
-
.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`.
---