From d31fb73a09629de3be7dec93e712fa0197d7003d Mon Sep 17 00:00:00 2001 From: age Date: Wed, 5 Jun 2019 12:30:01 +0300 Subject: [PATCH] 0030765: Visualization - Incorrect intersection with Select3D_SensitiveBox when clipping is turned on SelectMgr_RectangularFrustum::Overlaps method for computing intersection of box with frustum was reworked. Now the nearest non-clipped point is calculated. --- .../SelectMgr_RectangularFrustum.cxx | 82 +++++++++++++++++-- src/SelectMgr/SelectMgr_ViewClipRange.hxx | 44 ++++++++++ tests/v3d/point_cloud/sensitivebox | 66 +++++++++++++++ 3 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 tests/v3d/point_cloud/sensitivebox diff --git a/src/SelectMgr/SelectMgr_RectangularFrustum.cxx b/src/SelectMgr/SelectMgr_RectangularFrustum.cxx index 9b7f540d71..3b2bd06ff4 100644 --- a/src/SelectMgr/SelectMgr_RectangularFrustum.cxx +++ b/src/SelectMgr/SelectMgr_RectangularFrustum.cxx @@ -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; } // ======================================================================= diff --git a/src/SelectMgr/SelectMgr_ViewClipRange.hxx b/src/SelectMgr/SelectMgr_ViewClipRange.hxx index 97deae09c9..575d376f49 100644 --- a/src/SelectMgr/SelectMgr_ViewClipRange.hxx +++ b/src/SelectMgr/SelectMgr_ViewClipRange.hxx @@ -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 index 0000000000..89e9cfbaa4 --- /dev/null +++ b/tests/v3d/point_cloud/sensitivebox @@ -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