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
                    
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="Euclid.Kontur" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Euclid.Kontur" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Euclid.Kontur" />
                    
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 Euclid.Kontur --version 0.1.0
                    
#r "nuget: Euclid.Kontur, 0.1.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 Euclid.Kontur@0.1.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=Euclid.Kontur&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Euclid.Kontur&version=0.1.0
                    
Install as a Cake Tool

Euclid.Kontur logo

Euclid.Kontur

Euclid.Kontur on nuget.org Build Status Test Status Docs Build Status MIT license

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

MIT

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.