Skip to content

Claude/awesome pascal eex2py - #60

Closed
HarmenGrosseDeters wants to merge 54 commits into
wo80:masterfrom
HarmenGrosseDeters:claude/awesome-pascal-eex2py
Closed

HarmenGrosseDeters wants to merge 54 commits into
wo80:masterfrom
HarmenGrosseDeters:claude/awesome-pascal-eex2py

Conversation

@HarmenGrosseDeters

Copy link
Copy Markdown

No description provided.

claude and others added 30 commits October 23, 2026 21:20
Analysis of the library's behavior on topologically dirty input and
floating point edge cases (docs/Robustheitsanalyse.md), backed by tests:

- Red tests (category "KnownBug", 20 tests) documenting confirmed bugs:
  crossing segments producing invalid meshes, exceptions or a stack
  overflow; RobustPredicates.Default not being thread-safe; Dwyer stack
  overflow on identical points; SimpleSmoother failing far from the
  origin; missing NaN/Infinity validation; vertex id heuristic; Vertex
  Equals/GetHashCode contract; SweepLine not exactly Delaunay.
- Green regression tests for degenerate input that is handled correctly.
- MeshAssert helpers (consistency, inverted triangles, segment coverage,
  area, termination).
- Scenarios that crash or hang the process run in a child process via a
  custom entry point of the test assembly.
- CI runs KnownBug tests separately with continue-on-error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- Add SegmentNoder: split crossing segments in advance (exact crossing
  detection), merge new vertices with existing ones within a small
  tolerance and snap vertices lying almost on a segment onto it. Enabled
  by default via ConstraintOptions.ResolveIntersections.
- ConstraintMesher: replace tail recursion in ScoutSegment/ConstrainedEdge
  by loops, guard against non-terminating insertion and check orientation
  after inserting intersection vertices (never return an invalid mesh).
- RobustPredicates: thread-local workspace, so instances are thread-safe.
- QualityMesher: choose the point location start edge for aCute locations
  by orientation test (inverted triangles) and fall back to the
  circumcenter if the aCute location hits an existing vertex.
- Dwyer/SweepLine: return an empty mesh for identical input points.
- SweepLine: restore the Delaunay property by exact edge flips.
- SimpleSmoother: compute centroids relative to the generator.
- Mesh: reject NaN/infinite coordinates, check user ids for uniqueness.
- Vertex: hash code consistent with Point.Equals.

All former KnownBug tests pass and are now regular regression tests;
add tests for noding and quality refinement of noded input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
ConstraintOptions.CanSplitSegment decides, based on the subsegment (e.g.
its label), whether refinement may insert vertices on a segment. Locked
segments (e.g. breaklines of a terrain model) keep their input vertices,
except for intersections with other segments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Findings from triangulating the 2156 cases of the GEOS XML test suite
(overlay/noding failures reported against JTS, GEOS, PostGIS, QGIS and
Shapely) as PSLGs:

- SegmentNoder: merge vertices closer than the snap tolerance (exact
  duplicates are kept), don't snap vertices that are within the
  tolerance of a segment endpoint, avoid duplicate new segments. Tiny
  rings with coordinate noise made the noder split segments forever.
- SegmentNoder: if noding doesn't converge (many nearly coincident
  segments), retry with a larger snap tolerance (up to 2^-32 relative,
  like the GEOS snapping noder), otherwise throw instead of running
  forever.
- QualityMesher: if splitting a subsegment would create a flat or
  inverted triangle (exact test) or the split point coincides with an
  endpoint, don't split it and exclude it from further splitting
  instead of silently inverting triangles or throwing "Ran out of
  precision". Leave triangles that can't be refined instead of throwing
  "new vertex at circumcenter". Check orientation after refinement.
- RobustPredicates: guard reads past the end of an expansion in
  FastExpansionSumZeroElim (IndexOutOfRangeException for infinite input).
- Tests: regression tests for the above; explicit corpus tests (category
  "Corpus") for the GEOS test suite and for NRW open data (ALKIS parcels,
  DGM1), each case in a child process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- ConstraintOptions.SnapTolerance: minimum noder tolerance in coordinate units
  (e.g. 0.01 = 1 cm) merges nearly coincident lines instead of meshing slivers
- SegmentNoder: don't recreate already split segments from their pieces
  (snap cycle with large tolerance)
- Corpus harness: snap tolerance support, even tile sampling, fix coverage
  check for subsegments beyond segment endpoints, treat rejected non-finite
  input as skipped
- Tests for snap tolerance and snap cycle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…tion cycles

- If the aCute location doesn't destroy the bad triangle (it may lie outside the
  circumcircle), insert the circumcenter instead. Before, the triangle was dropped
  from the queue: maximum area violated, many triangles below the minimum angle
  (ALKIS tile at 30 degrees: 618 instead of 9 bad triangles).
- Accept an aCute location only strictly inside the circumcircle and not on a
  vertex of the triangle.
- Relocate each vertex at most once. Region area constraints made aCute move the
  same vertex back and forth (upstream: exception, then cycling until the
  Steiner point limit with a coarse result).
- Characterization suite: scenario catalog (point sets, polygons, real ALKIS
  tiles x algorithms, quality, conforming, segment splitting, locked segments,
  area constraints, refine, smooth), invariant tests (consistency, orientation,
  constrained/conforming Delaunay, area, segments, locked segments, angle and
  area guarantees) and golden fingerprints of the exact output.
- Corpus harness: configurable angles, timing and quality statistics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…segment

A crossing close to a segment endpoint was merged into that endpoint and the
segment was 'split' there. Nothing changed, but the segment was retired, so
snapping a nearly collinear vertex into it was blocked. Two segments with an
angle of 0.0002 degrees remained, and quality refinement spent the whole
Steiner point budget in that wedge (ALKIS Herne, parcels + buildings + land
use, 1 cm snapping: tile with 203,175 triangles, 154,870 below 20 degrees ->
24,355 triangles, 222 below 20 degrees). The useless splits also used up the
noding iterations: stacked squares with offsets of 1e-6 now converge.

Also: BenchmarkDotNet project on the characterization scenarios, corpus
summary with total bad triangles and the worst cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…chmark baseline

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…d vertices

Behavior-neutral (all golden fingerprints unchanged):
- per-call work arrays of NewLocation are fields, cleared where they used to
  be allocated
- GetNeighborsVertex only visits the neighbor across the matching edge
- DoSmoothing computes vertex stars only for vertices that can be relocated

ALKIS tile, 30 degrees: 810 -> 617 ms, 104 -> 73 MB allocated.
Benchmarks: TRIANGLE_BENCH_SCENARIOS selects scenarios.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Behavior-neutral (golden fingerprints unchanged). ALKIS tile, 30 degrees:
73 -> 60 MB allocated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Instead of inserting the vertex, flipping and undoing everything when a
subsegment is encroached, the cavity is determined with the same incircle
tests and its boundary subsegments are checked in the same order. For large
cavities (> 64 triangles) the search stops at the first encroached
subsegments. Needle-shaped regions (GEOS TestRelateAA#74, 80,000 points):
79 s -> 2.1 s.

Golden fingerprints of 7 scenarios change: inserting and undoing left stale
bad-triangle entries in the queue, which could come back to life when a vertex
with the same coordinates was inserted later (vertices are compared by
coordinates). Without them, the processing order differs slightly; vertex
counts differ by 0-3, bad triangle counts and minimum angles are identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
If the new vertex encroached upon or lay beyond a subsegment which must not be
split (SegmentSplitting 1/2, CanSplitSegment), the bad triangle was dropped
for good (upstream issue wo80#9: a 10x20 rectangle with maximum area 1.73 gave 8
triangles). Now the vertex is inserted anyway if it encroaches only upon such
subsegments, and a too large triangle is split at its centroid if the vertex
lies beyond one. Subsegments that can't be split because of the precision
limit are excluded.

Tests reproducing the bug reports of the upstream issue tracker, overview in
the docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…etry, DCEL

IO: segment endpoints written by list index (IDs of contour polygons were all
0), region areas written, region areas in scientific notation read, inline
comments, attribute column of 6-node elements, invariant integer formatting,
case-insensitive extensions, FileProcessor with mesh-only formats, consistent
node numbering with Jettison, MFEM leaves the caller's stream open.
Log is thread-safe. Coordinates beyond 1e70 (where the exact predicates
overflow) are rejected.
Mesh: unique triangle ids after Renumber (EdgeIterator skipped edges),
Renumber(CuthillMcKee) with duplicate vertices, vertices reused from another
mesh are reset, Refine of a point set keeps the convex hull and uses the mesh
configuration, NumberOfEdges of an empty mesh, an invalid angle option no
longer disables all quality constraints.
TrianglePool: Contains off-by-one, Sample covers all slots and is bounded,
Clear releases all blocks.
Geometry: interior point of contours (hole points) with exact inside test and
clearance (hole points landed on the hole boundary with UTM coordinates, thin
contours threw), Rectangle copy constructor and Intersects, Point.CompareTo
null. VertexCirculator: nested enumeration, vertices not in the mesh.
DCEL: boundary edges at pinch vertices, Face.Empty no longer references a mesh.

Tests from the class-by-class review (Review A, D, F).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…noi, smoothing, tools

Quality: MaximumAngle alone is enforced, NewLocation work arrays sized for
both wedge methods (IndexOutOfRange after refining with a maximum angle),
triangles after vertex deletion are always tested (too large triangles with
SegmentSplitting 1/2), VariableArea on point-set meshes, BadTriQueue clamps
extreme keys.
Noding: unique ids for intersection vertices (user ids were renumbered),
non-zero label wins for coinciding pieces, merged vertices keep their label,
hole/region points inside collapsed contours are dropped (SnapTolerance larger
than a hole emptied the mesh), sweep along the axis with less overlap (long
parallel segments were quadratic) with the same pair order.
Regions: last region point wins (values from the region, not the seed).
Converter: ToDCEL at pinch vertices, ToMesh with arbitrary vertex ids.
Voronoi: unique half-edge ids, StandardVoronoi doesn't modify the box, legacy
diagrams skip duplicate vertices, SimpleVoronoi bounds include the mesh,
BoundedVoronoi documents the conforming requirement.
SimpleSmoother: separate pool per run (reusing the smoother corrupted the
previous mesh), vertices restored on cancellation, stop criterion after two
real iterations. VoronoiFactory resets pooled objects.
Tools: Reverse Cuthill-McKee neighbor sorting (1-based leftovers),
TriangleQuadTree exact point-in-triangle test, PolygonValidator null points
and segment endpoints, relative tolerance in IsPointOnSegment, BoxRay-
Intersection without direction.

Golden fingerprints of three SegmentSplitting=1 scenarios change (fewer bad
triangles). Tests from the review (B, C, E).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- ConstraintOptions.GridSize: snap rounding of the input to the output grid
  (SegmentNoder.SnapRound, iterated snap rounding with hot pixels), Steiner
  points rounded to the grid during refinement (no aCute relocation, no
  angle refinement of triangles below two grid cells, shell rule with grid
  tolerance), remaining vertices moved by GridSnapper.SnapVertices where no
  triangle flips. Rounding the output no longer flips triangles.
- ConstraintOptions.WedgeTolerance: merge segments running along another
  segment at a small angle (thin wedges) without raising the snap tolerance.
- Tools.TerrainSimplifier: greedy insertion TIN simplification with a
  vertical error bound (and break lines), incremental with a max-heap.
- Corpus harness: grid, wedge tolerance and flipped-by-rounding statistics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- Split vertices are projected onto the input segment before rounding to
  the grid, so deviations don't add up over splitting levels.
- Wedge merging checks the distance to the original input segment, so
  chains of wedges can't drag a segment further than the tolerance.
- Corpus harness: bad triangles after rounding to the output grid.
- Docs: grid, wedge and terrain simplification results on ALKIS/DGM1 Herne.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
With a grid, subsegments where no grid point is a valid split are common.
Triangles whose circumcenter encroached upon such a subsegment were dropped
for good, leaving large bad triangles (edges up to 40 m). The vertex is now
inserted anyway, as for locked segments (the grid floor prevents a
cascade). ALKIS Herne, grid 1 cm + wedge 3 cm: bad triangles after rounding
5.3 % / 6.0 % -> 2.2 % / 2.2 % at 20 / 30 degrees.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- Cocircular points (grid data) make the Delaunay triangulation ambiguous;
  errors are now evaluated over all triangles of each cocircular cluster,
  and triangles whose cluster may have changed are re-evaluated. The final
  check against a triangulation built from scratch finds no violations, so
  it runs once (DGM1, 5 cm: 57 s -> 28 s).
- Fix: points added to an existing bucket could raise its error without
  queueing it (bound violated in rare cases).
- Errors equal to the bound up to roundoff are accepted.
- Docs: grid mode results after the insert-anyway fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Bad triangles are classified as at a small input angle, below the grid
floor, in a narrow strip (another segment closer than 5 cm), at a segment
or interior. ALKIS Herne, grid 1 cm + wedge 3 cm: at most 0.19 % / 0.30 %
of all triangles are possibly avoidable; most lie in 2-5 cm strips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
The aCute relocation was computed and then discarded in grid mode (24 % of
the time). SnapVertices builds vertex stars only for vertices off the grid.
ALKIS tile, q30, grid 1 cm + wedge 3 cm: 1155 -> 846 ms, same mesh size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- Triangle stores its vertices, neighbors and subsegments as fields with
  ref-returning accessors (same syntax as the arrays before). A triangle is
  now one heap object instead of four; the garbage collection time of a
  quality mesh is dominated by the number of live objects.
- The temporary arrays of InCircleAdapt and CounterClockwiseAdapt (about 40
  per call) moved to the thread-local workspace.
- aCute wedge intersection: rotations computed once per call.
Behavior unchanged (golden fingerprints identical). ALKIS tile, q30:
snap 1 cm -5 %, grid 1 cm + wedge 3 cm -13 % (GC pauses 266 -> 166 ms,
allocations 84 -> 56 MB).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Fewer heap objects per subsegment; about 5 % fewer allocations, no
measurable time difference on the ALKIS tiles. Behavior unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
The library now also targets net8.0. There, the vertices, neighbors and
subsegments of triangles and subsegments are [InlineArray] buffers (array
indexing, no separate heap objects). netstandard2.0 keeps plain arrays.
This replaces the ref-returning accessors, which were slower than arrays
for small, CPU-bound meshes. CounterClockwiseAdapt keeps its local arrays
(it is called very often for collinear input).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…ex2py

Add robustness analysis and red test suite for known bugs
Taken from the SoftSmile-Inc fork: with invalid topology the walk around
a vertex went back and forth between two triangles until the work array
was full. Never triggers in valid meshes (a free vertex has at least
three incident triangles).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- 1000 hard orient2d and 1000 hard incircle cases (Nanevski et al.) with
  the expected sign: all correct.
- Ruppert's maple leaf (Canadian flag) at 0/20/30/33 degrees.
- Synthetic edge cases: grids (cocircular, deterministic, Delaunay with all
  three triangulators, also at UTM coordinates), grid with diagonal
  constraints and quality, concentric circles with and without hole
  (constraints separate inside and outside), spiky stars with tip angles
  down to 1e-15 rad at three scales.
- Corpus readers for Natural Earth coastlines (LineStrings) and the test
  inputs of artem-ogre/CDT (TRIANGLE_NATURAL_EARTH, TRIANGLE_CDT_INPUTS).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
- SegmentNoder.Node: if noding doesn't converge with any tolerance, snap
  round to the largest tolerance instead of throwing (issue-211 of the CDT
  test inputs: long edges crossing a polyline at tiny angles).
- SnapRound routes segments through existing hot pixels before computing
  new intersections (the number of vertices exploded otherwise) and allows
  100 iterations. Results of inputs that converged before are unchanged.
- Corpus harness: spatial index for the coverage check and the strip
  classification (quadratic before: timeout on Natural Earth 10m), strip
  width relative to the extent, Steiner limit detected like the library.
- Docs: performance results (interleaved A/B), standard CDT benchmarks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
claude and others added 24 commits October 25, 2026 04:58
Triangles and subsegments can store vertices, neighbors and subsegments
as plain fields accessed through ref indexers, so .NET Framework 4.8 gets
one heap object per triangle instead of four. Default stays InlineArray
on net8.0 and arrays on netstandard2.0. Benchmarks also target net48.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
QualityOptions.ReduceSteinerPoints deletes free Steiner points whose
retriangulated cavity meets all quality constraints, or collapses an edge
to a free neighbor when that makes the point removable (exactly undoable
flips otherwise). QualityOptions.SmoothingSteps moves free Steiner points
towards the optimal Delaunay triangulation position (grid-rounded) and
keeps only moves that improve the changed triangles. Input vertices,
segments and segment vertices are never changed.

Herne tile 30 deg with 1 cm grid: 17-18% fewer triangles and fewer bad
triangles. Tests cover all catalog scenarios with full invariants.
Docs: research summary and .NET 10 layout benchmark (Fields slower).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
TRIANGLE_CORPUS_REDUCE, TRIANGLE_CORPUS_SMOOTH and TRIANGLE_CORPUS_CONFORMING
set the corresponding mesher options for the external corpus tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…asurements

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…ly too large

aCute tried to relocate a vertex of every bad triangle whose off-center lies
beyond the circumcenter, which includes all triangles that are bad only by
area. Relocation improves angles, so it rarely helped there and cost ~80% of
the time (unit square, area 0.001: 148 -> 14 ms, same triangle count).
Only regions/q25-variable-area changes (463 -> 464 vertices).

The encroachment pre-check now returns early when no boundary subsegment of
the cavity is encroached, before building the boundary map (legacy area
refinement back to upstream speed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
… docs

A hole contour without interior (all points on a line, fewer than three
distinct points) no longer throws in Polygon.Add: it removes nothing, so
its edges are added as segments (earcut fixtures water-huge, issue83).

CorpusTest.PublicFixtures runs the test fixtures of mapbox/earcut,
mapbox/delaunator and jhasse/poly2tri (TRIANGLE_PUBLIC_FIXTURES): 273 runs,
all valid. Docs: comparison with upstream Triangle.NET, delaunator-sharp
and BurstTriangulator (Delaunay grid/random, Lake Superior refinement).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…al Earth polygons

Prototype (Triangle.Tests/Prototype): the divide-and-conquer triangulation on
arrays (vertices and encoded neighbors per triangle) instead of objects, same
result. 1.05-1.8x faster on net10 with InlineArray, 1.2-2.4x with the array
layout of net48, up to 3.2x with a small gen0 budget.

Corpus tests CgalData (Polygon_repair, Straight_skeleton_2) and
NaturalEarthPolygons (10m layers as a whole and per feature). Docs: results
for ALKIS Muenster, Koeln, Hoexter, CGAL, Natural Earth polygons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Triangles get a mark field; the cavity search uses a per-mesh stamp
(Mesh.NextMark) instead of a HashSet, whose Clear cost grows with the largest
cavity seen. Results identical (all golden fingerprints unchanged);
Herne tile 30 deg with grid -6%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…the first relocatable vertex

72% of the half-plane intersections in the wedge computation leave the
polygon unchanged and 2% empty it; these are detected by the signs of the
vertices (with a margin that rules out the tolerance cases of the general
code) and give the same result. DoSmoothing returns as soon as the origin
(or destination) can be relocated, since that result was preferred anyway.
Results identical (all golden fingerprints unchanged); Herne tile 30 deg -4%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
PredicatesBenchmark measures the orientation and incircle tests through the
interface, directly and as an experimental fast path (same arithmetic and
bounds, checked against the library). Fast path 4-8 ns, exact stages on grid
data 140 ns; the predicates cost about 2% of a quality mesh, no worthwhile
optimization (documented).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Triangle is now a handle (pool + index) to the data of the triangle in the
arrays of the TrianglePool: vertices, neighbors (encoded index << 2 | orient),
subsegments, ids, labels, area constraints, infection and search marks. The
outer space triangle is position 0 of the pool. Otri, the mesh primitives and
the algorithms are unchanged except for the few places that wrote neighbor
references directly or compared triangles with null (now errors, CS8073).

Public API: Triangle is a readonly struct implementing ITriangle; two handles
are equal if they refer to the same triangle (ITriangle values from a mesh
must be compared with Equals, not ==). TrianglePool keeps its API, so reusing
a pool across meshes (Configuration) reuses the arrays.

All tests pass, all golden fingerprints unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…outer space triangle, pre-sized pool

Otri stores the pool and index directly (16 instead of 24 bytes); the 73
checks against the outer space triangle compare the index with 0 instead of
reading the id from the pool; the pool is sized for 2n triangles of n input
points. All tests pass, golden fingerprints unchanged.

BenchmarkDotNet against the object layouts (net10): large Herne tiles equal
(snap) or 9% faster (grid) than InlineArray and 10-13% faster than the array
layout of net48; small meshes 10-16% slower than InlineArray.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
…mple, viewer

IndexBuffer.Create takes any IEnumerable<T> of ITriangle (mesh.Triangles is a
collection of Triangle values; covariance doesn't apply to value types).
Example7 casts the quad tree result instead of 'as'; the viewer compares the
handle with default instead of null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Debug-Tests dauern rund 15 Minuten, Release rund 3,5 Minuten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Debug-Tests dauern rund 15 Minuten, Release rund 3,5 Minuten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Ganze Natural-Earth-Ebenen (z. B. US-Landkreise, 148.000 Punkte) brauchen mehr
als die festen 100.000 Steiner-Punkte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Öffentliche Fixtures von earcut, delaunator und poly2tri, an denen das
ursprüngliche Triangle.NET scheitert (Exception, Endlosschleife,
inkonsistentes Netz), jetzt fest im Repo und im CI: je ohne Qualität, 20°
und 30°. Dazu die fast deckungsgleiche Hin-und-zurück-Grenze aus den
Natural-Earth-US-Landkreisen (Steiner-Limit greift; mit Snap-Toleranz
keine schlechten Dreiecke).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Bei sehr kleinen oder sehr großen Koordinaten werden die Produkte subnormal
bzw. unendlich; dann gelten Shewchuks Fehlerschranken nicht und das
Vorzeichen war falsch (InCircle ab 1e-100 bzw. 1e100 etwa die Hälfte der
fast kozirkulären Fälle, Orient ab 1e-160) oder NaN.

- Orient: Absicherung nur in den Zweigen, in denen Unterlauf das Vorzeichen
  ändern kann (Produkt null, Filter bei subnormaler Summe).
- InCircle: Filter nur, wenn alle Differenzen 0 oder in [2^-200, 2^200]
  liegen (ein subnormales Produkt mal großem Lift kann die Schranke sonst
  überschreiten).
- Adaptive Stufen nur für Koordinaten in [2^-100, 2^100] (oder 0), sonst
  exaktes Vorzeichen mit BigInteger.

Idee aus geometry3Sharp (ExactPredicates). Normale Daten: Orient
unverändert, InCircle etwa 3 ns langsamer pro Aufruf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bnBWGQoVJCM7Rtx2a6Z86
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants