GeomPP 0.7.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package GeomPP --version 0.7.0
                    
NuGet\Install-Package GeomPP -Version 0.7.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.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GeomPP" Version="0.7.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.7.0
                    
#r "nuget: GeomPP, 0.7.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.7.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.7.0
                    
Install as a Cake Addin
#tool nuget:?package=GeomPP&version=0.7.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 (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.7.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 */ }

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 Compatible and additional computed target framework versions.
.NET 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.

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.7.0` · C# / NuGet — tagged `csharp-v0.7.0` · Python / PyPI — tagged `python-v0.7.0`

> Touches `Triangle2D`, `Triangle3D`, `Polygon2D`, `Polygon3D`.

### Added

**C++ core**
- `Triangle2D::Location(Point2D const&)` → `std::optional<std::tuple<double, double>>` — returns barycentric coordinates `(s, t)` where `P = P0 + s·(P1−P0) + t·(P2−P0)`, if the point is inside or on the boundary; `nullopt` if outside. Inverse of `Interpolate`. Implemented via 2D perpendicular dot products (`u.Perp()` / `v.Perp()`).
- `Triangle3D::Location(Point3D const&)` → `std::optional<std::tuple<double, double>>` — same semantics; returns `nullopt` also when the point is off the triangle's plane. Implemented via 3D cross-product isolating each barycentric coordinate without any 2D projection.
- `Polygon2D::IsOnBoundary(Point2D const&) const` — returns `true` if the point lies exactly on an edge (outer ring or any hole boundary); uses `LineSegment2D::Contains` per edge, which tolerates floating-point rounding up to `DECIMAL_PRECISION` digits.
- `Polygon3D::IsOnBoundary(Point3D const&) const` — same semantics; rejects off-plane points immediately, then projects to 2D and delegates to `Polygon2D::IsOnBoundary`.

**Python / PyPI**
- `Triangle2D.location(point)` → `tuple[float, float] | None` — Python binding for the new `Location` method.
- `Triangle3D.location(point)` → `tuple[float, float] | None` — Python binding for the new `Location` method.
- `Polygon2D.is_on_boundary(point)` → `bool` — Python binding for the new method.
- `Polygon3D.is_on_boundary(point)` → `bool` — Python binding for the new method.

**C# / NuGet**
- `Triangle2D.Location(Point2D^ point)` → `Tuple<double, double>^` (or `null` if outside) — C# binding for the new `Location` method.
- `Triangle3D.Location(Point3D^ point)` → `Tuple<double, double>^` (or `null` if off-plane or outside) — C# binding for the new `Location` method.
- `Polygon2D.IsOnBoundary(Point2D^ point)` → `bool` — C# binding for the new method.
- `Polygon3D.IsOnBoundary(Point3D^ point)` → `bool` — C# binding for the new method.

### Changed

**C++ core**
- `Triangle2D::Contains(Point2D const&)` — rewritten to delegate entirely to `Location(point).has_value()`. Behavior is unchanged; implementation is now consistent and symmetric with `Interpolate`.
- `Triangle3D::Contains(Point3D const&)` — was an unimplemented stub (`throw std::runtime_error("not implemented")`); now fully implemented. Rejects off-plane points via `BBox3D` and plane check, then delegates to `Location(point).has_value()`. No 2D projection is performed.
- `Polygon2D::Contains(Point2D const&)` — was an unimplemented stub; now implemented using a winding-number algorithm with boundary-inclusive semantics: calls `IsOnBoundary` first, then falls back to winding number for strictly interior points.
- `Polygon3D::Contains(Point3D const&)` — was an unimplemented stub; now implemented. Returns `false` immediately for off-plane points; projects to 2D and applies the winding-number algorithm for in-plane points.

### Fixed

**C++ core**
- `Polygon2D::Contains(Point2D const&)` — fixed inverted boundary logic: `IsOnBoundary` is called first and short-circuits to `true`; previously the order was reversed, causing interior points to return `false`.
- `Polygon2D::FromWkt` / `Polygon3D::FromWkt` — were throwing for valid WKT strings in C++ tests (leftover `EXPECT_ANY_THROW` from when the function was a stub); tests updated to expect successful parse and verify vertex count and first point.
- `Triangle3DTest::Contains_OnBoundary` — off-plane assertion changed from `z=0.001` to `z=0.01`; with `DECIMAL_PRECISION=3` the epsilon is exactly `0.001`, so the old value was within tolerance and the point was classified as on-plane.

### Tests

**C++ (`geompp_tests`)**
- `test_triangle2d.cpp`: `Location` test rewritten — `check_inside` / `check_outside` lambdas that assert `Location` value AND `Contains` status together; round-trip A (`Interpolate(Location(p)) == p`) and round-trip B (`Location(Interpolate(s,t)) == (s,t)`).
- `test_triangle3d.cpp`: `Location` test added — same `check_inside` / `check_outside` / round-trip structure; off-plane point asserts both `Location == nullopt` and `Contains == false`.
- `test_triangle3d.cpp`: `Contains_OnBoundary` — off-plane assertion fixed to `z=0.01`.
- `test_polygon2d.cpp`: `Contains` test completed — interior, near-corner, exterior, and polygon-with-hole cases. `Contains_OnBoundary` — vertices, edge midpoints, and hole boundary. `IsOnBoundary_True` / `IsOnBoundary_False` — explicit standalone tests. `Wkt` and `FromFile` updated to verify successful round-trip.
- `test_polygon3d.cpp`: same coverage as 2D plus off-plane and YZ-plane cases.

**Python (`geompp_python/tests`)**
- `TestTriangle2D.test_location`: rewritten with `check_inside`/`check_outside` helpers and both round-trips.
- `TestTriangle3D.test_location`: added — same structure, including off-plane case.
- `TestPolygon2D.test_contains`: completed — interior, near-corner, exterior, polygon-with-hole, and boundary cases.
- `TestPolygon3D.test_contains`: completed — same plus off-plane assertion.
- `TestPolygon2D.test_is_on_boundary`: vertices, edge midpoints, interior/exterior false cases, hole boundary true and false cases.
- `TestPolygon3D.test_is_on_boundary`: same in 3D plus off-plane false case.

**C# (`geompp_csharp/tests`)**
- `Triangle2D` — `Location_Vertices_ReturnExpectedCoords_2D`, `Location_Centroid_OneThirdEach_2D`, `Location_NullImpliesNotContained_2D`, `Location_RoundTrip_A_And_B_2D`: each asserts `Location` value AND paired `Contains` call; round-trips A and B included.
- `Triangle3D` — `Location_Vertices_ReturnExpectedCoords`, `Location_Centroid_OneThirdEach`, `Location_NullImpliesNotContained`, `Location_RoundTrip_A_And_B`: same relationship-focused structure.
- `Polygon2D` — `Contains_Interior_True`, `Contains_Exterior_False`, `Contains_WithHole`, `Contains_OnBoundary_True`, `IsOnBoundary_OnEdge_True`, `IsOnBoundary_Interior_False` added.
- `Polygon3D` — `Contains_Interior_True`, `Contains_OffPlane_False`, `Contains_WithHole`, `Contains_OnBoundary_True`, `IsOnBoundary_OnEdge_True`, `IsOnBoundary_Interior_False` added.
- `Triangle3D` — `Contains_Interior_True`, `Contains_OffPlane_False` added (delegating to `Location`).

---