]> OCCT Git - occt.git/commitdiff
0030765: Visualization - Incorrect intersection with Select3D_SensitiveBox when clipp... IR-2019-06-07
authorage <age@opencascade.com>
Wed, 5 Jun 2019 09:30:01 +0000 (12:30 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 7 Jun 2019 12:02:39 +0000 (15:02 +0300)
SelectMgr_RectangularFrustum::Overlaps method for computing intersection of box with frustum was reworked. Now the nearest non-clipped point is calculated.

src/SelectMgr/SelectMgr_RectangularFrustum.cxx
src/SelectMgr/SelectMgr_ViewClipRange.hxx
tests/v3d/point_cloud/sensitivebox [new file with mode: 0644]

index 9b7f540d71354dbd5bedf6a6a726482bcbeb383f..3b2bd06ff4f90a980d423b3284a4e0b26f20d07a 100644 (file)
@@ -204,6 +204,56 @@ namespace
     // Far
     theNormals[5] = -theNormals[4];
   }
+  
+  // =======================================================================
+  // function : rayBoxIntersection
+  // purpose  : Computes an intersection of ray with box
+  //            Returns distances to the first (or 0.0 if the ray origin is inside the box) and second intersection
+  //            If the ray has no intersection with the box returns DBL_MAX
+  // =======================================================================
+  Bnd_Range rayBoxIntersection (const gp_Ax1& theRay, const gp_Pnt& theBoxMin, const gp_Pnt& theBoxMax)
+  {
+    Standard_Real aTimeMinX = -DBL_MAX;
+    Standard_Real aTimeMinY = -DBL_MAX;
+    Standard_Real aTimeMinZ = -DBL_MAX;
+    Standard_Real aTimeMaxX = DBL_MAX;
+    Standard_Real aTimeMaxY = DBL_MAX;
+    Standard_Real aTimeMaxZ = DBL_MAX;
+
+    Standard_Real aTime1;
+    Standard_Real aTime2;
+
+    if (Abs (theRay.Direction().X()) > DBL_EPSILON)
+    {
+      aTime1 = (theBoxMin.X() - theRay.Location().X()) / theRay.Direction().X();
+      aTime2 = (theBoxMax.X() - theRay.Location().X()) / theRay.Direction().X();
+
+      aTimeMinX = Min (aTime1, aTime2);
+      aTimeMaxX = Max (aTime1, aTime2);
+    }
+    if (Abs (theRay.Direction().Y()) > DBL_EPSILON)
+    {
+      aTime1 = (theBoxMin.Y() - theRay.Location().Y()) / theRay.Direction().Y();
+      aTime2 = (theBoxMax.Y() - theRay.Location().Y()) / theRay.Direction().Y();
+
+      aTimeMinY = Min (aTime1, aTime2);
+      aTimeMaxY = Max (aTime1, aTime2);
+    }
+    if (Abs (theRay.Direction().Z()) > DBL_EPSILON)
+    {
+      aTime1 = (theBoxMin.Z() - theRay.Location().Z()) / theRay.Direction().Z();
+      aTime2 = (theBoxMax.Z() - theRay.Location().Z()) / theRay.Direction().Z();
+
+      aTimeMinZ = Min (aTime1, aTime2);
+      aTimeMaxZ = Max (aTime1, aTime2);
+    }
+
+    Standard_Real aTimeMin = Max (aTimeMinX, Max (aTimeMinY, aTimeMinZ));
+    Standard_Real aTimeMax = Min (aTimeMaxX, Min (aTimeMaxY, aTimeMaxZ));
+
+    return aTimeMin > aTimeMax || aTimeMax < 0.0 ? Bnd_Range (DBL_MAX, DBL_MAX)
+      : Bnd_Range (Max (aTimeMin, 0.0), aTimeMax);
+  }
 }
 
 // =======================================================================
@@ -446,14 +496,34 @@ Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& t
   if (!hasOverlap (theBoxMin, theBoxMax))
     return Standard_False;
 
-  gp_Pnt aNearestPnt (RealLast(), RealLast(), RealLast());
-  aNearestPnt.SetX (Max (Min (myNearPickedPnt.X(), theBoxMax.x()), theBoxMin.x()));
-  aNearestPnt.SetY (Max (Min (myNearPickedPnt.Y(), theBoxMax.y()), theBoxMin.y()));
-  aNearestPnt.SetZ (Max (Min (myNearPickedPnt.Z(), theBoxMax.z()), theBoxMin.z()));
+  gp_Ax1 aRay (myNearPickedPnt, myViewRayDir);
+  Bnd_Range aRange = rayBoxIntersection (aRay,
+                                         gp_Pnt (theBoxMin.x(), theBoxMin.y(), theBoxMin.z()),
+                                         gp_Pnt (theBoxMax.x(), theBoxMax.y(), theBoxMax.z()));
 
-  thePickResult.SetDepth (aNearestPnt.Distance (myNearPickedPnt));
+  Standard_Real aDepth = 0.0;
+  aRange.GetMin (aDepth);
 
-  return isViewClippingOk (thePickResult);
+  if (aDepth == DBL_MAX)
+  {
+    gp_Pnt aNearestPnt (RealLast(), RealLast(), RealLast());
+    aNearestPnt.SetX (Max (Min (myNearPickedPnt.X(), theBoxMax.x()), theBoxMin.x()));
+    aNearestPnt.SetY (Max (Min (myNearPickedPnt.Y(), theBoxMax.y()), theBoxMin.y()));
+    aNearestPnt.SetZ (Max (Min (myNearPickedPnt.Z(), theBoxMax.z()), theBoxMin.z()));
+
+    aDepth = aNearestPnt.Distance (myNearPickedPnt);
+    thePickResult.SetDepth (aDepth);
+    return isViewClippingOk (thePickResult);
+  }
+
+  if (myIsViewClipEnabled && !myViewClipRange.GetNearestDepth (aRange, aDepth))
+  {
+    return Standard_False;
+  }
+
+  thePickResult.SetDepth (aDepth);
+
+  return Standard_True;
 }
 
 // =======================================================================
index 97deae09c9750677bcb94f4c202cccf70ccfc83e..575d376f4974d32f15e78a883ebf15bfff1ce6c9 100644 (file)
@@ -48,6 +48,50 @@ public:
     }
     return Standard_False;
   }
+  
+  //! Calculates the min not clipped value from the range.
+  //! Returns FALSE if the whole range is clipped.
+  Standard_Boolean GetNearestDepth (const Bnd_Range& theRange, Standard_Real& theDepth) const
+  {
+    if (!myUnclipRange.IsVoid() && myUnclipRange.IsOut (theRange))
+    {
+      return false;
+    }
+
+    Bnd_Range aCommonClipRange;
+    theRange.GetMin (theDepth);
+
+    if (!myUnclipRange.IsVoid() && myUnclipRange.IsOut (theDepth))
+    {
+      myUnclipRange.GetMin (theDepth);
+    }
+
+    for (size_t aRangeIter = 0; aRangeIter < myClipRanges.size(); ++aRangeIter)
+    {
+      if (!myClipRanges[aRangeIter].IsOut (theDepth))
+      {
+        aCommonClipRange = myClipRanges[aRangeIter];
+        break;
+      }
+    }
+
+    if (aCommonClipRange.IsVoid())
+    {
+      return true;
+    }
+
+    for (size_t aRangeIter = 0; aRangeIter < myClipRanges.size(); ++aRangeIter)
+    {
+      if (!aCommonClipRange.IsOut (myClipRanges[aRangeIter]))
+      {
+        aCommonClipRange.Add (myClipRanges[aRangeIter]);
+      }
+    }
+
+    aCommonClipRange.GetMax (theDepth);
+
+    return !theRange.IsOut (theDepth);
+  }
 
   //! Clears clipping range.
   void SetVoid()
diff --git a/tests/v3d/point_cloud/sensitivebox b/tests/v3d/point_cloud/sensitivebox
new file mode 100644 (file)
index 0000000..89e9cfb
--- /dev/null
@@ -0,0 +1,66 @@
+puts "========"
+puts "Sensitive box selection"
+puts "========"
+
+proc checkPoint {theName theValue theExpected} {
+  set e 0.0001
+  foreach i {0 1 2} { if { [expr abs([lindex $theValue $i] - [lindex $theExpected $i])] > $e } { puts "Error: wrong picked point $theName" } }
+  return
+}
+
+# create sphere
+sphere ss 10
+mkface s ss
+incmesh s 0.01
+
+# draw sphere
+vinit View1
+vclear
+vsetdispmode 1
+vpointcloud p s -nonormals
+vselmode p 2 1
+vaxo
+vfit
+
+vclipplane pl1 -set -equation -1 0 0 0
+set p1 [vmoveto 200 200]
+if {[string first "e+308" $p1] != -1} {
+  puts "Faulty : Selection 1"
+}
+vpoint pp1 {*}$p1
+checkPoint pp1 $p1 {-1.7763568394002505e-15 -0.51078486684208357 0.59985611160264973}
+vdump $::imagedir/${::casename}_clip1_selection_axo.png
+vtop
+vdump $::imagedir/${::casename}_clip1_selection_top.png
+vaxo
+verase pp1
+
+vmoveto 300 200
+vdump $::imagedir/${::casename}_clip1_no_selection.png
+
+vclipplane pl1 -set -equation -1 0 0 3
+vclipplane pl2 -set -equation 1 0 0 3
+set p2 [vmoveto 200 200]
+if {[string first "e+308" $p2] != -1} {
+  puts "Faulty : Selection 2"
+}
+vpoint pp2 {*}$p2
+checkPoint pp2 $p2 {2.9999999999999991 -3.5107848668420845 3.5998561116026506}
+vdump $::imagedir/${::casename}_clip2_selection_axo.png
+vtop
+vdump $::imagedir/${::casename}_clip2_selection_top.png
+vaxo
+verase pp2
+
+vmoveto 75 200
+vdump $::imagedir/${::casename}_clip2_no_selection.png
+
+vtop
+vmoveto 250 200
+vdump $::imagedir/${::casename}_clip2_no_selection_top.png
+
+vaxo
+vfit
+vclipplane pl1 -delete
+vclipplane pl2 -delete
+vmoveto 200 200
\ No newline at end of file