triwild: simplify the input curves before the arrangement - #960
Open
danielepanozzo wants to merge 1 commit into
Open
triwild: simplify the input curves before the arrangement#960danielepanozzo wants to merge 1 commit into
danielepanozzo wants to merge 1 commit into
Conversation
tetwild coarsens its input surface before inserting it -- shortest-edge collapse
inside half the envelope -- and hands the arrangement the simplified version.
triwild had no equivalent: it fed the raw curves straight to
embed_seg_in_tri_mesh. That is expensive twice over, because every input vertex
becomes a constrained vertex of the exact arrangement and then a surface vertex
of the initial mesh, and inputs are routinely far finer than the target edge
length. Measured against the default length_rel, 92% of puzzle.obj's segments
and 100% of every other bundled 2D model's are shorter than the target.
New wmtk::utils::simplify_segments is the 1D counterpart. It is a plain routine
on (V, E) rather than a mesh subclass -- wmtk has no edge-mesh class and a curve
network does not need one. It repeatedly collapses the shortest segment whose
removal keeps every affected segment inside the envelope, with two deliberate
differences from the 3D version:
- Vertices of valence != 2 are frozen -- open endpoints, junctions, and (they end
up with valence >= 4) anything shared between two input curves. But a segment
with one frozen endpoint still collapses, onto that endpoint's exact position,
so junction geometry is preserved bit-exactly. Rejecting outright, as the 3D
code does, would leave a ring of short segments around every junction.
- The queue is keyed on (squared length, segment id). The id is not decoration:
a uniformly sampled curve is full of exactly-equal lengths, and a partial key
there would make the result depend on heap order.
Wiring it in required reordering triwild's front end to tetwild's order, since
the tolerance has to exist before the arrangement runs. Two consequences worth
knowing:
- The bounding box now comes from the input curves. It used to come from the
arrangement output, which includes the background grid and is about 13%
larger, so eps and the target edge length shrink accordingly.
- The envelope is built at eps/2 around the *original* input and shared by the
simplification and the optimizer, as tetwild does with surf_mesh.m_envelope.
init_mesh takes the envelope source explicitly for that reason: the optimizer
has to stay near what the user gave us, not near the simplified version.
Its existing "all surface edges are inside the envelope" check thereby stops
being a tautology and becomes a real test of the simplification.
The bbox tagging in init_mesh now derives the domain box from the arrangement
output instead of m_params, which is the box of the input and no longer the same
thing -- otherwise interior edges get mistaken for boundary ones.
Together the envelope tightens by roughly 2.3x. Both integration configs still
pass with throw_on_fail.
Measured, serial, on a raw dense contour with input dedup disabled:
10k segments, length_rel 5e-3: 2.327s -> 0.789s (2.9x), arrangement
#V 10270 -> 585
100k segments, length_rel 5e-2: 5.114s -> 2.320s (2.2x), arrangement
#V 100270 -> 587
On the bundled models it is roughly neutral end to end -- puzzle 438 -> 110
input vertices and the arrangement 657 -> 329, but at 0.4s total the arrangement
was never the bottleneck. The win is on dense inputs, which is what traced
contours and image boundaries are. Note that remove_duplicate_eps already
removes much of the redundancy on inputs sampled below that tolerance, so the
two together matter more than either alone.
tetwild and simwild are untouched: 23/23 output files still byte-identical
across the 15 deterministic non-triwild integration configs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #959. Adds the phase triwild was missing: the 2D counterpart of tetwild's input simplification.
Why
tetwild coarsens its input surface before inserting it — shortest-edge collapse inside half the envelope — and hands the arrangement the simplified version. triwild fed the raw curves straight to
embed_seg_in_tri_mesh. That costs twice over: every input vertex becomes a constrained vertex of the exact arrangement, and then a surface vertex of the initial mesh.And the inputs really are over-refined. Measured against the default
length_relof 5e-2:The simplifier
wmtk::utils::simplify_segmentsis a plain routine on (V, E), not a mesh subclass — wmtk has no edge-mesh class and a curve network does not need one. It repeatedly collapses the shortest segment whose removal keeps every affected segment inside the envelope, with two deliberate differences from the 3D version:Reordering, and what changes
The tolerance has to exist before the arrangement runs, so triwild's front end moves to tetwild's order. Two consequences:
epsand the target edge length shrink accordingly.surf_mesh.m_envelope.init_meshtakes the envelope source explicitly for that reason — the optimizer has to stay near what the user gave us, not near the simplified version. Its existing "all surface edges are inside the envelope" check thereby stops being a tautology and becomes a real test of the simplification.Together the envelope tightens by roughly 2.3x. Both integration configs still pass with
throw_on_fail.The bbox tagging in
init_meshnow derives the domain box from the arrangement output instead ofm_params, which is the box of the input and no longer the same thing — otherwise interior edges get mistaken for boundary ones (which is exactly what happened, and how the bug was found).Measured
Serial, raw dense contour, input dedup disabled so the simplification is doing the work:
On the bundled models it is roughly neutral end to end — puzzle goes 438 -> 110 input vertices and the arrangement 657 -> 329, but at 0.4s total the arrangement was never the bottleneck. The win is on dense inputs, which is what traced contours and image boundaries are.
Worth knowing:
remove_duplicate_epsalready removes much of the redundancy on inputs sampled below that tolerance. On my first measurement it had quietly done most of the coarsening, and the simplification looked like a 24% regression; with it disabled the 2.9x above is the honest number. The two together matter more than either alone.Verification
wmtk_test_triwildnow 1751 assertions in 14 cases).skip_simplifyjoins the shared option set, leaving five keys that genuinely have no 2D meaning:use_sample_envelope,use_legacy_code,allow_surface_swap,check_surface_topology,DEBUG_euler.🤖 Generated with Claude Code