.NET methods for numeric area and region overlap, with no external runtime dependencies in the core. Includes self-intersecting paths with explicit fill rules, plus normalization, resampling and alignment. Supports .NET Standard 2.0 and .NET 10. MIT licensed.
dotnet add package PolylineKit.Core --prereleaseusing PolylineKit;
Point2[] first = [new(0, 0), new(2, 0), new(2, 2), new(0, 2)];
Point2[] second = [new(1, 0), new(3, 0), new(3, 2), new(1, 2)];
double? iou = PolylineArea.CompareRegions(first, second).IntersectionOverUnion; // 1/3Trying it on your own polygons? Follow the short walkthrough and share your result, including where the input or API needs to be clearer.
| You need | Use |
|---|---|
| Area, intersection, IoU or coverage as numbers, with an explicit fill rule for self-intersecting input | PolylineKit.Core |
| Output polygons, offsets or general Boolean geometry | Clipper2, or the optional PolylineKit.Clipper adapter for its supported operations |
| A geometry model, topology validation, spatial predicates and geometry overlays | NetTopologySuite |
Recorded workloads give a guide, not a universal ranking: the building evaluator was 3.05× faster than its configured NTS backend and effectively tied with Clipper2; the complete county/district coverage took 24–25% less time than reusable Clipper2. At 1,024 rings per operand, warm Core comparisons were 18–34% slower than Clipper2. Selected warm geometry calls allocated 0 B; preparation, first calls, growth and oversized workspaces can allocate. These are historical measurements, not timings of the current package version. See the measured scopes and limits.
| Developer task | Result | Runnable example |
|---|---|---|
| Check a model's building outlines against reference annotations | Matched and missed buildings, false detections, IoU, missing/excess area and an interactive HTML review | Building annotation evaluation |
| Calculate how much of one territory lies in another | Shared square metres and the fraction of each county covered by a congressional district | Territorial coverage |
| Simplify a contour within an area-change budget | Draw, load JSON or choose a contour, inspect the changed fill, and export the selected SVG/JSON | Interactive contour simplification; batch checks |
Run the contour demo locally with
dotnet run --project examples/ContourSimplificationDemo -c Release, then open
http://127.0.0.1:5090/. It measures a finite series generated by Clipper2 and
selects the fewest vertices within the chosen Jaccard-distance budget.
Load one contour from an XY points array or the demo's own JSON export; see
the input format and limits.
For annotations and territorial coverage, positions are part of the question: keep the supplied coordinates. RMS between boundary samples does not supply intersection, missing area or coverage. Normalization and alignment are separate tools for applications where moving or resizing a shape is intentional.
These are implemented integration examples with recorded checks and measurements; they do not establish external production adoption. The building evaluator shows the most complete review workflow. The territorial example has a recorded speed advantage for its coverage workload. See the scenario-specific evidence.
Actual HTML review: prediction #3 against reference #15 in the supplied Las Vegas sample. IoU is 0.68774; missing area is 505.89 pixel² and excess area is 972.32 pixel². The view shows original contours, without an image raster.
Choose PolylineKit.Core for numeric areas, intersection and coverage. The
command selects the latest prerelease available on
NuGet.org:
dotnet add MyApp/MyApp.csproj package PolylineKit.Core --prereleaseThe package guide covers installation and the optional adapter. Alpha.3 is published on NuGet.org: Core and the optional Clipper adapter. It delivers the guarded orientation optimization for the polygon-role factories; see the alpha.3 release notes. The GitHub prerelease preserves the verified original packages. Alpha.1 and alpha.2 package contents remain unchanged. Alpha.1 was unlisted on NuGet.org on 2026-10-14 and remains available through exact-version restore; use alpha.3 for new installations. The polygon-role factories were introduced in alpha.2.
Alternatively, use a project reference:
git clone https://github.com/bgtnt/polylinekit.git
dotnet add MyApp/MyApp.csproj reference polylinekit/src/PolylineKit.Core/PolylineKit.Core.csprojPolylineKit.Core includes area methods, normalization, scaling, resampling and
alignment. All use the PolylineKit namespace. Alpha status means the package
has an API that may evolve, with numerical and input limits described below.
The optional PolylineKit.Clipper project adds Clipper2 for resolved output
contours and the existing quantized PolylineComparison methods. Only reference
it if you need those operations; see the adapter guide.
using PolylineKit;
Point2[] zone = [new(0, 0), new(6, 0), new(6, 2),
new(2, 2), new(2, 6), new(0, 6)];
Point2[] footprint = [new(1, 1), new(5, 1), new(5, 5), new(1, 5)];
const PathFillRule rule = PathFillRule.NonZero;
double zoneArea = PolylineArea.FilledArea(zone, rule); // 20; cache for this zone
double intersection = PolylineArea.IntersectionArea(zone, footprint, rule); // 7
double? coverage = zoneArea > 0 ? intersection / zoneArea : null; // 35%
var change = PolylineArea.CompareRegions(zone, footprint, rule);
// FirstArea = 20; SecondArea = 16; UnionArea = 29;
// SymmetricDifferenceArea = 22; IntersectionOverUnion = 7/29.Paths close implicitly. Self-intersections and repeated edges are accepted.
PathFillRule.NonZero is the default; pass PathFillRule.EvenOdd to use parity
fill. Areas use squared coordinate units. These methods calculate values without
producing output contours or quantizing input onto a decimal grid.
Coverage answers how much of the fixed zone is covered; IoU compares the shared
area with the union of both regions. A zero-area zone has undefined coverage
(null). Summing coverage from overlapping footprints can double-count area.
This small example explains the result, not its speed. For a trusted simple
ring's own area alone, a shoelace sum is cheaper than general fill-aware processing.
Run the package-based example for checked results.
| You need | Method |
|---|---|
| Area filled by one closed path | PolylineArea.FilledArea(path, fillRule) |
| Area shared by two single-walk regions | PolylineArea.IntersectionArea(first, second, fillRule) |
| Single-walk region change, union, XOR, IoU or Jaccard distance | PolylineArea.CompareRegions(first, second, fillRule) |
| Polygons with holes or disconnected components | Preserve shell/hole roles with PreparedRegion.FromPolygon or FromPolygons; use RegionArea.FilledArea, IntersectionArea or Compare |
| Vertical separation between increasing-x graphs | PolylineArea.BetweenGraphs(first, second) |
| Centering and scaling | PolylineNormalization.ToUnitBounds(path) / MatchBounds(path, reference) |
| Equidistant samples along a path | PolylineSampling.ResampleByArcLength(path) |
| Translation, rotation and optional uniform-scale alignment | PolylineAlignment.FitSimilarity(moving, reference) |
| Resolved contours as well as area | Optional Clipper adapter: PolylineComparison.EndpointBridgedArea(...) / FilledRegionDifference(...), with includeContours: true |
FromPolygon and FromPolygons orient rings by their supplied shell/hole roles.
FromRings preserves the supplied directions for raw winding input. Each ring
closes independently; none of these methods repairs topology. See
polygon input and the 100-versus-84 hole example.
Normalization, alignment and measurement are separate operations. For example:
Point2[] moving = AffineTransform2D.Scaling(2)
.Then(AffineTransform2D.Translation(10, -5)).Apply(zone);
var fit = PolylineAlignment.FitSimilarity(moving, zone);
double rms = fit.RmsError;
var alignedChange = PolylineArea.CompareRegions(zone, fit.AlignedPoints);Alignment minimizes sampled squared distances. Area measures accumulated region difference; it does not bound maximum boundary displacement or identify every distinct stroke. Choose invariances such as rotation or scaling to suit your data.
The complete county/district layers, including holes and disconnected components, took 24–25% less time than reusable prepared Clipper2 C# with preparation plus one full traversal. Warm prepared traversals were 1.44–1.49× faster and allocated 0 B on the measured thread. Preparation alone was slower. At 1,024 generated rings per operand, warm comparisons took 18–34% longer than Clipper (46–97% longer including preparation). Performance depends on component count, geometry and input reuse; there is no universal speed advantage. See the prepared-region assessment.
Earlier single-walk application measurements remain separate: Complete building-evaluator times were effectively tied, with 15.8% fewer allocated bytes; its prepared intersection batch was 12% slower than Clipper2. The complete evaluator was 3.05× faster than the configured NetTopologySuite backend. A nine-ring county own-area batch was about 1.3× faster than Clipper2.
Selected synthetic shapes show larger FilledArea wins over Clipper2: about
26× on a simple spiky star and 17–45× on a repeatedly traced square.
A dense integer crossing grid shows a 1.65–3.03× advantage. These compare one
area result with preloaded Clipper2 C# producing filled contours.
See the scenario table, reproduction commands and limits before choosing for your workload. A trusted simple ring needs only a shoelace sum; the star result does not establish an advantage for that narrower operation. Reducing the repeated square to a minimal path before clipping was not measured.
PolylineAreafilled-area methods accept one ordered walk per operand, with at least three vertices after consecutive duplicates and an optional closing duplicate are removed. Null, empty and too-short inputs throw; valid collinear walks can have zero area.- Coordinates must be finite and have magnitude at most
1e100. Keep input unchanged during a call. No method silently repairs invalid input. - Calculations use
double. Exact geometric decisions do not make intersection positions or final areas exact. Tiny features near very large coordinates can lose precision. IoU/Jaccard arenullwhen the union is not positive. RegionAreaaccepts prepared collections of rings, including empty regions. Each nonempty collection's rings follow the same vertex and coordinate limits. Role-based factories orient ordinary holes; rawFromRingsinput requires deliberate orientation under NonZero.- These methods do not calculate geodesic areas or polygon offsets.
Read the area guide for fill rules, examples and contracts, or the transformation guide for normalization and alignment.
- Runnable quick start
- Install and run the coverage example from NuGet packages
- Convert NetTopologySuite Polygon/MultiPolygon input
- Optional contour output
- Measure change after contour simplification
- Measure how much of a region is covered
- Evaluate and visually review polygon annotations
- Documentation index
- Performance and memory
- Build, tests and contributing
- Alpha.3 changes and delivery
- Alpha.2 polygon-role factories and delivery
- Alpha.1 release notes and unlisting status
Original code is MIT licensed. Dependencies, data and algorithm attribution are documented in third-party notices.
