Pipe geometry: Skip non-finite coords and degenerate cylinder segments

Robustness fixes in RivPipeGeometryGenerator:

- updateFilteredPipeCenterCoords: drop NaN/Inf input coords. The main loop
  now skips iterations where any of the three sampled coords is undefined,
  and the trailing endpoint walks back to the last finite coord so the
  segment-to-result bookkeeping invariant holds.

- computeExtrudedCoordsAndNormals: stop asserting on parallel plane/ray
  intersections (these can occur at very sharp bends due to numerical
  issues) and return early when the extruded node count does not match the
  expected cross-section node count.
This commit is contained in:
Magne Sjaastad committed 2026-05-05 21:55:10 +02:00
1 parent bac0249e6a
commit 498c6be663
1 file changed
+19 -6
@@ -604,14 +604,16 @@ void RivPipeGeometryGenerator::computeExtrudedCoordsAndNormals( cvf::Vec3d
{
nextExtrudedNodes.push_back( intersection );
}
else
{
// Parallel plane and ray
CVF_ASSERT( false );
}
}
}
if ( nextExtrudedNodes.size() != crossSectionNodeCount )
{
// This should not happen, but if it does, we just skip the segment. This can happen for very sharp bends where the intersection
// plane is almost parallel to the ray direction, and numerical issues can cause us to miss the intersection.
return;
}
// Calculate the normals for the cylinder segment
cvf::Plane cylinderPlane;
@@ -662,6 +664,10 @@ void RivPipeGeometryGenerator::updateFilteredPipeCenterCoords()
cvf::Vec3d coordB = m_originalPipeCenterCoords->get( coordBIdx + 0 );
cvf::Vec3d coordC = m_originalPipeCenterCoords->get( coordBIdx + 1 );
// Skip non-finite coords; treat as zero-length so they neither update
// lastValidDirectionAB nor get pushed to the filtered list.
if ( coordA.isUndefined() || coordB.isUndefined() || coordC.isUndefined() ) continue;
cvf::Vec3d directionAB = coordB - coordA;
if ( directionAB.lengthSquared() > squareDistanceTolerance )
@@ -722,7 +728,14 @@ void RivPipeGeometryGenerator::updateFilteredPipeCenterCoords()
// Add the last point, as the above loop will not end the last none-zero segment, but wait for the start of the next
// valid one.
m_filteredPipeCenterCoords.push_back( m_originalPipeCenterCoords->get( lastOriginalCoordIdx ) );
// Walk backwards to the last finite coord. The loop pre-pushes a segment-to-result entry for the
// upcoming endpoint, so we must push one finite coord here to keep the assert below in sync.
size_t lastFiniteIdx = lastOriginalCoordIdx;
while ( lastFiniteIdx > firstSegmentWithLength && m_originalPipeCenterCoords->get( lastFiniteIdx ).isUndefined() )
{
--lastFiniteIdx;
}
m_filteredPipeCenterCoords.push_back( m_originalPipeCenterCoords->get( lastFiniteIdx ) );
CVF_ASSERT( m_filteredPipeCenterCoords.size() - 1 == m_filteredPipeSegmentToResult.size() );
}