Fix crash in Camera::computeFitViewEyePosition with parallel dir/up

dir^up becomes a zero vector when the view direction and up vector are parallel, producing an invalid zero-normal plane. Plane::distanceToOrigin() then asserts/aborts (CVF_ASSERT, always active). Fall back to an arbitrary axis to compute a valid right vector in this degenerate case.
This commit is contained in:
Magne Sjaastad committed 2026-10-05 19:56:10 +02:00
1 parent b0e8d0b62c
commit 1a6c29a319
4 files changed
+62 -1

No files matched your search

@@ -168,6 +168,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RimPolygonInViewCollection-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceInViewCollection-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RimViewController-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/CvfCameraCrashTriage-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaNameUniquenessTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifVtkSurfaceImporter-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifVtkReader-Test.cpp
@@ -0,0 +1,29 @@
#include "gtest/gtest.h"
#include "cvfBoundingBox.h"
#include "cvfCamera.h"
using namespace cvf;
//--------------------------------------------------------------------------------------------------
/// computeFitViewEyePosition() used to assert/abort (CVF_ASSERT in Plane::distanceToOrigin) when
/// the view direction and up vector are parallel: dir^up then becomes a zero vector, producing an
/// invalid (zero-normal) plane used internally. Reproduces the crash reported from
/// caf::Viewer::zoomAll() with a degenerate camera orientation.
//--------------------------------------------------------------------------------------------------
TEST( CvfCameraCrashTriageTest, FitViewWithParallelDirAndUpDoesNotCrash )
{
Camera c;
BoundingBox bb;
bb.add( Vec3d( 0, 0, 0 ) );
bb.add( Vec3d( 1, 1, 1 ) );
Vec3d inViewDir( 0, 0, 1 );
Vec3d inUp( 0, 0, 1 );
double coverageFactor = 0.9;
Vec3d eye = Camera::computeFitViewEyePosition( bb, inViewDir, inUp, coverageFactor, c.fieldOfViewYDeg(), 1.0 );
EXPECT_FALSE( eye.isUndefined() );
}
+8 -1
View File
@@ -306,7 +306,14 @@ Vec3d Camera::computeFitViewEyePosition(const BoundingBox& boundingBox, const Ve
cvf::Vec3d upNorm = up.getNormalized();
cvf::Vec3d right = dir^up;
right.normalize();
if (!right.normalize())
{
// dir and up are parallel (degenerate camera orientation). Fall back to an arbitrary axis
// not parallel to dir to still produce a valid right vector.
const cvf::Vec3d& fallbackAxis = (cvf::Math::abs(dir.x()) < 0.9) ? cvf::Vec3d::X_AXIS : cvf::Vec3d::Y_AXIS;
right = dir^fallbackAxis;
right.normalize();
}
cvf::Vec3d boxEyeNorm = (-dir).getNormalized();
cvf::Plane planeTop;
@@ -184,6 +184,30 @@ TEST(CameraTest, FitView)
}
//--------------------------------------------------------------------------------------------------
/// computeFitViewEyePosition() used to assert/abort (CVF_ASSERT in Plane::distanceToOrigin) when
/// the view direction and up vector are parallel: dir^up then becomes a zero vector, producing an
/// invalid (zero-normal) plane used internally. Reproduces the crash reported from
/// caf::Viewer::zoomAll() with a degenerate camera orientation.
//--------------------------------------------------------------------------------------------------
TEST(CameraTest, FitViewWithParallelDirAndUpDoesNotCrash)
{
Camera c;
BoundingBox bb;
bb.add(Vec3d(0, 0, 0));
bb.add(Vec3d(1, 1, 1));
Vec3d inViewDir(0, 0, 1);
Vec3d inUp(0, 0, 1);
double coverageFactor = 0.9;
Vec3d eye = Camera::computeFitViewEyePosition(bb, inViewDir, inUp, coverageFactor, c.fieldOfViewYDeg(), 1.0);
EXPECT_FALSE(eye.isUndefined());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------