Skip to content

Triangle test: scale-relative tolerances and exact coplanar overlap - #154

Open
menes97 wants to merge 1 commit into
IOExtend:mainfrom
menes97:fix/triangle-intersection-false-positives
Open

menes97 wants to merge 1 commit into
IOExtend:mainfrom
menes97:fix/triangle-intersection-false-positives

Conversation

@menes97

@menes97 menes97 commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Fixes #48

NwcTriangleIntersection had two problems:

  • It compared plane distances computed with unnormalised normals against an absolute epsilon (1e-7). The distances scale with triangle area, so for small triangles vertices that clearly miss a plane were snapped onto it.
  • It treated nearly coplanar triangles as clashing whenever their bounding boxes overlapped.

Following the issue:

  • The plane normals are normalised, and the snapping tolerance is relative to the larger triangle's longest edge. The parallelism test is therefore dimensionless.
  • Zero-area triangles are rejected.
  • For a coplanar pair, the triangles are projected onto the plane that drops the normal's dominant axis. They clash only when none of their six edge normals separates them (a 2D separating-axis test). A parallel pair on different planes doesn't clash.
  • ComputeInterval compares the already snapped distances to zero instead of to the absolute epsilon.

Tests (ClashDetectiveTests), each at scales 0.001, 1 and 1000:

  • TriangleIntersectionPerpendicularPairsAreScaleIndependent: the disjoint perpendicular example from the issue, and touching and crossing controls.
  • TriangleIntersectionCoplanarPairsUseProjectedOverlap: coplanar pairs whose bounding boxes overlap but whose triangles don't, in each axis plane.
  • TriangleIntersectionRejectsZeroAreaTriangles.

14 cases fail without the src change. The existing triangle and mesh clash tests still pass. dotnet build NwcIO.slnx succeeds, and NwcIO.Tests passes locally: 260 passed, and 2 skipped because they need private Navisworks files (.NET 10.0.400 on Windows, with global.json pinned down to it temporarily; not committed). I didn't benchmark the narrow phase; the change adds two square roots per triangle pair.

The change was drafted with OpenAI Codex, then reviewed and verified with Claude Code.

🤖 Generated with Claude Code

The plane distances used unnormalised normals against an absolute
epsilon, so small triangles were treated as touching planes they did
not reach, and coplanar triangles clashed whenever their bounding boxes
overlapped. The test now uses unit normals with a tolerance relative to
the triangle size, rejects zero-area triangles, and checks coplanar
pairs with a 2D separating-axis test on their edges.

Fixes IOExtend#48

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@mahach666 mahach666 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: the intended fix is useful, but the inline comment below describes a regression reproduced against the reviewed head and the unchanged base. Please address it and add the indicated regression test before merging.

Finding: Preserve actual intersections when triangle sizes differ.

if (Math.Abs(du0) < Epsilon) du0 = 0.0;
if (Math.Abs(du1) < Epsilon) du1 = 0.0;
if (Math.Abs(du2) < Epsilon) du2 = 0.0;
if (Math.Abs(du0) < distanceTolerance) du0 = 0.0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve actual intersections when triangle sizes differ

The tolerance derived from the larger triangle can snap every plane distance of the smaller triangle to zero even though the two planes are perpendicular.

Reproduction:

V = (0,0,0), (100000000,0,0), (0,100000000,0)
U = (99999999,3,-1), (99999999,3,1), (99999999,-1,0)

The point (99999999,0.5,0) lies strictly inside both triangles. Passing these as two meshes to the public NwcClashDetector.Run yields one clash on main/base and zero on this PR. Here the tolerance is about 14.14, so all three U distances (-1, 1, 0) become zero. ComputeInterval then uses only U's first two vertices, collapsing its true intersection interval to the wrong point.

Please preserve the correct intersection interval for non-coplanar triangles instead of treating all snapped distances as a coplanar interval, and add this case as a regression test.

This branch has not been deployed

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

Triangle-triangle test reports false hard clashes for small triangles and coplanar faces

2 participants