Refactor/moller trumbore ray triangle#1305
Refactor/moller trumbore ray triangle#1305sbryngelson wants to merge 7 commits intoMFlowCode:masterfrom
Conversation
…e barycentric algorithm The previous f_intersects_triangle used three cross products and sign checks against the triangle normal, which is winding-order dependent. If vertices are loaded in the wrong order, the sign flips and intersections are missed. Replace with the Moller-Trumbore algorithm, which computes barycentric coordinates (u, v) directly from the vertices. This is vertex winding-order independent, uses two cross products instead of three, and no longer depends on triangle%n. Also remove the now-dead tri%n copy in f_model_is_inside_flat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ide/outside test Replace the 26-ray casting approach in f_model_is_inside_flat with the generalized winding number (Jacobson et al., SIGGRAPH 2013). The winding number sums the solid angle subtended by each triangle at the query point using the Van Oosterom-Strackee formula. This is robust to small triangles: a tiny triangle contributes a proportionally small solid angle rather than being missed entirely by rays. It is also winding-order independent and branch-free in the inner loop, which is GPU-friendly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code ReviewHead SHA: a1d3d9e Summary
Findings1. 2D simulation case — winding number may degenerate (medium concern) The old code explicitly skipped z-direction rays when 2. pi computed via fraction = fraction/(2.0_wp*acos(-1.0_wp))If the codebase already defines a named 3. Möller–Trumbore epsilon threshold — unchanged behaviour, noted for clarity if (abs(a) < 1e-7_wp) returnThe old implementation used 4. Möller–Trumbore works from vertices alone and correctly drops the normal. However, check whether the No other issues foundThe winding-number normalization is mathematically correct: each triangle contributes |
The 3D solid angle (Van Oosterom-Strackee) degenerates when all triangles are coplanar (z=0), producing near-zero winding numbers everywhere. For 2D (p==0), use the 2D winding number instead: sum the signed angle subtended by each boundary edge via atan2(cross,dot). This uses the existing gpu_boundary_v and gpu_boundary_edge_count arrays that are already computed and uploaded for 2D STL models. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code ReviewHead SHA: Files changed: 1
Summary:
Findings1. [Critical]
do q = 1, gpu_boundary_edge_count(pid)
d1(1) = gpu_boundary_v(q, 1, 1, pid) - point(1)
...These two arrays are referenced but do not appear anywhere in the diff. For this PR to be complete, they must be:
If these arrays are defined elsewhere in the same file outside this diff, please point to the line numbers. If they are not yet defined, the PR is incomplete and will fail to compile. 2. [Minor] The comment at ~line 655: ! atan2(0,0) = 0 per IEEE 754, so degenerate
! triangles contribute nothing.IEEE 754 does not define 3. [Minor] Hardcoded
if (abs(a) < 1e-7_wp) returnThe old code used 4. [Nit] PR description The body is just "better stls". A one-paragraph description of the algorithmic change, why winding number is more robust than ray casting, and any known limitations (e.g., non-watertight meshes) would aid reviewers and future readers. Correctness AssessmentThe mathematics in both the 2D and 3D winding number implementations appears correct:
The primary blocker is item 1 above: confirming |
Add explicit tiny() guard for degenerate triangles (zero-area or query point on vertex) instead of relying on atan2(0,0) behavior, which is compiler-dependent. Add comment explaining the Moller-Trumbore epsilon threshold. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code ReviewHead SHA: a819dd9 Summary
Findings1. Unverified 2D arrays
|
- Use max(1e-7, 10*epsilon) for Moller-Trumbore parallel-ray threshold so single-precision builds get a sensible floor above machine epsilon. - Fix comment on degenerate guard: only fires for vertex-coincident query points (subnormal/zero magnitudes), not general degeneracy. - Fix winding number normalization comment: each atan2 returns Omega/2, so dividing by 2*pi gives sum(Omega)/(4*pi). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code ReviewHead SHA: 58273b1 Summary
Findings1. [Critical] Undeclared arrays
|
Claude Code ReviewHead SHA: Summary
Findings1. Product underflow in coincidence guard — medium! src/common/m_model.fpp (new code, ~line 658)
if (r1_mag*r2_mag*r3_mag < tiny(1.0_wp)) cycleMultiplying three magnitudes can silently underflow to 0.0 even when each individual magnitude is much larger than zero. In single-precision ( if (r1_mag < tiny(1.0_wp)**0.34_wp .or. &
r2_mag < tiny(1.0_wp)**0.34_wp .or. &
r3_mag < tiny(1.0_wp)**0.34_wp) cycleor simply: if (min(r1_mag, r2_mag, r3_mag) < sqrt(tiny(1.0_wp))) cycle2. Misleading inline comment — minor! Each atan2 returns Omega/2 per triangle; divide
! by 2*pi to get winding number = sum(Omega)/(4*pi).
fraction = fraction/(2.0_wp*acos(-1.0_wp))The comment is partially correct: each ! accumulator = sum(Omega/2); winding number = sum(Omega)/(4*pi) = accumulator/(2*pi)3.
|
Claude Code ReviewHead SHA: e8aff26 Files changed: 1
Summary
Findings[Minor – numerical stability] if (abs(a) < max(1e-7_wp, 10.0_wp*epsilon(1.0_wp))) return
[Minor – edge case, 3D winding number] if (r1_mag*r2_mag*r3_mag < tiny(1.0_wp)) cycleThe guard catches exact coincidence with a vertex, but not near-coincidence (e.g. query point ~ [Correctness – verified ✓] The 3D division: fraction = fraction/(2.0_wp*acos(-1.0_wp))Each [Correctness – verified ✓] 2D indexing matches master convention: Opportunities (non-blocking)
Overall the algorithmic replacement is well-motivated, mathematically sound, and cleaner than the old approach. The two minor numerical points above are both pre-existing in spirit and do not block merging. |
Summary
Refactors the STL ray-triangle intersection and inside/outside classification in m_model.fpp:
Moller-Trumbore algorithm replaces the old cross-product sign test in f_intersects_triangle. The old approach was winding-order dependent. Moller-Trumbore computes barycentric coordinates directly from vertices, making it vertex-order agnostic.
Generalized winding number replaces the 26-ray casting approach in f_model_is_inside_flat. Ray casting fails on small triangles because rays can miss or edge-graze tiny faces, corrupting the parity count. The winding number sums the solid angle (Van Oosterom-Strackee formula) subtended by each triangle. This is the standard approach in geometry processing (Jacobson et al., SIGGRAPH 2013).
2D branch uses the 2D winding number (signed angle sum over boundary edges) for p==0 simulations, since the 3D solid angle degenerates when all triangles are coplanar.
Known limitations