0031412: Visualization - entity behind is returned as topmost at the edges
authormzernova <mzernova@opencascade.com>
Tue, 10 Mar 2020 13:06:24 +0000 (16:06 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 25 Jun 2020 16:09:04 +0000 (19:09 +0300)
SelectMgr_RectangularFrustum now handles degenerated triangle as a segment or a point.
Triangle orthogonal to view direction is now handled as a segment.
myViewRayDir field is now defined as normalized vector to avoid confusing math.

For the case when the segment and myViewRayDir are almost parallel,
the segmentSegmentDistance function may set the depth to zero, although this will not
be the correct value, therefore it is better to pass a segment that will not
be parallel to myViewRayDir as an argument to the function.

vpoint command has been extended by arguments -2d and -nosel
for displaying picking mouse position as on-screen point in pixels.
Fixed double-binding-map error in case of displaying point with already used name.

bugs/vis/bug31412: test case added.

src/SelectMgr/SelectMgr_RectangularFrustum.cxx
src/SelectMgr/SelectMgr_RectangularFrustum.hxx
src/ViewerTest/ViewerTest_ObjectCommands.cxx
tests/bugs/vis/bug31412 [new file with mode: 0644]
tests/v3d/manipulator/zoom_persistence

index c5b9f2f..6ab3470 100644 (file)
@@ -27,7 +27,7 @@ void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegP
                                                            SelectBasics_PickResult& thePickResult) const
 {
   gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
-  gp_XYZ aV = myViewRayDir.XYZ();
+  gp_XYZ aV = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane
   gp_XYZ aW = theSegPnt1.XYZ() - myNearPickedPnt.XYZ();
 
   Standard_Real anA = anU.Dot (anU);
@@ -70,7 +70,7 @@ void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegP
   }
   aTc = (Abs (aTd) < gp::Resolution() ? 0.0 : aTn / aTd);
 
-  const gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTc;
+  const gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + aV * aTc;
   thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
 
   const gp_Vec aPickedVec = aClosestPnt.XYZ() - theSegPnt1.XYZ();
@@ -97,7 +97,7 @@ bool SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePl
                                                              const gp_Pnt& thePntOnPlane,
                                                              SelectBasics_PickResult& thePickResult) const
 {
-  gp_XYZ anU = myViewRayDir.XYZ();
+  gp_XYZ anU = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane by > 1.0 check
   gp_XYZ aW = myNearPickedPnt.XYZ() - thePntOnPlane.XYZ();
   Standard_Real aD = thePlane.Dot (anU);
   Standard_Real aN = -thePlane.Dot (aW);
@@ -117,7 +117,7 @@ bool SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePl
   }
 
   Standard_Real aParam = aN / aD;
-  if (aParam < 0.0 || aParam > 1.0)
+  if (aParam < 0.0 || aParam > 1.0) // > 1.0 check could be removed for an infinite ray and anU=myViewRayDir
   {
     thePickResult.Invalidate();
     return false;
@@ -429,7 +429,7 @@ Handle(SelectMgr_BaseFrustum) SelectMgr_RectangularFrustum::ScaleAndTransform (c
     theTrsf.Transforms (aPoint.ChangeCoord());
     aRes->myFarPickedPnt = aPoint;
 
-    aRes->myViewRayDir    = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
+    aRes->myViewRayDir = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
 
     for (Standard_Integer anIt = 0; anIt < 8; anIt++)
     {
@@ -533,10 +533,9 @@ Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt,
     return Standard_False;
 
   gp_XYZ aV = thePnt.XYZ() - myNearPickedPnt.XYZ();
-  gp_Pnt aDetectedPnt =
-    myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * (aV.Dot (myViewRayDir.XYZ()) / myViewRayDir.Dot (myViewRayDir));
+  const Standard_Real aDepth = aV.Dot (myViewRayDir.XYZ());
 
-  thePickResult.SetDepth (aDetectedPnt.Distance (myNearPickedPnt) * myScale);
+  thePickResult.SetDepth (Abs (aDepth) * myScale);
   thePickResult.SetPickedPoint (thePnt);
 
   return !theClipRange.IsClipped (thePickResult.Depth());
@@ -648,39 +647,47 @@ Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
   {
     gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
     if (!hasOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
+    {
       return Standard_False;
+    }
 
-    // check if intersection point belongs to triangle's interior part
-    gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
-                           thePnt3.XYZ() - thePnt2.XYZ(),
-                           thePnt1.XYZ() - thePnt3.XYZ() };
+    const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
+                                 thePnt3.XYZ() - thePnt2.XYZ(),
+                                 thePnt1.XYZ() - thePnt3.XYZ() };
+         if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
+    {
+      // consider degenerated triangle as point or segment
+      return aTrEdges[0].SquareModulus() > gp::Resolution()
+           ? Overlaps (thePnt1, thePnt2, theClipRange, thePickResult)
+           : (aTrEdges[1].SquareModulus() > gp::Resolution()
+            ? Overlaps (thePnt2, thePnt3, theClipRange, thePickResult)
+            : Overlaps (thePnt1, theClipRange, thePickResult));
+    }
 
-    Standard_Real anAlpha = aTriangleNormal.Dot (myViewRayDir);
+    const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
+    const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myViewRayDir.XYZ());
     if (Abs (anAlpha) < gp::Resolution())
     {
-      // handle degenerated triangles: in this case, there is no possible way to detect overlap correctly.
-      if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
+      // handle the case when triangle normal and selecting frustum direction are orthogonal
+      SelectBasics_PickResult aPickResult;
+      thePickResult.Invalidate();
+      for (Standard_Integer anEdgeIter = 0; anEdgeIter < 3; ++anEdgeIter)
       {
-        return Standard_False;
+        const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
+        const gp_Pnt& anEndPnt  = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
+        segmentSegmentDistance (aStartPnt, anEndPnt, aPickResult);
+        thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
       }
-
-      // handle the case when triangle normal and selecting frustum direction are orthogonal: for this case, overlap
-      // is detected correctly, and distance to triangle's plane can be measured as distance to its arbitrary vertex.
-      const gp_XYZ aDiff = myNearPickedPnt.XYZ() - thePnt1.XYZ();
-      thePickResult.SetDepth (aTriangleNormal.Dot (aDiff) * myScale);
-      thePickResult.SetPickedPoint (thePnt1);
-      thePickResult.SetSurfaceNormal (aTriangleNormal);
       return !theClipRange.IsClipped (thePickResult.Depth());
     }
 
-    gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
-
-    Standard_Real aTime = aTriangleNormal.Dot (anEdge);
-
-    gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
+    // check if intersection point belongs to triangle's interior part
+    const gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
 
-    Standard_Real anU = aVec.Dot (aTrEdges[2]);
-    Standard_Real aV  = aVec.Dot (aTrEdges[0]);
+    const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
+    const gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
+    const Standard_Real anU = aVec.Dot (aTrEdges[2]);
+    const Standard_Real aV  = aVec.Dot (aTrEdges[0]);
 
     const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
     const gp_Pnt aPtOnPlane = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
@@ -692,21 +699,25 @@ Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
       return !theClipRange.IsClipped (thePickResult.Depth());
     }
 
-    gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
     Standard_Real aMinDist = RealLast();
-    Standard_Integer aNearestEdgeIdx = -1;
+    Standard_Integer aNearestEdgeIdx1 = -1;
     for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
     {
       gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
       Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
       Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
-      if (aMinDist > aDist)
+      if (aDist < aMinDist)
       {
         aMinDist = aDist;
-        aNearestEdgeIdx = anEdgeIdx;
+        aNearestEdgeIdx1 = anEdgeIdx;
       }
     }
-    segmentSegmentDistance (aPnts[aNearestEdgeIdx], aPnts[(aNearestEdgeIdx + 1) % 3], thePickResult);
+    Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
+    if (myViewRayDir.IsParallel (gp_Vec (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]), Precision::Angular()))
+    {
+      aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
+    }
+    segmentSegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult);
   }
 
   return !theClipRange.IsClipped (thePickResult.Depth());
@@ -729,7 +740,7 @@ Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt&
 // =======================================================================
 gp_Pnt SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
 {
-  return myNearPickedPnt.XYZ() + myViewRayDir.Normalized().XYZ() * theDepth / myScale;
+  return myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * theDepth / myScale;
 }
 
 // =======================================================================
index 8141140..e5170fc 100644 (file)
@@ -120,7 +120,7 @@ public:
   inline const gp_Pnt& GetFarPnt() const { return myFarPickedPnt; }
 
   //! Return view ray direction.
-  const gp_Vec& GetViewRayDirection() const { return myViewRayDir; }
+  const gp_Dir& GetViewRayDirection() const { return myViewRayDir; }
 
   //! Return mouse coordinates.
   const gp_Pnt2d& GetMousePosition() const { return myMousePos; }
@@ -153,7 +153,7 @@ private:
 
   gp_Pnt                  myNearPickedPnt;             //!< 3d projection of user-picked selection point onto near view plane
   gp_Pnt                  myFarPickedPnt;              //!< 3d projection of user-picked selection point onto far view plane
-  gp_Vec                  myViewRayDir;
+  gp_Dir                  myViewRayDir;
   gp_Pnt2d                myMousePos;                  //!< Mouse coordinates
   Standard_Real           myScale;                     //!< Scale factor of applied transformation, if there was any
 
index 40bd123..d2c3aa1 100644 (file)
@@ -1062,8 +1062,7 @@ static int VAxisBuilder(Draw_Interpretor& di, Standard_Integer argc, const char*
 
 //==============================================================================
 //function : VPointBuilder
-//purpose  : Build an AIS_Point from coordinates or with a selected vertex or edge
-//Draw arg : vpoint PoinName [Xa] [Ya] [Za]
+//purpose  :
 //==============================================================================
 #include <TopoDS_Edge.hxx>
 #include <TopoDS_Vertex.hxx>
@@ -1071,70 +1070,105 @@ static int VAxisBuilder(Draw_Interpretor& di, Standard_Integer argc, const char*
 #include <AIS_Point.hxx>
 #include <Geom_CartesianPoint.hxx>
 
-static int VPointBuilder(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
+static int VPointBuilder(Draw_Interpretor& ,
+                         Standard_Integer theArgNb,
+                         const char** theArgVec)
 {
-  // Declarations
-  Standard_Boolean HasArg;
-  TCollection_AsciiString name;
-
-  // Verification
-  if (argc<2 || argc>5 ) {di<<" Syntaxe error\n";return 1;}
-  if (argc==5) HasArg=Standard_True;
-  else HasArg=Standard_False;
-
-  name=argv[1];
-
-  // Il y a des arguments: teste l'unique constructeur AIS_Pnt::AIS_Pnt(Point from Geom)
-  if (HasArg) {
-    Standard_Real thecoord[3];
-    for(Standard_Integer i=0;i<=2;i++)
-      thecoord[i]=Draw::Atof(argv[2+i]);
-    Handle(Geom_CartesianPoint )  myGeomPoint= new Geom_CartesianPoint (thecoord[0],thecoord[1],thecoord[2]);
-    Handle(AIS_Point)  myAISPoint=new AIS_Point(myGeomPoint );
-    GetMapOfAIS().Bind (myAISPoint,name);
-    TheAISContext()->Display (myAISPoint, Standard_True);
-  }
-
-  // Il n'a pas d'arguments
-  else
+  TCollection_AsciiString aName;
+  gp_Pnt aPnt (RealLast(), 0.0, 0.0);
+  bool is2d = false, isNoSel = false;
+  for (Standard_Integer anArgIter = 1; anArgIter < theArgNb; ++anArgIter)
   {
-    TopTools_ListOfShape aShapes;
-    ViewerTest::GetSelectedShapes (aShapes);
-
-    if (aShapes.Extent() != 1)
+    TCollection_AsciiString anArg (theArgVec[anArgIter]);
+    anArg.LowerCase();
+    if (anArg == "-2d")
+    {
+      is2d = true;
+    }
+    else if (anArg == "-nosel"
+          || anArg == "-noselection")
+    {
+      isNoSel = true;
+    }
+    else if (aName.IsEmpty())
+    {
+      aName = theArgVec[anArgIter];
+    }
+    else if (aPnt.X() == RealLast()
+          && anArgIter + 1 < theArgNb
+          && Draw::ParseReal (theArgVec[anArgIter + 0], aPnt.ChangeCoord().ChangeCoord (1))
+          && Draw::ParseReal (theArgVec[anArgIter + 1], aPnt.ChangeCoord().ChangeCoord (2)))
+    {
+      if (anArgIter + 2 < theArgNb
+       && TCollection_AsciiString (theArgVec[anArgIter + 2]) != "-2d"
+       && Draw::ParseReal (theArgVec[anArgIter + 2], aPnt.ChangeCoord().ChangeCoord (3)))
+      {
+        anArgIter += 2;
+      }
+      else
+      {
+        anArgIter += 1;
+      }
+    }
+    else
     {
-      Message::SendFail() << "Error: Wrong number of selected shapes.\n"
-                          << "\tYou should select one edge or vertex.";
+      Message::SendFail() << "Syntax error at argument '" << anArg << "'\n";
       return 1;
     }
+  }
 
-    const TopoDS_Shape& aShapeA = aShapes.First();
-
-    if (aShapeA.ShapeType()==TopAbs_VERTEX )
+  if (aPnt.X() == RealLast())
+  {
+    TopTools_ListOfShape aShapes;
+    ViewerTest::GetSelectedShapes (aShapes);
+    TopoDS_Shape aShapeA;
+    if (aShapes.Extent() == 1)
     {
-      gp_Pnt A=BRep_Tool::Pnt(TopoDS::Vertex(aShapeA ) );
-      Handle(Geom_CartesianPoint) myGeomPoint= new Geom_CartesianPoint (A );
-      Handle(AIS_Point)  myAISPoint = new AIS_Point  (myGeomPoint );
-      GetMapOfAIS().Bind(myAISPoint,name);
-      TheAISContext()->Display (myAISPoint, Standard_True);
+      aShapeA = aShapes.First();
     }
-    else
+    switch (!aShapeA.IsNull() ? aShapeA.ShapeType() : TopAbs_SHAPE)
     {
-      TopoDS_Edge myEdge=TopoDS::Edge(aShapeA);
-      TopoDS_Vertex myVertexA,myVertexB;
-      TopExp::Vertices (myEdge ,myVertexA ,myVertexB );
-      gp_Pnt A=BRep_Tool::Pnt(myVertexA );
-      gp_Pnt B=BRep_Tool::Pnt(myVertexB );
-      // M est le milieu de [AB]
-      Handle(Geom_CartesianPoint) myGeomPointM= new Geom_CartesianPoint ( (A.X()+B.X())/2  , (A.Y()+B.Y())/2  , (A.Z()+B.Z())/2  );
-      Handle(AIS_Point)  myAISPointM = new AIS_Point  (myGeomPointM );
-      GetMapOfAIS().Bind(myAISPointM,name);
-      TheAISContext()->Display (myAISPointM, Standard_True);
+      case TopAbs_VERTEX:
+      {
+        aPnt = BRep_Tool::Pnt (TopoDS::Vertex (aShapeA));
+        break;
+      }
+      case TopAbs_EDGE: // edge middle point
+      {
+        const TopoDS_Edge& anEdge = TopoDS::Edge (aShapeA);
+        TopoDS_Vertex aVertPair[2];
+        TopExp::Vertices (anEdge, aVertPair[0], aVertPair[1]);
+        const gp_Pnt A = BRep_Tool::Pnt (aVertPair[0]);
+        const gp_Pnt B = BRep_Tool::Pnt (aVertPair[1]);
+        aPnt = (A.XYZ() + B.XYZ()) / 2;
+        break;
+      }
+      default:
+      {
+        Message::SendFail() << "Error: Wrong number of selected shapes.\n"
+                            << "\tYou should select one edge or vertex.";
+        return 1;
+      }
     }
+  }
 
+  if (is2d)
+  {
+    aPnt.SetY (-aPnt.Y());
+  }
+  Handle(Geom_CartesianPoint ) aGeomPoint = new Geom_CartesianPoint (aPnt);
+  Handle(AIS_Point) aPointPrs = new AIS_Point (aGeomPoint);
+  if (is2d)
+  {
+    aPointPrs->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER));
+    aPointPrs->SetZLayer (Graphic3d_ZLayerId_TopOSD);
+  }
+  ViewerTest::Display (aName, aPointPrs);
+  if (isNoSel)
+  {
+    ViewerTest::GetAISContext()->Deactivate (aPointPrs);
   }
   return 0;
-
 }
 
 //==============================================================================
@@ -6448,9 +6482,11 @@ void ViewerTest::ObjectCommands(Draw_Interpretor& theCommands)
     __FILE__,VAxisBuilder,group);
 
   theCommands.Add("vpoint",
-    "vpoint  PointName [Xa] [Ya] [Za] "
-    "\n\t\t: Creates a point from coordinates. If the values are not defined,"
-    "\n\t\t: a point is created by interactive selection of a vertice or an edge (in the center of the edge).",
+    "vpoint name [X Y [Z]] [-2d] [-nosel]"
+    "\n\t\t: Creates a point from coordinates."
+    "\n\t\t: If the values are not defined, a point is created from selected vertex or edge (center)."
+    "\n\t\t:  -2d    defines on-screen 2D point from top-left window corner"
+    "\n\t\t:  -nosel creates non-selectable presentation",
     __FILE__,VPointBuilder,group);
 
   theCommands.Add("vplane",
diff --git a/tests/bugs/vis/bug31412 b/tests/bugs/vis/bug31412
new file mode 100644 (file)
index 0000000..f581d90
--- /dev/null
@@ -0,0 +1,35 @@
+puts "============="
+puts "0031412: Visualization - entity behind is returned as topmost at the edges"
+puts "============="
+
+pload VISUALIZATION
+box b1 250 250 900 1000 1000 200
+box b2 1000 1000 1000
+vdisplay -dispMode 1 b1 b2
+vaspects b1 -setWidth 3 -setColor ORANGE
+vaspects b2 -setWidth 3 -setColor MATRABLUE
+vrenderparams -shadingModel UNLIT
+
+vviewparams -proj 0 0 1
+vfit
+vmoveto 325 100
+if { [vreadpixel 325 400 rgb name] == "CYAN" } { puts "ERROR: back box is misdetected" }
+vpoint p1 -2d -nosel 325 100
+vdump $imagedir/${casename}_proj_Z.png
+vremove p1
+
+vviewparams -proj 0 1 0
+vfit
+vmoveto 200 58
+if { [vreadpixel 400 58 rgb name] == "CYAN" } { puts "ERROR: back box is misdetected" }
+vpoint p2 -2d -nosel 200 58
+vdump $imagedir/${casename}_proj_Y.png
+vremove p2
+
+vviewparams -proj 1 0 0
+vfit
+vmoveto 200 58
+if { [vreadpixel 50 58 rgb name] == "CYAN" } { puts "ERROR: back box is misdetected" }
+vpoint p3 -2d -nosel 200 58
+vdump $imagedir/${casename}_proj_X.png
+vremove p3
index 22f53c0..4cbd69d 100644 (file)
@@ -28,7 +28,7 @@ vfit
 
 vmanipulator m1 -attach b1 -adjustPosition 1 -adjustSize 1 -enableModes 1 -zoomable 1
 
-set mouse_pick_1 {84 135}
+set mouse_pick_1 {84 133}
 set mouse_pick_2 {29 103}
 set mouse_pick_3 {29 103}
 set mouse_drag_3 {121 126}
@@ -60,9 +60,9 @@ vfit
 
 vmanipulator m2 -attach b2 -adjustPosition 1 -adjustSize 0 -enableModes 1 -zoomable 0 -size 40
 
-set mouse_pick_1 {341 283}
-set mouse_pick_2 {277 246}
-set mouse_pick_3 {277 246}
+set mouse_pick_1 {341 282}
+set mouse_pick_2 {277 245}
+set mouse_pick_3 {277 245}
 set mouse_drag_3 {210 210}
 
 # pick at default zoom