]> OCCT Git - occt.git/commitdiff
0032037: Visualization - move V3d_View::FitMinMax() to Graphic3d_Camera::FitMinMax()
authorkgv <kgv@opencascade.com>
Mon, 4 Jan 2021 08:53:24 +0000 (11:53 +0300)
committerbugmaster <bugmaster@opencascade.com>
Tue, 12 Jan 2021 10:29:21 +0000 (13:29 +0300)
src/Graphic3d/Graphic3d_Camera.cxx
src/Graphic3d/Graphic3d_Camera.hxx
src/V3d/V3d_View.cxx

index 0392b3ec0c233520567eb7ecc5f5c746d4ac1ebf..a7820bf1a8590efc5e4cfe71f6b929475643dc3f 100644 (file)
@@ -1370,6 +1370,165 @@ void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
   theOutMx.Multiply (anAxialScaleMx);
 }
 
+// =======================================================================
+// function : FitMinMax
+// purpose  :
+// =======================================================================
+bool Graphic3d_Camera::FitMinMax (const Bnd_Box& theBox,
+                                  const Standard_Real theResolution,
+                                  const bool theToEnlargeIfLine)
+{
+  // Check bounding box for validness
+  if (theBox.IsVoid())
+  {
+    return false; // bounding box is out of bounds...
+  }
+
+  // Apply "axial scaling" to the bounding points.
+  // It is not the best approach to make this scaling as a part of fit all operation,
+  // but the axial scale is integrated into camera orientation matrix and the other
+  // option is to perform frustum plane adjustment algorithm in view camera space,
+  // which will lead to a number of additional world-view space conversions and
+  // loosing precision as well.
+  const gp_Pnt aBndMin = theBox.CornerMin().XYZ().Multiplied (myAxialScale);
+  const gp_Pnt aBndMax = theBox.CornerMax().XYZ().Multiplied (myAxialScale);
+  if (aBndMax.IsEqual (aBndMin, RealEpsilon()))
+  {
+    return false; // nothing to fit all
+  }
+
+  // Prepare camera frustum planes.
+  gp_Pln aFrustumPlaneArray[6];
+  NCollection_Array1<gp_Pln> aFrustumPlane (aFrustumPlaneArray[0], 1, 6);
+  Frustum (aFrustumPlane[1], aFrustumPlane[2], aFrustumPlane[3],
+           aFrustumPlane[4], aFrustumPlane[5], aFrustumPlane[6]);
+
+  // Prepare camera up, side, direction vectors.
+  const gp_Dir aCamUp  = OrthogonalizedUp();
+  const gp_Dir aCamDir = Direction();
+  const gp_Dir aCamSide = aCamDir ^ aCamUp;
+
+  // Prepare scene bounding box parameters.
+  const gp_Pnt aBndCenter = (aBndMin.XYZ() + aBndMax.XYZ()) / 2.0;
+
+  gp_Pnt aBndCornerArray[8];
+  NCollection_Array1<gp_Pnt> aBndCorner (aBndCornerArray[0], 1, 8);
+  aBndCorner[1].SetCoord (aBndMin.X(), aBndMin.Y(), aBndMin.Z());
+  aBndCorner[2].SetCoord (aBndMin.X(), aBndMin.Y(), aBndMax.Z());
+  aBndCorner[3].SetCoord (aBndMin.X(), aBndMax.Y(), aBndMin.Z());
+  aBndCorner[4].SetCoord (aBndMin.X(), aBndMax.Y(), aBndMax.Z());
+  aBndCorner[5].SetCoord (aBndMax.X(), aBndMin.Y(), aBndMin.Z());
+  aBndCorner[6].SetCoord (aBndMax.X(), aBndMin.Y(), aBndMax.Z());
+  aBndCorner[7].SetCoord (aBndMax.X(), aBndMax.Y(), aBndMin.Z());
+  aBndCorner[8].SetCoord (aBndMax.X(), aBndMax.Y(), aBndMax.Z());
+
+  // Perspective-correct camera projection vector, matching the bounding box is determined geometrically.
+  // Knowing the initial shape of a frustum it is possible to match it to a bounding box.
+  // Then, knowing the relation of camera projection vector to the frustum shape it is possible to
+  // set up perspective-correct camera projection matching the bounding box.
+  // These steps support non-asymmetric transformations of view-projection space provided by camera.
+  // The zooming can be done by calculating view plane size matching the bounding box at center of
+  // the bounding box. The only limitation here is that the scale of camera should define size of
+  // its view plane passing through the camera center, and the center of camera should be on the
+  // same line with the center of bounding box.
+
+  // The following method is applied:
+  // 1) Determine normalized asymmetry of camera projection vector by frustum planes.
+  // 2) Determine new location of frustum planes, "matching" the bounding box.
+  // 3) Determine new camera projection vector using the normalized asymmetry.
+  // 4) Determine new zooming in view space.
+
+  // 1. Determine normalized projection asymmetry (if any).
+  Standard_Real anAssymX = Tan (( aCamSide).Angle (aFrustumPlane[1].Axis().Direction()))
+                         - Tan ((-aCamSide).Angle (aFrustumPlane[2].Axis().Direction()));
+  Standard_Real anAssymY = Tan (( aCamUp)  .Angle (aFrustumPlane[3].Axis().Direction()))
+                         - Tan ((-aCamUp)  .Angle (aFrustumPlane[4].Axis().Direction()));
+
+  // 2. Determine how far should be the frustum planes placed from center
+  //    of bounding box, in order to match the bounding box closely.
+  Standard_Real aFitDistanceArray[6];
+  NCollection_Array1<Standard_Real> aFitDistance (aFitDistanceArray[0], 1, 6);
+  aFitDistance.Init (0.0);
+  for (Standard_Integer anI = aFrustumPlane.Lower(); anI <= aFrustumPlane.Upper(); ++anI)
+  {
+    // Measure distances from center of bounding box to its corners towards the frustum plane.
+    const gp_Dir& aPlaneN = aFrustumPlane[anI].Axis().Direction();
+
+    Standard_Real& aFitDist = aFitDistance[anI];
+    for (Standard_Integer aJ = aBndCorner.Lower(); aJ <= aBndCorner.Upper(); ++aJ)
+    {
+      aFitDist = Max (aFitDist, gp_Vec (aBndCenter, aBndCorner[aJ]).Dot (aPlaneN));
+    }
+  }
+  // The center of camera is placed on the same line with center of bounding box.
+  // The view plane section crosses the bounding box at its center.
+  // To compute view plane size, evaluate coefficients converting "point -> plane distance"
+  // into view section size between the point and the frustum plane.
+  //       proj
+  //       /|\   right half of frame     //
+  //        |                           //
+  //  point o<--  distance * coeff  -->//---- (view plane section)
+  //         \                        //
+  //      (distance)                 //
+  //                ~               //
+  //                 (distance)    //
+  //                           \/\//
+  //                            \//
+  //                            //
+  //                      (frustum plane)
+  aFitDistance[1] *= Sqrt(1 + Pow (Tan (  aCamSide .Angle (aFrustumPlane[1].Axis().Direction())), 2.0));
+  aFitDistance[2] *= Sqrt(1 + Pow (Tan ((-aCamSide).Angle (aFrustumPlane[2].Axis().Direction())), 2.0));
+  aFitDistance[3] *= Sqrt(1 + Pow (Tan (  aCamUp   .Angle (aFrustumPlane[3].Axis().Direction())), 2.0));
+  aFitDistance[4] *= Sqrt(1 + Pow (Tan ((-aCamUp)  .Angle (aFrustumPlane[4].Axis().Direction())), 2.0));
+  aFitDistance[5] *= Sqrt(1 + Pow (Tan (  aCamDir  .Angle (aFrustumPlane[5].Axis().Direction())), 2.0));
+  aFitDistance[6] *= Sqrt(1 + Pow (Tan ((-aCamDir) .Angle (aFrustumPlane[6].Axis().Direction())), 2.0));
+
+  Standard_Real aViewSizeXv = aFitDistance[1] + aFitDistance[2];
+  Standard_Real aViewSizeYv = aFitDistance[3] + aFitDistance[4];
+  Standard_Real aViewSizeZv = aFitDistance[5] + aFitDistance[6];
+
+  // 3. Place center of camera on the same line with center of bounding
+  //    box applying corresponding projection asymmetry (if any).
+  Standard_Real anAssymXv = anAssymX * aViewSizeXv * 0.5;
+  Standard_Real anAssymYv = anAssymY * aViewSizeYv * 0.5;
+  Standard_Real anOffsetXv = (aFitDistance[2] - aFitDistance[1]) * 0.5 + anAssymXv;
+  Standard_Real anOffsetYv = (aFitDistance[4] - aFitDistance[3]) * 0.5 + anAssymYv;
+  gp_Vec aTranslateSide = gp_Vec (aCamSide) * anOffsetXv;
+  gp_Vec aTranslateUp   = gp_Vec (aCamUp)   * anOffsetYv;
+  gp_Pnt aCamNewCenter  = aBndCenter.Translated (aTranslateSide).Translated (aTranslateUp);
+
+  gp_Trsf aCenterTrsf;
+  aCenterTrsf.SetTranslation (Center(), aCamNewCenter);
+  Transform (aCenterTrsf);
+  SetDistance (aFitDistance[6] + aFitDistance[5]);
+
+  if (aViewSizeXv < theResolution
+   && aViewSizeYv < theResolution)
+  {
+    // Bounding box collapses to a point or thin line going in depth of the screen
+    if (aViewSizeXv < theResolution || !theToEnlargeIfLine)
+    {
+      return false; // This is just one point or line and zooming has no effect.
+    }
+
+    // Looking along line and "theToEnlargeIfLine" is requested.
+    // Fit view to see whole scene on rotation.
+    aViewSizeXv = aViewSizeZv;
+    aViewSizeYv = aViewSizeZv;
+  }
+
+  const Standard_Real anAspect = Aspect();
+  if (anAspect > 1.0)
+  {
+    SetScale (Max (aViewSizeXv / anAspect, aViewSizeYv));
+  }
+  else
+  {
+    SetScale (Max (aViewSizeXv, aViewSizeYv * anAspect));
+  }
+  return true;
+}
+
 //=============================================================================
 //function : ZFitAll
 //purpose  :
index 57b382d930df35c311f0ebb0df640f508799526a..966bd57c928624b8a134cb245d9527cb0ac49e2a 100644 (file)
@@ -359,6 +359,11 @@ public:
   //! Set Field Of View (FOV) restriction for 2D on-screen elements.
   Standard_EXPORT void SetFOV2d (Standard_Real theFOV);
 
+  //! Adjust camera to fit in specified AABB.
+  Standard_EXPORT bool FitMinMax (const Bnd_Box& theBox,
+                                  const Standard_Real theResolution,
+                                  const bool theToEnlargeIfLine);
+
   //! Estimate Z-min and Z-max planes of projection volume to match the
   //! displayed objects. The methods ensures that view volume will
   //! be close by depth range to the displayed objects. Fitting assumes that
index 9893f73e0b951c37050aa5cc536b6e5c51c75987..42705dacefdee49c8df908919502f9ef93389fe4 100644 (file)
@@ -2927,159 +2927,13 @@ Standard_Boolean V3d_View::FitMinMax (const Handle(Graphic3d_Camera)& theCamera,
                                       const Standard_Real theResolution,
                                       const Standard_Boolean theToEnlargeIfLine) const
 {
-  // Check bounding box for validness
-  if (theBox.IsVoid())
+  if (!theCamera->FitMinMax (theBox, theResolution, theToEnlargeIfLine))
   {
     return Standard_False; // bounding box is out of bounds...
   }
 
-  // Apply "axial scaling" to the bounding points.
-  // It is not the best approach to make this scaling as a part of fit all operation,
-  // but the axial scale is integrated into camera orientation matrix and the other
-  // option is to perform frustum plane adjustment algorithm in view camera space,
-  // which will lead to a number of additional world-view space conversions and
-  // loosing precision as well.
-  gp_Pnt aBndMin = theBox.CornerMin().XYZ().Multiplied (theCamera->AxialScale());
-  gp_Pnt aBndMax = theBox.CornerMax().XYZ().Multiplied (theCamera->AxialScale());
-
-  if (aBndMax.IsEqual (aBndMin, RealEpsilon()))
-  {
-    return Standard_False; // nothing to fit all
-  }
-
-  // Prepare camera frustum planes.
-  NCollection_Array1<gp_Pln> aFrustumPlane (1, 6);
-  theCamera->Frustum (aFrustumPlane.ChangeValue (1),
-                      aFrustumPlane.ChangeValue (2),
-                      aFrustumPlane.ChangeValue (3),
-                      aFrustumPlane.ChangeValue (4),
-                      aFrustumPlane.ChangeValue (5),
-                      aFrustumPlane.ChangeValue (6));
-
-  // Prepare camera up, side, direction vectors.
-  gp_Dir aCamUp  = theCamera->OrthogonalizedUp();
-  gp_Dir aCamDir = theCamera->Direction();
-  gp_Dir aCamSide = aCamDir ^ aCamUp;
-
-  // Prepare scene bounding box parameters.
-  gp_Pnt aBndCenter = (aBndMin.XYZ() + aBndMax.XYZ()) / 2.0;
-
-  NCollection_Array1<gp_Pnt> aBndCorner (1, 8);
-  aBndCorner.ChangeValue (1) = gp_Pnt (aBndMin.X(), aBndMin.Y(), aBndMin.Z());
-  aBndCorner.ChangeValue (2) = gp_Pnt (aBndMin.X(), aBndMin.Y(), aBndMax.Z());
-  aBndCorner.ChangeValue (3) = gp_Pnt (aBndMin.X(), aBndMax.Y(), aBndMin.Z());
-  aBndCorner.ChangeValue (4) = gp_Pnt (aBndMin.X(), aBndMax.Y(), aBndMax.Z());
-  aBndCorner.ChangeValue (5) = gp_Pnt (aBndMax.X(), aBndMin.Y(), aBndMin.Z());
-  aBndCorner.ChangeValue (6) = gp_Pnt (aBndMax.X(), aBndMin.Y(), aBndMax.Z());
-  aBndCorner.ChangeValue (7) = gp_Pnt (aBndMax.X(), aBndMax.Y(), aBndMin.Z());
-  aBndCorner.ChangeValue (8) = gp_Pnt (aBndMax.X(), aBndMax.Y(), aBndMax.Z());
-
-  // Perspective-correct camera projection vector, matching the bounding box is determined geometrically.
-  // Knowing the initial shape of a frustum it is possible to match it to a bounding box.
-  // Then, knowing the relation of camera projection vector to the frustum shape it is possible to
-  // set up perspective-correct camera projection matching the bounding box.
-  // These steps support non-asymmetric transformations of view-projection space provided by camera.
-  // The zooming can be done by calculating view plane size matching the bounding box at center of
-  // the bounding box. The only limitation here is that the scale of camera should define size of
-  // its view plane passing through the camera center, and the center of camera should be on the
-  // same line with the center of bounding box.
-
-  // The following method is applied:
-  // 1) Determine normalized asymmetry of camera projection vector by frustum planes.
-  // 2) Determine new location of frustum planes, "matching" the bounding box.
-  // 3) Determine new camera projection vector using the normalized asymmetry.
-  // 4) Determine new zooming in view space.
-
-  // 1. Determine normalized projection asymmetry (if any).
-  Standard_Real anAssymX = Tan (( aCamSide).Angle (aFrustumPlane (1).Axis().Direction()))
-                         - Tan ((-aCamSide).Angle (aFrustumPlane (2).Axis().Direction()));
-  Standard_Real anAssymY = Tan (( aCamUp)  .Angle (aFrustumPlane (3).Axis().Direction()))
-                         - Tan ((-aCamUp)  .Angle (aFrustumPlane (4).Axis().Direction()));
-
-  // 2. Determine how far should be the frustum planes placed from center
-  //    of bounding box, in order to match the bounding box closely.
-  NCollection_Array1<Standard_Real> aFitDistance (1, 6);
-  aFitDistance.ChangeValue (1) = 0.0;
-  aFitDistance.ChangeValue (2) = 0.0;
-  aFitDistance.ChangeValue (3) = 0.0;
-  aFitDistance.ChangeValue (4) = 0.0;
-  aFitDistance.ChangeValue (5) = 0.0;
-  aFitDistance.ChangeValue (6) = 0.0;
-
-  for (Standard_Integer anI = aFrustumPlane.Lower(); anI <= aFrustumPlane.Upper(); ++anI)
-  {
-    // Measure distances from center of bounding box to its corners towards the frustum plane.
-    const gp_Dir& aPlaneN = aFrustumPlane.ChangeValue (anI).Axis().Direction();
-
-    Standard_Real& aFitDist = aFitDistance.ChangeValue (anI);
-
-    for (Standard_Integer aJ = aBndCorner.Lower(); aJ <= aBndCorner.Upper(); ++aJ)
-    {
-      aFitDist = Max (aFitDist, gp_Vec (aBndCenter, aBndCorner (aJ)).Dot (aPlaneN));
-    }
-  }
-  // The center of camera is placed on the same line with center of bounding box.
-  // The view plane section crosses the bounding box at its center.
-  // To compute view plane size, evaluate coefficients converting "point -> plane distance"
-  // into view section size between the point and the frustum plane.
-  //       proj
-  //       /|\   right half of frame     //
-  //        |                           //
-  //  point o<--  distance * coeff  -->//---- (view plane section)
-  //         \                        //
-  //      (distance)                 // 
-  //                ~               //
-  //                 (distance)    //
-  //                           \/\//
-  //                            \//
-  //                            //
-  //                      (frustum plane)
-  aFitDistance.ChangeValue (1) *= Sqrt(1 + Pow (Tan (  aCamSide .Angle (aFrustumPlane (1).Axis().Direction())), 2.0));
-  aFitDistance.ChangeValue (2) *= Sqrt(1 + Pow (Tan ((-aCamSide).Angle (aFrustumPlane (2).Axis().Direction())), 2.0));
-  aFitDistance.ChangeValue (3) *= Sqrt(1 + Pow (Tan (  aCamUp   .Angle (aFrustumPlane (3).Axis().Direction())), 2.0));
-  aFitDistance.ChangeValue (4) *= Sqrt(1 + Pow (Tan ((-aCamUp)  .Angle (aFrustumPlane (4).Axis().Direction())), 2.0));
-  aFitDistance.ChangeValue (5) *= Sqrt(1 + Pow (Tan (  aCamDir  .Angle (aFrustumPlane (5).Axis().Direction())), 2.0));
-  aFitDistance.ChangeValue (6) *= Sqrt(1 + Pow (Tan ((-aCamDir) .Angle (aFrustumPlane (6).Axis().Direction())), 2.0));
-
-  Standard_Real aViewSizeXv = aFitDistance (1) + aFitDistance (2);
-  Standard_Real aViewSizeYv = aFitDistance (3) + aFitDistance (4);
-  Standard_Real aViewSizeZv = aFitDistance (5) + aFitDistance (6);
-
-  // 3. Place center of camera on the same line with center of bounding
-  //    box applying corresponding projection asymmetry (if any).
-  Standard_Real anAssymXv = anAssymX * aViewSizeXv * 0.5;
-  Standard_Real anAssymYv = anAssymY * aViewSizeYv * 0.5;
-  Standard_Real anOffsetXv = (aFitDistance (2) - aFitDistance (1)) * 0.5 + anAssymXv;
-  Standard_Real anOffsetYv = (aFitDistance (4) - aFitDistance (3)) * 0.5 + anAssymYv;
-  gp_Vec aTranslateSide = gp_Vec (aCamSide) * anOffsetXv;
-  gp_Vec aTranslateUp   = gp_Vec (aCamUp)   * anOffsetYv;
-  gp_Pnt aCamNewCenter  = aBndCenter.Translated (aTranslateSide).Translated (aTranslateUp);
-
-  gp_Trsf aCenterTrsf;
-  aCenterTrsf.SetTranslation (theCamera->Center(), aCamNewCenter);
-  theCamera->Transform (aCenterTrsf);
-  theCamera->SetDistance (aFitDistance (6) + aFitDistance (5));
-
-  // Bounding box collapses to a point or thin line going in depth of the screen
-  if (aViewSizeXv < theResolution && aViewSizeYv < theResolution)
-  {
-    if (aViewSizeXv < theResolution || !theToEnlargeIfLine)
-    {
-      return Standard_True; // This is just one point or line and zooming has no effect.
-    }
-
-    // Looking along line and "theToEnlargeIfLine" is requested.
-    // Fit view to see whole scene on rotation.
-    aViewSizeXv = aViewSizeZv;
-    aViewSizeYv = aViewSizeZv;
-  }
-
-  Scale (theCamera, aViewSizeXv, aViewSizeYv);
-
   const Standard_Real aZoomCoef = myView->ConsiderZoomPersistenceObjects();
-
   Scale (theCamera, theCamera->ViewDimensions().X() * (aZoomCoef + theMargin), theCamera->ViewDimensions().Y() * (aZoomCoef + theMargin));
-
   return Standard_True;
 }