Fix crash in Camera::computeFitViewEyePosition with zero view direction

Zoom All could pass a zero view direction (eye equal to view reference
point). The up vector then also became zero, giving a plane with a zero
normal and an assert in Plane::distanceToOrigin. Fall back to the Y axis
when the direction is zero or undefined. Follow-up to the parallel
dir/up fix in #14849.

Call stack:
[5] cvf::AssertHandlerConsole::handleAssert(char const*, int, char const*, char const*) at cvfAssert.cpp:125
[6] cvf::Plane::distanceToOrigin() const at cvfPlane.cpp:313
[7] cvf::Camera::computeFitViewEyePosition(cvf::BoundingBox const&, cvf::Vector3<double> const&, cvf::Vector3<double> const&, double, double, double) at cvfCamera.cpp:361
[8] caf::Viewer::zoomAll() at cafViewer.cpp:992
[9] caf::CmdFeature::actionTriggered(bool) at cafCmdFeature.cpp:176
This commit is contained in:
Magne Sjaastad committed 2026-10-19 19:58:37 +02:00
1 parent 72df8a69f5
commit d8dc1c051f
2 files changed
+60 -2

No files matched your search

@@ -85,3 +85,44 @@ TEST( CvfCameraCrashTriageTest, FitViewWithParallelDirAndUpProducesValidViewMatr
EXPECT_NEAR( c.direction() * c.right(), 0.0, 1e-6 );
EXPECT_NEAR( c.up() * c.right(), 0.0, 1e-6 );
}
//--------------------------------------------------------------------------------------------------
/// A zero view direction (eye == view reference point, as in caf::Viewer::zoomAll()) gave a zero up
/// vector and a zero-normal plane, which asserted in Plane::distanceToOrigin().
//--------------------------------------------------------------------------------------------------
TEST( CvfCameraCrashTriageTest, FitViewEyePositionWithZeroDirDoesNotCrash )
{
Camera c;
BoundingBox bb;
bb.add( Vec3d( 0, 0, 0 ) );
bb.add( Vec3d( 1, 1, 1 ) );
for ( const auto& dir : { Vec3d::ZERO, Vec3d::UNDEFINED } )
{
Vec3d eye = Camera::computeFitViewEyePosition( bb, dir, Vec3d( 0, 0, 1 ), 0.9, c.fieldOfViewYDeg(), 1.0 );
EXPECT_FALSE( eye.isUndefined() );
}
}
//--------------------------------------------------------------------------------------------------
/// fitView() must derive the up vector from the same valid direction as the eye position
//--------------------------------------------------------------------------------------------------
TEST( CvfCameraCrashTriageTest, FitViewWithZeroOrUndefinedDirProducesValidViewMatrix )
{
BoundingBox bb;
bb.add( Vec3d( 0, 0, 0 ) );
bb.add( Vec3d( 1, 1, 1 ) );
for ( const auto& dir : { Vec3d::ZERO, Vec3d::UNDEFINED } )
{
Camera c;
c.setViewport( 0, 0, 100, 100 );
c.fitView( bb, dir, Vec3d( 0, 0, 1 ), 0.9 );
EXPECT_NEAR( c.direction().length(), 1.0, 1e-6 );
EXPECT_NEAR( c.up().length(), 1.0, 1e-6 );
EXPECT_NEAR( c.right().length(), 1.0, 1e-6 );
}
}
+19 -2
View File
@@ -43,10 +43,23 @@
#include "cvfBoundingBox.h"
#include "cvfGeometryUtils.h"
#include <limits>
namespace cvf {
namespace {
//--------------------------------------------------------------------------------------------------
/// A zero, undefined or non-finite view direction cannot define a view; fall back to looking along the Y axis
//--------------------------------------------------------------------------------------------------
Vec3d validViewDirection(const Vec3d& dir)
{
const double length = dir.length();
if (!dir.isUndefined() && length > 0.0 && length < std::numeric_limits<double>::max()) return dir;
return Vec3d::Y_AXIS;
}
//--------------------------------------------------------------------------------------------------
/// Returns an up vector guaranteed to be non-parallel to dir. If the passed up vector is already
/// non-parallel to dir, it is returned unchanged. Otherwise a corrected, orthogonal up vector is
@@ -310,8 +323,10 @@ void Camera::setProjectionAsPixelExact2D()
/// the view to. The relativeDistance parameter specifies the distance from the camera to the
/// center of the bounding box
//--------------------------------------------------------------------------------------------------
void Camera::fitView(const BoundingBox& boundingBox, const Vec3d& dir, const Vec3d& up, double coverageFactor)
void Camera::fitView(const BoundingBox& boundingBox, const Vec3d& dirIn, const Vec3d& up, double coverageFactor)
{
const cvf::Vec3d dir = validViewDirection(dirIn);
// dir and up may be parallel (degenerate camera orientation); derive a corrected, non-parallel
// up vector so both the eye computation and the final look-at matrix use a consistent basis.
cvf::Vec3d correctedUp = nonParallelUpVector(dir, up);
@@ -327,8 +342,10 @@ void Camera::fitView(const BoundingBox& boundingBox, const Vec3d& dir, const Vec
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Vec3d Camera::computeFitViewEyePosition(const BoundingBox& boundingBox, const Vec3d& dir, const Vec3d& up, double coverageFactor, double fieldOfViewYDeg, double aspectRatio)
Vec3d Camera::computeFitViewEyePosition(const BoundingBox& boundingBox, const Vec3d& dirIn, const Vec3d& up, double coverageFactor, double fieldOfViewYDeg, double aspectRatio)
{
const cvf::Vec3d dir = validViewDirection(dirIn);
cvf::Vec3d corners[8];
boundingBox.cornerVertices(corners);