Skip to content

fix: a failed end tangent reads SmVector3d's UNDEF default, as upstream (#521) - #704

Merged
bherbruck merged 4 commits into
tangency-labs:mainfrom
denelson1-dot:fix/521-vertex-undef-tangent
Oct 24, 2026
Merged

bherbruck merged 4 commits into
tangency-labs:mainfrom
denelson1-dot:fix/521-vertex-undef-tangent

Conversation

@denelson1-dot

@denelson1-dot denelson1-dot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #521. Standalone on main (no other PR stacked).

I checked every site #521 lists against the port at c9c5979. The issue was written from the fork. At c9c5979 none of these sites reads uninitialised memory:

  • SmVector3d, SmPoint3d and SmPoint2d default to SM_UNDEF_DOUBLE (SmVector3d.h:56-58).
  • SmExtent1d defaults to an empty interval.
  • AngleBetween writes 0 before it can fail.

The port matches upstream at every site but one: SmVertex::AreEdgesTangent / IsTopologicalVertex. This PR fixes that site and records the others.

Fixed: AreEdgesTangent / IsTopologicalVertex after a failed GetEndTangent (WR-256)

Upstream. The ignored failure leaves the caller's tangent as it was. That is the SmVector3d default, or in IsTopologicalVertex the line vector IsLine wrote. AngleBetween sees a zero-length vector and reports 0, so both functions answer TRUE. UB-106 had called that tangent uninitialised; a correction line now points it to WR-256. Only the curveless edge is really UB.

What changes:

  • Port. topology::vertex returned InvalidInput here. It now reads Vec3::UNDEF, as upstream does.
  • Shim. The topology_vertex shim guard (ok_tangent, ok_topological) also refused these inputs, and called GetEndTangent to decide. It now refuses only the real crash: a null curve whose tangent upstream would read.

Reachability. No input the topology.vertex harness can build makes GetEndTangent fail, so no existing case changes value:

  • The harness builds only line and B-spline edges.
  • SmBSplineCurve::Evaluate clamps the parameter.
  • N_BasisFindSpan skips end spans shorter than 1e-12, so sm_BasisDerivs never sees the tiny knot difference it rejects.
  • A NaN parameter gives NaN, not an error.

An SmCompositeCurve edge always gets there: that class keeps SmCurve::Evaluate, which returns SM_ERR. The new unit test failed_end_tangent_reads_the_default_vector builds that case.

New fixtures. fixtures/topology.vertex/end_tangents.json adds 70 compared cases around this path:

  • zero end tangents (a B-spline collapsed to a point), which do reach the AngleBetween failure
  • 1e-30 end spans with matching parameters, which exercise the end-span skip

To be clear: these cases agree with or without the port change. They pin the neighbouring behaviour; the unit test is what covers the changed lines.

Recorded, no code change (plan/findings.md, "#521 audit")

  • SmLoop::GetUVCornerBoundaries: "keeps the previous edgeuse's curve" doesn't hold at c9c5979. GetOrCreateUVTrimCurve sets its out-pointer before it builds (SmEdgeuse.cpp:4275), so a failed build leaves NULL and the drop-point branch runs. Stale UV ends after a failed EvaluatePoint / DropPoint are ported as they are.
  • SmEdge::ReverseOrientation: the default interval after a failed ReverseParameterization is already ported and documented.
  • SmLoop::ContainsUVPoint: the four ignored evaluations read UNDEF points or the previous step's derivatives. The port returns their error (its documented deviation, reached only by a failing surface evaluation, a NaN parameter or a degenerate span). The doc wording there said "uninitialised"; this PR corrects it.
  • Edge-use loop-gap / dog-leg checks: GetStartPointUVLoopGap is faithful. GetEndPointUVLoopGap and HasDogLegNearPole are unreachable and not ported.
  • sm_CalcDistAndDev: faithful, with one open difference noted for a follow-up. The port propagates an evaluate_normal error that upstream ignores. That matters only for a zero-radius sphere or a SrfInVolume whose evaluation fails.
  • SmFaceProps::SetProps_Stage3: no unset reads. A failed Stage 3 leaves the stage at 3a, which the port copies.

The fork's status propagation isn't ported. As with #465 and #468, the port keeps c9c5979's results because difftest compares against them.

Preserved invariants

No tolerance loosened, no case dropped, generators unchanged. The guard change only lets through inputs the C++ handles without UB. topology.vertex stays green.

Difftest

PASS topology.vertex cases=5578 passed=5578 failed=0 deferred=0 max_rel_err=0e0 with_values=5576 no_values=2 vacuous=0
SENSITIVE topology.vertex perturbed=5578 detected=5578 unperturbed=0 deferred=0
PASS topology.vertex cases=7562 passed=7562 failed=0 deferred=0 max_rel_err=0e0 with_values=7560 no_values=2 vacuous=0   (--seeds 2000)
FUZZ-PASS topology.vertex cases=2000 rejected=244 malformed=391 ran=1365 differs=0 crashes=0 panics=0 timeouts=0
--green: 260 unit(s), 206 passed, 54 partial, 0 failed

Verified on head fb0aec152 (x86-64): cargo fmt --all --check (+ rustfmt on the touched FFI unit file), clippy -D warnings (plain and --features difftest/cpp), cargo test -p solidcore (2080 passed), the difftest runner tests, the wasm32 check, harness/plan/units.py check, and a clean git merge-tree against main. --green --jobs 4 above.

Since then: merged main (no conflicts locally; #703 took WR-255, so this PR's entry is now WR-256, renumbered in its own commit). Re-checked on f3df18881: fmt, units.py check, clippy, the vertex unit tests and topology.vertex 5578/5578.

🤖 Generated with Claude Code

…am (tangency-labs#521)

At c9c5979 SmVector3d defaults to SM_UNDEF_DOUBLE, so the tangent a failed
GetEndTangent leaves in SmVertex::AreEdgesTangent / IsTopologicalVertex is
defined: AngleBetween reports 0 and both answer TRUE. The port returned
InvalidInput there and the topology.vertex shim refused the input. Read
Vec3::UNDEF instead, refuse only a null curve in the shim, and record WR-255
(correcting UB-106) plus the per-site tangency-labs#521 audit in findings.

70 compared cases in fixtures/topology.vertex/end_tangents.json (zero end
tangents, 1e-30 end spans) and a unit test on a composite-curve edge, whose
evaluation always fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bherbruck
bherbruck added this pull request to the merge queue Oct 24, 2026
@bherbruck
bherbruck removed this pull request from the merge queue due to a manual request Oct 24, 2026
@bherbruck
bherbruck added this pull request to the merge queue Oct 24, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 24, 2026
denelson1-dot and others added 2 commits October 23, 2026 22:17
…on main)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bherbruck
bherbruck added this pull request to the merge queue Oct 24, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 24, 2026
@bherbruck
bherbruck added this pull request to the merge queue Oct 24, 2026
@fkirill

fkirill commented Oct 24, 2026 •

Copy link
Copy Markdown

Update after #704 landed: #700 is being narrowed to the remaining topology/healing failure paths, while retaining #704’s tangent behavior, tests and fixture coverage.

Correction to my earlier comment: #700’s 20 constant-segment fixtures exercise failed angle calculations / zero tangents. They do not establish failed GetEndTangent evaluation in the existing harness and do not contradict the reachability analysis here. The standalone regression’s failed tangent evaluations use an empty B-spline in Rust. The proposed status-propagation policy in #700 was broader than this PR’s parity contract; that vertex change will be removed rather than overwrite the merged behavior.

The remaining follow-up covers failed edge reversal, UV corner/loop-gap/deviation evaluations and healing classification/retry state. Its updated native comparisons and explicit known records will be published after revalidation.

Merged via the queue into tangency-labs:main with commit 6766fe5 Oct 24, 2026
7 checks passed
fkirill added a commit to fkirill/solidcore that referenced this pull request Oct 24, 2026
Keep tangency-labs#704 implementation and coverage. Remove only proven obsolete vertex exceptions; retain six remaining failure/retry regressions and four explicit healing differences.

Co-Authored-By: Codex <noreply@openai.com>
fkirill added a commit to fkirill/solidcore that referenced this pull request Oct 24, 2026
Keep tangency-labs#704 implementation and coverage. Remove only proven obsolete vertex exceptions; retain six remaining failure/retry regressions and four explicit healing differences.

Co-Authored-By: Codex <noreply@openai.com>
fkirill added a commit to fkirill/solidcore that referenced this pull request Oct 24, 2026
Keep tangency-labs#704 implementation and coverage. Remove only proven obsolete vertex exceptions; retain six remaining failure/retry regressions and four explicit healing differences.

Co-Authored-By: Codex <noreply@openai.com>
fkirill added a commit to fkirill/solidcore that referenced this pull request Oct 24, 2026
Keep tangency-labs#704 implementation and coverage. Remove only proven obsolete vertex exceptions; retain six remaining failure/retry regressions and four explicit healing differences.

Co-Authored-By: Codex <noreply@openai.com>
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.

SMLib: loops, edges and vertices read values their failed calls never set (corner boundaries, edge reversal, topological vertex)

3 participants