Euclid.Kontur
0.1.0
Prefix Reserved
dotnet add package Euclid.Kontur --version 0.1.0
NuGet\Install-Package Euclid.Kontur -Version 0.1.0
<PackageReference Include="Euclid.Kontur" Version="0.1.0" />
<PackageVersion Include="Euclid.Kontur" Version="0.1.0" />
<PackageReference Include="Euclid.Kontur" />
paket add Euclid.Kontur --version 0.1.0
#r "nuget: Euclid.Kontur, 0.1.0"
#:package Euclid.Kontur@0.1.0
#addin nuget:?package=Euclid.Kontur&version=0.1.0
#tool nuget:?package=Euclid.Kontur&version=0.1.0

Euclid.Kontur
Exact and fast boolean operations on 2D polygons in F#: union, intersection, difference and xor, plus
self-intersection cleanup and union of many shapes. Euclid.Kontur works directly with floating-point
coordinates and closed Polyline2Ds from Euclid.
Written 99% by ChatGPT-6-Astra and Claude-Fable-5.1. But diligently prompted and tested with insights gained from porting Clipper2 to F# and building Euclid.
The library targets .NET 6.0 and .NET Framework 4.7.2. The NuGet package also includes its F# source for compilation to JavaScript and TypeScript with Fable.
API documentation · Changelog · Algorithm design
Install
dotnet add package Euclid.Kontur
For an F# script, use #r "nuget: Euclid.Kontur". Euclid is included as a dependency.
Quick start
open Euclid
let rectangle x y width height =
let path =
Polyline2D.createFromPts [
Pt (x, y)
Pt (x + width, y)
Pt (x + width, y + height)
Pt (x, y + height)
]
path.CloseInPlace 0.0
Kontur.ofPolyline path // NonZero fill rule by default
let a = rectangle 0.0 0.0 10.0 10.0
let b = rectangle 5.0 0.0 10.0 10.0
let merged = Kontur.union a b
let overlap = Kontur.intersection a b
let cut = Kontur.difference a b // subject minus clip
let exclusive = Kontur.xor a b
printfn "Union: %d contour(s), area %g" merged.PathCount merged.SignedArea
// Union: 1 contour(s), area 150
let inside = merged.Contains (Pt (2.0, 2.0)) // true
let contours : ResizeArray<Polyline2D> = merged.Paths
Input paths must be closed: the last point repeats the first. Close them explicitly before
creating a Kontur; open paths are rejected. A Kontur keeps references to its input polylines,
so changing those polylines also changes the shape.
Results contain closed contours with counterclockwise outer boundaries and clockwise holes,
and use FillRule.Positive. SignedArea gives the net area of a result, subtracting holes.
For an unsimplified input shape with overlapping paths, its sum of signed areas need not equal
the area of the filled region.
Fill rules and multiple paths
Each shape has its own fill rule: NonZero, EvenOdd, Positive or Negative. Subject and
clip winding numbers are evaluated separately, so shapes with different rules can be combined
in one operation. For example, an EvenOdd outline can be cut from a NonZero solid.
Using the rectangles from the quick start:
let outline = rectangle 0.0 0.0 10.0 10.0
let hole = rectangle 2.0 2.0 6.0 6.0
let frame = Kontur.create (Seq.append outline.Paths hole.Paths, FillRule.EvenOdd)
let cleaned = Kontur.simplify frame // resolves overlaps and self intersections under its rule
let filled = Kontur.unionAll [ frame; hole ] // simplifies each shape, then merges the results
Use simplify for paths that together define one region under one fill rule. Use unionAll
for independent shapes: each is simplified under its own rule before the final merge.
Tolerance and coordinate preservation
Euclid.Kontur uses an absolute distance tolerance in the same units as the coordinates; the default
is 1e-6. Choose it to suit the smallest features you need to keep. Every operation has a
...With variant for an explicit tolerance:
let mergedAtTolerance = Kontur.unionWith 1e-4 a b
Coordinates are never quantized to an integer grid. Retained input vertices keep their original coordinates, and each new intersection is shared by the intersecting edges. Vertices within tolerance can merge onto an existing representative; nearby features can therefore disappear. Merging is transitive, so a chain of nearby vertices can collapse even when its endpoints are farther apart than the tolerance. Collinear input vertices are retained by default.
This is tolerance-based geometry, not exact arithmetic. Residual crossings and slivers at the
tolerance scale may remain, and Contains on a boundary may return either result. Version
0.1.0 supports closed polygonal paths; open-path clipping, curves, offsetting and a nesting-tree
result are outside its scope.
Reuse an engine
The convenience functions create an engine per call. For repeated operations, reuse one engine to retain its scratch buffers and reduce allocations:
let engine = KonturEngine 1e-4
let intersection = engine.Execute (a, b, ClipType.Intersection)
let simplified = engine.Simplify frame
let combined = engine.UnionAll [ a; b; frame ]
An engine is not thread safe; use a separate instance for each concurrent operation. Results own their output polylines and remain valid when the engine is reused.
Internally, Euclid.Kontur finds candidate segment pairs with sweep and prune, clusters nearby vertices, then propagates winding numbers through a planar graph and links the selected boundaries. Flat numeric arrays keep the same implementation efficient on .NET and under Fable.
Build
Developing the repository requires the .NET 10 SDK; the library itself still targets net6.0
and net472.
dotnet build Euclid.Kontur.slnx --configuration Release
Test
Run the .NET tests from the repository root:
dotnet run --project Test/Test.fsproj --configuration Release
The JavaScript and TypeScript checks also require Node.js and npm:
cd Test
npm ci
npm test
The same geometry tests run on .NET and Node, including randomized point-in-region checks,
area identities, degenerate geometry, Klip regression cases and 195 Clipper2 polygon fixtures.
npm test runs the JavaScript tests in Release and Debug configurations and checks the generated
TypeScript declarations.
Benchmark
Benchmarks compare shared fixtures against Klip and Clipper2 on .NET and Klip and clipper2-ts on Node. Performance depends on the input and tolerance; see the benchmark instructions and recorded results.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net472 is compatible. net48 was computed. net481 was computed. |
-
.NETFramework 4.7.2
- Euclid (>= 0.51.0)
- FSharp.Core (>= 6.0.7)
-
net6.0
- Euclid (>= 0.51.0)
- FSharp.Core (>= 6.0.7)
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.1.0 | 94 | 9/20/2026 |
### Added
- First public release of Kontur.
- Boolean operations on Euclid `Polyline2D` polygons: union, intersection, difference and xor, plus simplification of self intersections and union of many shapes.
- `Shape`, `FillRule` and `ClipType`, with NonZero, EvenOdd, Positive and Negative fill rules chosen independently for each shape; point containment, winding number, bounds and signed area queries.
- `KonturEngine` with `Execute`, `Simplify` and `UnionAll`, reusable scratch buffers, and an explicit absolute tolerance; convenience functions in the `Kontur` module use a default tolerance of `1e-6` or accept one through their `...With` variants.
- Floating-point coordinates without integer-grid quantization; tolerance-based handling of touching vertices, T junctions, collinear overlaps, duplicate points, spikes and self intersections, preserving the coordinates of retained input vertices.
- Closed result contours with counterclockwise outer boundaries, clockwise holes and `FillRule.Positive`; collinear input vertices are retained by default.
- A flat-array pipeline with sweep-and-prune segment pairing, vertex clustering, graph construction, winding propagation seeded from graph edges, and contour linking; a private BVH accelerates winding queries across disconnected components.
- Validation of open paths, invalid tolerances and non-finite coordinates, with errors reported through Euclid.
- .NET 6.0 and .NET Framework 4.7.2 targets, with Fable source included in the NuGet package for JavaScript and TypeScript compilation.
- Shared .NET and Node tests covering point-in-region oracles, area identities, random and degenerate polygons, the Klip regression cases and 195 Clipper2 polygon fixtures.
- Shared benchmark fixtures and scripts comparing Kontur with Klip and Clipper2 on .NET and with Klip and clipper2-ts on Node, plus noisy polygon datasets, phase profiling and Rhino reproduction scripts.
- API documentation, usage examples and the reviewed algorithm design.