Skip to content

Repository files navigation

PolylineKit

.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.

Applications

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
Check a contour after simplification Changed filled area and overlays of the original and simplified boundary Simplification change

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.

Building review with selected prediction, reference outline, IoU and area errors

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.

Add to your application

Choose PolylineKit.Core for numeric areas, intersection and coverage. Version 0.1.0-alpha.1 is an alpha candidate; it has not been published on NuGet.org. The package guide produces and verifies a local feed:

pwsh -File scripts/verify-packages.ps1
dotnet add MyApp/MyApp.csproj package PolylineKit.Core --version 0.1.0-alpha.1 --source /absolute/path/to/feed

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.csproj

PolylineKit.Core includes area methods, normalization, scaling, resampling and alignment. All use the PolylineKit namespace. Alpha status means the package is ready for evaluation, 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.

Measure zone coverage

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.

Choose a method

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)
Regions with holes or disconnected components Prepare rings with PreparedRegion.FromRings; 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

The prepared-region API is available in the current source; earlier verified alpha binaries predate it. Each ring closes independently, with explicit fill semantics and no automatic shell/hole orientation.

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.

Performance by scenario

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.

Input and numerical limits

  • PolylineArea filled-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 are null when the union is not positive.
  • RegionArea accepts prepared collections of rings, including empty regions. Each nonempty collection's rings follow the same vertex and coordinate limits; NonZero holes require deliberate orientation.
  • 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.

Examples and documentation

Original code is MIT licensed. Dependencies, data and algorithm attribution are documented in third-party notices.

About

C# tools for filled areas, region overlap, polyline comparison, normalization and alignment. .NET Standard 2.0 and .NET 10.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages