]> OCCT Git - occt.git/commitdiff
0032338: Visualization - provide straightforward interface for ray-picking
authorosa <osa@opencascade.com>
Tue, 18 May 2021 07:07:59 +0000 (10:07 +0300)
committerbugmaster <bugmaster@opencascade.com>
Sat, 22 May 2021 07:36:39 +0000 (10:36 +0300)
12 files changed:
src/AIS/AIS_ViewController.cxx
src/AIS/AIS_ViewController.hxx
src/BVH/BVH_Tools.hxx
src/SelectMgr/FILES
src/SelectMgr/SelectMgr_AxisIntersector.cxx [new file with mode: 0644]
src/SelectMgr/SelectMgr_AxisIntersector.hxx [new file with mode: 0644]
src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx
src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx
src/SelectMgr/SelectMgr_ViewerSelector3d.cxx
src/SelectMgr/SelectMgr_ViewerSelector3d.hxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/vselect/axis/A1 [new file with mode: 0644]

index f93aeb93a8de4c6b18fe0fc98ae65e866e7f4b49..63ab92ebfd4e91853790de4b026a7a83e13941dc 100644 (file)
@@ -1729,6 +1729,31 @@ bool AIS_ViewController::PickPoint (gp_Pnt& thePnt,
       && !Precision::IsInfinite (thePnt.Z());
 }
 
+// =======================================================================
+// function : PickAxis
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::PickAxis (gp_Pnt& theTopPnt,
+                                   const Handle(AIS_InteractiveContext)& theCtx,
+                                   const Handle(V3d_View)& theView,
+                                   const gp_Ax1& theAxis)
+{
+  ResetPreviousMoveTo();
+
+  const Handle(StdSelect_ViewerSelector3d)& aSelector = theCtx->MainSelector();
+  aSelector->Pick (theAxis, theView);
+  if (aSelector->NbPicked() < 1)
+  {
+    return false;
+  }
+
+  const SelectMgr_SortCriterion& aPickedData = aSelector->PickedData (1);
+  theTopPnt = aPickedData.Point;
+  return !Precision::IsInfinite (theTopPnt.X())
+      && !Precision::IsInfinite (theTopPnt.Y())
+      && !Precision::IsInfinite (theTopPnt.Z());
+}
+
 // =======================================================================
 // function : GravityPoint
 // purpose  :
index 982f222235f8bfd1668d3a6858c5d91eb5e1986b..eefabc6cee928a4b9aeab0540c12e558c98ab45c 100644 (file)
@@ -487,6 +487,18 @@ public:
                                           const Graphic3d_Vec2i& theCursor,
                                           bool theToStickToPickRay);
 
+  //! Pick closest point by axis.
+  //! This method is expected to be called from rendering thread.
+  //! @param theTopPnt [out] result point
+  //! @param theCtx    [in] interactive context
+  //! @param theView   [in] active view
+  //! @param theAxis   [in] selection axis
+  //! @return TRUE if result has been found
+  Standard_EXPORT virtual bool PickAxis (gp_Pnt& theTopPnt,
+                                         const Handle(AIS_InteractiveContext)& theCtx,
+                                         const Handle(V3d_View)& theView,
+                                         const gp_Ax1& theAxis);
+
   //! Compute rotation gravity center point depending on rotation mode.
   //! This method is expected to be called from rendering thread.
   Standard_EXPORT virtual gp_Pnt GravityPoint (const Handle(AIS_InteractiveContext)& theCtx,
index 847d904a5687f0718f8d12c7a80ae736c970ee13..b90034778b7b52726ac5a7b2f52692d64d6bb9d9 100644 (file)
@@ -247,7 +247,7 @@ public: //! @name Ray-Box Intersection
     {
       if (theRayDirection[i] == 0)
       {
-        aNodeMin[i] = (theBoxCMin[i] - theRayOrigin[i]) < 0 ?
+        aNodeMin[i] = (theBoxCMin[i] - theRayOrigin[i]) <= 0 ?
                        (std::numeric_limits<T>::min)() : (std::numeric_limits<T>::max)();
         aNodeMax[i] = (theBoxCMax[i] - theRayOrigin[i]) < 0 ?
                        (std::numeric_limits<T>::min)() : (std::numeric_limits<T>::max)();
index 8b457da1d6b20bf58c0f0efbfea29f5ea486f9f4..82bc6096f0034254079cc1a627b9bb2441f3c087 100755 (executable)
@@ -4,6 +4,8 @@ SelectMgr_AndFilter.cxx
 SelectMgr_AndFilter.hxx
 SelectMgr_AndOrFilter.cxx
 SelectMgr_AndOrFilter.hxx
+SelectMgr_AxisIntersector.cxx
+SelectMgr_AxisIntersector.hxx
 SelectMgr_BaseIntersector.cxx
 SelectMgr_BaseIntersector.hxx
 SelectMgr_BaseFrustum.cxx
diff --git a/src/SelectMgr/SelectMgr_AxisIntersector.cxx b/src/SelectMgr/SelectMgr_AxisIntersector.cxx
new file mode 100644 (file)
index 0000000..e7c8d38
--- /dev/null
@@ -0,0 +1,541 @@
+// Copyright (c) 2021 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <SelectMgr_AxisIntersector.hxx>
+
+#include <Bnd_Range.hxx>
+#include <BVH_Tools.hxx>
+#include <Precision.hxx>
+#include <SelectBasics_PickResult.hxx>
+#include <SelectMgr_ViewClipRange.hxx>
+
+// =======================================================================
+// function : Constructor
+// purpose  :
+// =======================================================================
+SelectMgr_AxisIntersector::SelectMgr_AxisIntersector()
+{
+}
+
+// =======================================================================
+// function : Init
+// purpose  :
+// =======================================================================
+void SelectMgr_AxisIntersector::Init (const gp_Ax1& theAxis)
+{
+  mySelectionType = SelectMgr_SelectionType_Point;
+  myAxis = theAxis;
+}
+
+// =======================================================================
+// function : Build
+// purpose  :
+// =======================================================================
+void SelectMgr_AxisIntersector::Build()
+{
+}
+
+// =======================================================================
+// function : ScaleAndTransform
+// purpose  :
+// =======================================================================
+Handle(SelectMgr_BaseIntersector) SelectMgr_AxisIntersector::ScaleAndTransform (const Standard_Integer theScaleFactor,
+                                                                                const gp_GTrsf& theTrsf,
+                                                                                const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::ScaleAndTransform() should be called after selection axis initialization");
+
+  (void )theScaleFactor;
+  (void )theBuilder;
+  if (theTrsf.Form() == gp_Identity)
+  {
+    return new SelectMgr_AxisIntersector();
+  }
+
+  gp_Pnt aTransformedLoc = myAxis.Location();
+  theTrsf.Transforms (aTransformedLoc.ChangeCoord());
+  gp_XYZ aTransformedDir = myAxis.Direction().XYZ();
+  gp_GTrsf aTrsf = theTrsf;
+  aTrsf.SetTranslationPart (gp_XYZ(0., 0., 0.));
+  aTrsf.Transforms (aTransformedDir);
+
+  Handle(SelectMgr_AxisIntersector) aRes = new SelectMgr_AxisIntersector();
+  aRes->myAxis = gp_Ax1(aTransformedLoc, gp_Dir(aTransformedDir));
+  aRes->mySelectionType = mySelectionType;
+  return aRes;
+}
+
+// =======================================================================
+// function : hasIntersection
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::hasIntersection (const SelectMgr_Vec3& theBoxMin,
+                                                             const SelectMgr_Vec3& theBoxMax,
+                                                             Standard_Real& theTimeEnter,
+                                                             Standard_Real& theTimeLeave) const
+{
+  const gp_Pnt& anAxisLoc = myAxis.Location();
+  const gp_Dir& anAxisDir = myAxis.Direction();
+  BVH_Ray<Standard_Real, 3> aRay(SelectMgr_Vec3(anAxisLoc.X(), anAxisLoc.Y(), anAxisLoc.Z()),
+                                 SelectMgr_Vec3(anAxisDir.X(), anAxisDir.Y(), anAxisDir.Z()));
+  if (!BVH_Tools<Standard_Real, 3>::RayBoxIntersection (aRay, theBoxMin, theBoxMax, theTimeEnter, theTimeLeave))
+  {
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : hasIntersection
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::hasIntersection (const gp_Pnt& thePnt,
+                                                             Standard_Real& theDepth) const
+{
+  const gp_Pnt& anAxisLoc = myAxis.Location();
+  const gp_Dir& anAxisDir = myAxis.Direction();
+
+  // Check that vectors are co-directed (thePnt lies on this axis)
+  gp_Dir aDirToPnt(thePnt.XYZ() - anAxisLoc.XYZ());
+  if (!anAxisDir.IsEqual (aDirToPnt, Precision::Angular()))
+  {
+    return Standard_False;
+  }
+  theDepth = anAxisLoc.Distance (thePnt);
+  return Standard_True;
+}
+
+// =======================================================================
+// function : raySegmentDistance
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::raySegmentDistance (const gp_Pnt& theSegPnt1,
+                                                                const gp_Pnt& theSegPnt2,
+                                                                SelectBasics_PickResult& thePickResult) const
+{
+  gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
+  gp_XYZ aV = myAxis.Direction().XYZ();
+  gp_XYZ aW = theSegPnt1.XYZ() - myAxis.Location().XYZ();
+
+  gp_XYZ anUVNormVec = aV.Crossed (anU);
+  gp_XYZ anUWNormVec = aW.Crossed (anU);
+  if (anUVNormVec.Modulus() <= Precision::Confusion() ||
+      anUWNormVec.Modulus() <= Precision::Confusion())
+  {
+    // Lines have no intersection
+    thePickResult.Invalidate();
+    return false;
+  }
+
+  Standard_Real aParam = anUWNormVec.Dot (anUVNormVec) / anUVNormVec.SquareModulus();
+  if (aParam < 0.0)
+  {
+    // Intersection is out of axis start point
+    thePickResult.Invalidate();
+    return false;
+  }
+
+  gp_XYZ anIntersectPnt = myAxis.Location().XYZ() + aV * aParam;
+  if ((anIntersectPnt - theSegPnt1.XYZ()).SquareModulus() +
+      (anIntersectPnt - theSegPnt2.XYZ()).SquareModulus() >
+       anU.SquareModulus() + Precision::Confusion())
+  {
+    // Intersection point doesn't lie on the segment
+    thePickResult.Invalidate();
+    return false;
+  }
+
+  thePickResult.SetDepth (myAxis.Location().Distance (anIntersectPnt));
+  thePickResult.SetPickedPoint (anIntersectPnt);
+  return true;
+}
+
+// =======================================================================
+// function : rayPlaneIntersection
+// purpose  :
+// =======================================================================
+bool SelectMgr_AxisIntersector::rayPlaneIntersection (const gp_Vec& thePlane,
+                                                      const gp_Pnt& thePntOnPlane,
+                                                      SelectBasics_PickResult& thePickResult) const
+{
+  gp_XYZ anU = myAxis.Direction().XYZ();
+  gp_XYZ aW = myAxis.Location().XYZ() - thePntOnPlane.XYZ();
+  Standard_Real aD = thePlane.Dot (anU);
+  Standard_Real aN = -thePlane.Dot (aW);
+
+  if (Abs (aD) < Precision::Confusion())
+  {
+    thePickResult.Invalidate();
+    return false;
+  }
+
+  Standard_Real aParam = aN / aD;
+  if (aParam < 0.0)
+  {
+    thePickResult.Invalidate();
+    return false;
+  }
+
+  gp_Pnt aClosestPnt = myAxis.Location().XYZ() + anU * aParam;
+  thePickResult.SetDepth (myAxis.Location().Distance (aClosestPnt));
+  thePickResult.SetPickedPoint (aClosestPnt);
+  return true;
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const SelectMgr_Vec3& theBoxMin,
+                                                      const SelectMgr_Vec3& theBoxMax,
+                                                      Standard_Boolean*     theInside) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
+
+  (void )theInside;
+  Standard_Real aTimeEnter, aTimeLeave;
+  if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
+  {
+    return Standard_False;
+  }
+  if (theInside != NULL)
+  {
+    *theInside &= (aTimeEnter >= 0.0);
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const SelectMgr_Vec3& theBoxMin,
+                                                      const SelectMgr_Vec3& theBoxMax,
+                                                      const SelectMgr_ViewClipRange& theClipRange,
+                                                      SelectBasics_PickResult& thePickResult) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
+
+  Standard_Real aTimeEnter, aTimeLeave;
+  if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
+  {
+    return Standard_False;
+  }
+
+  Standard_Real aDepth = 0.0;
+  Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
+  aRange.GetMin (aDepth);
+
+  if (!theClipRange.GetNearestDepth (aRange, aDepth))
+  {
+    return Standard_False;
+  }
+
+  thePickResult.SetDepth (aDepth);
+
+  return Standard_True;
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt,
+                                                      const SelectMgr_ViewClipRange& theClipRange,
+                                                      SelectBasics_PickResult& thePickResult) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
+
+  Standard_Real aDepth = 0.0;
+  if (!hasIntersection (thePnt, aDepth))
+  {
+    return Standard_False;
+  }
+
+  thePickResult.SetDepth (aDepth);
+  thePickResult.SetPickedPoint (thePnt);
+
+  return !theClipRange.IsClipped (thePickResult.Depth());
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
+
+  Standard_Real aDepth = 0.0;
+  return hasIntersection (thePnt, aDepth);
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt1,
+                                                      const gp_Pnt& thePnt2,
+                                                      const SelectMgr_ViewClipRange& theClipRange,
+                                                      SelectBasics_PickResult& thePickResult) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
+
+  if (!raySegmentDistance (thePnt1, thePnt2, thePickResult))
+  {
+    return Standard_False;
+  }
+
+  return !theClipRange.IsClipped (thePickResult.Depth());
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const TColgp_Array1OfPnt& theArrayOfPnts,
+                                                      Select3D_TypeOfSensitivity theSensType,
+                                                      const SelectMgr_ViewClipRange& theClipRange,
+                                                      SelectBasics_PickResult& thePickResult) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
+
+  if (theSensType == Select3D_TOS_BOUNDARY)
+  {
+    Standard_Integer aMatchingSegmentsNb = -1;
+    SelectBasics_PickResult aPickResult;
+    thePickResult.Invalidate();
+    const Standard_Integer aLower  = theArrayOfPnts.Lower();
+    const Standard_Integer anUpper = theArrayOfPnts.Upper();
+    for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
+    {
+      const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
+      const gp_Pnt& aEndPnt   = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
+      if (raySegmentDistance (aStartPnt, aEndPnt, aPickResult))
+      {
+        aMatchingSegmentsNb++;
+        thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
+      }
+    }
+
+    if (aMatchingSegmentsNb == -1)
+    {
+      return Standard_False;
+    }
+  }
+  else if (theSensType == Select3D_TOS_INTERIOR)
+  {
+    Standard_Integer aStartIdx = theArrayOfPnts.Lower();
+    const gp_XYZ& aPnt1 = theArrayOfPnts.Value (aStartIdx).XYZ();
+    const gp_XYZ& aPnt2 = theArrayOfPnts.Value (aStartIdx + 1).XYZ();
+    const gp_XYZ& aPnt3 = theArrayOfPnts.Value (aStartIdx + 2).XYZ();
+    const gp_XYZ aVec1 = aPnt1 - aPnt2;
+    const gp_XYZ aVec2 = aPnt3 - aPnt2;
+    gp_Vec aPolyNorm = aVec2.Crossed (aVec1);
+    if (aPolyNorm.Magnitude() <= Precision::Confusion())
+    {
+      // treat degenerated polygon as point
+      return Overlaps (theArrayOfPnts.First(), theClipRange, thePickResult);
+    }
+    else if (!rayPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
+    {
+      return Standard_False;
+    }
+  }
+
+  return !theClipRange.IsClipped (thePickResult.Depth());
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt1,
+                                                      const gp_Pnt& thePnt2,
+                                                      const gp_Pnt& thePnt3,
+                                                      Select3D_TypeOfSensitivity theSensType,
+                                                      const SelectMgr_ViewClipRange& theClipRange,
+                                                      SelectBasics_PickResult& thePickResult) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
+
+  if (theSensType == Select3D_TOS_BOUNDARY)
+  {
+    const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
+    const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
+    return Overlaps (aPntsArray, Select3D_TOS_BOUNDARY, theClipRange, thePickResult);
+  }
+  else if (theSensType == Select3D_TOS_INTERIOR)
+  {
+    gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
+    const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
+                                 thePnt3.XYZ() - thePnt2.XYZ(),
+                                 thePnt1.XYZ() - thePnt3.XYZ() };
+    aTriangleNormal = aTrEdges[2].Crossed (aTrEdges[0]);
+         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));
+    }
+
+    const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
+    const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myAxis.Direction().XYZ());
+    if (Abs (anAlpha) < 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)
+      {
+        const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
+        const gp_Pnt& anEndPnt  = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
+        if (raySegmentDistance (aStartPnt, anEndPnt, aPickResult))
+        {
+          thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
+        }
+      }
+      thePickResult.SetSurfaceNormal (aTriangleNormal);
+      return !theClipRange.IsClipped (thePickResult.Depth());
+    }
+
+    // check if intersection point belongs to triangle's interior part
+    const gp_XYZ anEdge = (thePnt1.XYZ() - myAxis.Location().XYZ()) * (1.0 / anAlpha);
+
+    const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
+    const gp_XYZ aVec = myAxis.Direction().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 = myAxis.Location().XYZ() + myAxis.Direction().XYZ() * aTime;
+    if (isInterior)
+    {
+      thePickResult.SetDepth (myAxis.Location().Distance (aPtOnPlane));
+      thePickResult.SetPickedPoint (aPtOnPlane);
+      thePickResult.SetSurfaceNormal (aTriangleNormal);
+      return !theClipRange.IsClipped (thePickResult.Depth());
+    }
+
+    Standard_Real aMinDist = RealLast();
+    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 (aDist < aMinDist)
+      {
+        aMinDist = aDist;
+        aNearestEdgeIdx1 = anEdgeIdx;
+      }
+    }
+    Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
+    const gp_Vec aVec12 (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]);
+    if (aVec12.SquareMagnitude() > gp::Resolution()
+     && myAxis.Direction().IsParallel (aVec12, Precision::Angular()))
+    {
+      aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
+    }
+    if (raySegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult))
+    {
+      thePickResult.SetSurfaceNormal (aTriangleNormal);
+    }
+  }
+
+  return !theClipRange.IsClipped (thePickResult.Depth());
+}
+
+//=======================================================================
+// function : GetNearPnt
+// purpose  :
+//=======================================================================
+const gp_Pnt& SelectMgr_AxisIntersector::GetNearPnt() const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::GetNearPnt() should be called after selection axis initialization");
+
+  return myAxis.Location();
+}
+
+//=======================================================================
+// function : GetFarPnt
+// purpose  :
+//=======================================================================
+const gp_Pnt& SelectMgr_AxisIntersector::GetFarPnt() const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::GetFarPnt() should be called after selection axis initialization");
+
+  static gp_Pnt anInfPnt(RealLast(), RealLast(), RealLast());
+  return anInfPnt;
+}
+
+//=======================================================================
+// function : GetViewRayDirection
+// purpose  :
+//=======================================================================
+const gp_Dir& SelectMgr_AxisIntersector::GetViewRayDirection() const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::GetViewRayDirection() should be called after selection axis initialization");
+
+  return myAxis.Direction();
+}
+
+// =======================================================================
+// function : DistToGeometryCenter
+// purpose  :
+// =======================================================================
+Standard_Real SelectMgr_AxisIntersector::DistToGeometryCenter (const gp_Pnt& theCOG) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::DistToGeometryCenter() should be called after selection axis initialization");
+
+  return theCOG.Distance (myAxis.Location());
+}
+
+// =======================================================================
+// function : DetectedPoint
+// purpose  :
+// =======================================================================
+gp_Pnt SelectMgr_AxisIntersector::DetectedPoint (const Standard_Real theDepth) const
+{
+  Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
+    "Error! SelectMgr_AxisIntersector::DetectedPoint() should be called after selection axis initialization");
+
+  return myAxis.Location().XYZ() + myAxis.Direction().XYZ() * theDepth;
+}
+
+//=======================================================================
+//function : DumpJson
+//purpose  : 
+//=======================================================================
+void SelectMgr_AxisIntersector::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
+{
+  OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_AxisIntersector)
+  OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_BaseIntersector)
+
+  OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myAxis)
+}
diff --git a/src/SelectMgr/SelectMgr_AxisIntersector.hxx b/src/SelectMgr/SelectMgr_AxisIntersector.hxx
new file mode 100644 (file)
index 0000000..69cf55c
--- /dev/null
@@ -0,0 +1,147 @@
+// Copyright (c) 2021 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _SelectMgr_AxisIntersector_HeaderFile
+#define _SelectMgr_AxisIntersector_HeaderFile
+
+#include <SelectMgr_BaseIntersector.hxx>
+
+#include <gp_Ax1.hxx>
+
+//! This class contains representation of selecting axis, created in case of point selection
+//! and algorithms for overlap detection between this axis and sensitive entities.
+class SelectMgr_AxisIntersector : public SelectMgr_BaseIntersector
+{
+public:
+
+  //! Empty constructor
+  Standard_EXPORT SelectMgr_AxisIntersector();
+
+  //! Destructor
+  virtual ~SelectMgr_AxisIntersector() {}
+
+  //! Initializes selecting axis according to the input one
+  Standard_EXPORT void Init (const gp_Ax1& theAxis);
+
+  //! Builds axis according to internal parameters.
+  //! NOTE: it should be called after Init() method
+  Standard_EXPORT virtual void Build() Standard_OVERRIDE;
+
+  //! IMPORTANT: Scaling doesn't make sense for this intersector.
+  //! Returns a copy of the intersector transformed using the matrix given.
+  //! Builder is an optional argument that represents corresponding settings for re-constructing transformed
+  //! frustum from scratch. Can be null if reconstruction is not expected furthermore.
+  Standard_EXPORT virtual Handle(SelectMgr_BaseIntersector)
+    ScaleAndTransform (const Standard_Integer theScaleFactor,
+                       const gp_GTrsf& theTrsf,
+                       const Handle(SelectMgr_FrustumBuilder)& theBuilder = Handle(SelectMgr_FrustumBuilder)()) const Standard_OVERRIDE;
+
+public:
+
+  //! Intersection test between defined axis and given axis-aligned box
+  Standard_EXPORT virtual Standard_Boolean Overlaps (const SelectMgr_Vec3& theBoxMin,
+                                                     const SelectMgr_Vec3& theBoxMax,
+                                                     const SelectMgr_ViewClipRange& theClipRange,
+                                                     SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
+
+  //! Returns true if selecting axis intersects axis-aligned bounding box
+  //! with minimum corner at point theMinPt and maximum at point theMaxPt
+  Standard_EXPORT virtual Standard_Boolean Overlaps (const SelectMgr_Vec3& theBoxMin,
+                                                     const SelectMgr_Vec3& theBoxMax,
+                                                     Standard_Boolean*     theInside) const Standard_OVERRIDE;
+
+  //! Intersection test between defined axis and given point
+  Standard_EXPORT virtual Standard_Boolean Overlaps (const gp_Pnt& thePnt,
+                                                     const SelectMgr_ViewClipRange& theClipRange,
+                                                     SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
+
+  //! Intersection test between defined axis and given point
+  Standard_EXPORT virtual Standard_Boolean Overlaps (const gp_Pnt& thePnt) const Standard_OVERRIDE;
+
+  //! Intersection test between defined axis and given ordered set of points,
+  //! representing line segments. The test may be considered of interior part or
+  //! boundary line defined by segments depending on given sensitivity type
+  Standard_EXPORT virtual Standard_Boolean Overlaps (const TColgp_Array1OfPnt& theArrayOfPnts,
+                                                     Select3D_TypeOfSensitivity theSensType,
+                                                     const SelectMgr_ViewClipRange& theClipRange,
+                                                     SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
+
+  //! Checks if selecting axis intersects line segment
+  Standard_EXPORT virtual Standard_Boolean Overlaps (const gp_Pnt& thePnt1,
+                                                     const gp_Pnt& thePnt2,
+                                                     const SelectMgr_ViewClipRange& theClipRange,
+                                                     SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
+
+  //! Intersection test between defined axis and given triangle. The test may
+  //! be considered of interior part or boundary line defined by triangle vertices
+  //! depending on given sensitivity type
+  Standard_EXPORT virtual Standard_Boolean Overlaps (const gp_Pnt& thePnt1,
+                                                     const gp_Pnt& thePnt2,
+                                                     const gp_Pnt& thePnt3,
+                                                     Select3D_TypeOfSensitivity theSensType,
+                                                     const SelectMgr_ViewClipRange& theClipRange,
+                                                     SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
+
+public:
+
+  //! Measures distance between start axis point and given point theCOG.
+  Standard_EXPORT virtual Standard_Real DistToGeometryCenter (const gp_Pnt& theCOG) const Standard_OVERRIDE;
+
+  //! Calculates the point on a axis ray that was detected during the run of selection algo by given depth
+  Standard_EXPORT virtual gp_Pnt DetectedPoint (const Standard_Real theDepth) const Standard_OVERRIDE;
+
+  //! Returns near point along axis.
+  Standard_EXPORT virtual const gp_Pnt& GetNearPnt() const Standard_OVERRIDE;
+
+  //! Returns far point along axis (infinite).
+  Standard_EXPORT virtual const gp_Pnt& GetFarPnt() const Standard_OVERRIDE;
+
+  //! Returns axis direction.
+  Standard_EXPORT virtual const gp_Dir& GetViewRayDirection() const Standard_OVERRIDE;
+
+  //! Dumps the content of me into the stream
+  Standard_EXPORT virtual void DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth = -1) const Standard_OVERRIDE;
+
+protected:
+
+  //! Returns true if selecting axis intersects axis-aligned bounding box
+  //! with minimum corner at point theBoxMin and maximum at point theBoxMax.
+  //! Also returns enter and leave time of axis-box intersection.
+  Standard_EXPORT Standard_Boolean hasIntersection (const SelectMgr_Vec3& theBoxMin,
+                                                    const SelectMgr_Vec3& theBoxMax,
+                                                    Standard_Real& theTimeEnter,
+                                                    Standard_Real& theTimeLeave) const;
+
+  //! Returns true if selecting axis intersects point.
+  //! Also returns time of axis-point intersection.
+  Standard_EXPORT Standard_Boolean hasIntersection (const gp_Pnt& thePnt,
+                                                    Standard_Real& theDepth) const;
+
+  //! Returns true if selecting axis intersects segment.
+  //! Also saves time of axis-segment intersection and intersection point as pick result.
+  Standard_EXPORT Standard_Boolean raySegmentDistance (const gp_Pnt& theSegPnt1,
+                                                       const gp_Pnt& theSegPnt2,
+                                                       SelectBasics_PickResult& thePickResult) const;
+
+  //! Returns true if selecting axis intersects plane.
+  //! Also saves time of axis-plane intersection and intersection point as pick result.
+  Standard_EXPORT Standard_Boolean rayPlaneIntersection (const gp_Vec& thePlane,
+                                                         const gp_Pnt& thePntOnPlane,
+                                                         SelectBasics_PickResult& thePickResult) const;
+private:
+
+  gp_Ax1 myAxis;
+
+};
+
+#endif // _SelectMgr_AxisIntersector_HeaderFile
index b7ef91848b89399320e633cb752801f6340bc102..b9130f541916e545f961586ae9bdf5db9094f99f 100644 (file)
@@ -16,6 +16,7 @@
 #include <SelectMgr_SelectingVolumeManager.hxx>
 
 #include <Graphic3d_SequenceOfHClipPlane.hxx>
+#include <SelectMgr_AxisIntersector.hxx>
 #include <SelectMgr_RectangularFrustum.hxx>
 #include <SelectMgr_TriangularFrustumSet.hxx>
 
@@ -261,6 +262,21 @@ void SelectMgr_SelectingVolumeManager::InitPolylineSelectingVolume (const TColgp
   aPolylineVolume->SetAllowOverlapDetection (IsOverlapAllowed());
 }
 
+//=======================================================================
+// function : InitAxisSelectingVolume
+// purpose  :
+//=======================================================================
+void SelectMgr_SelectingVolumeManager::InitAxisSelectingVolume (const gp_Ax1& theAxis)
+{
+  Handle(SelectMgr_AxisIntersector) anAxisVolume = Handle(SelectMgr_AxisIntersector)::DownCast(myActiveSelectingVolume);
+  if (anAxisVolume.IsNull())
+  {
+    anAxisVolume = new SelectMgr_AxisIntersector();
+  }
+  anAxisVolume->Init (theAxis);
+  myActiveSelectingVolume = anAxisVolume;
+}
+
 //=======================================================================
 // function : InitSelectingVolume
 // purpose  :
index 76f2d04e34e2f3e8aedbf122c684c06f5f51fa5b..2d0b687b666c86340923339a8fd16c48eefe0b6d 100644 (file)
@@ -51,6 +51,9 @@ public:
   //! Creates, initializes and activates set of triangular selecting frustums for polyline selection
   Standard_EXPORT void InitPolylineSelectingVolume (const TColgp_Array1OfPnt2d& thePoints);
 
+  //! Creates and activates axis selector for point selection
+  Standard_EXPORT void InitAxisSelectingVolume (const gp_Ax1& theAxis);
+
   //! Sets as active the custom selecting volume
   Standard_EXPORT void InitSelectingVolume (const Handle(SelectMgr_BaseIntersector)& theVolume);
 
index 94bf57236ee70993e00519a4eb66d6428dbf2c70..94c40d478a8e101a3772c578e8f0a50dfac71e67 100644 (file)
@@ -119,6 +119,22 @@ void SelectMgr_ViewerSelector3d::Pick (const TColgp_Array1OfPnt2d& thePolyline,
   TraverseSensitives();
 }
 
+//=======================================================================
+// Function: Pick
+// Purpose :
+//=======================================================================
+void SelectMgr_ViewerSelector3d::Pick (const gp_Ax1& theAxis,
+                                       const Handle(V3d_View)& theView)
+{
+  updateZLayers (theView);
+
+  mySelectingVolumeMgr.InitAxisSelectingVolume (theAxis);
+  mySelectingVolumeMgr.BuildSelectingVolume();
+  mySelectingVolumeMgr.SetViewClipping (theView->ClipPlanes(), Handle(Graphic3d_SequenceOfHClipPlane)(), NULL);
+
+  TraverseSensitives();
+}
+
 //=======================================================================
 // Function: DisplaySensitive.
 // Purpose : Display active primitives.
index 2aa5b38819c0463d5ffa6beccfed2294b43d9f89..4bd237286f30b06278e74d1fed094bfe3641200d 100644 (file)
@@ -54,6 +54,12 @@ public:
   Standard_EXPORT void Pick (const TColgp_Array1OfPnt2d& thePolyline,
                              const Handle(V3d_View)& theView);
 
+  //! Picks the sensitive entity according to the input axis.
+  //! This is geometric intersection 3D objects by axis
+  //! (camera parameters are ignored and objects with transform persistance are skipped).
+  Standard_EXPORT void Pick (const gp_Ax1& theAxis,
+                             const Handle(V3d_View)& theView);
+
   //! Dump of detection results into image.
   //! This method performs axis picking for each pixel in the image
   //! and generates a color depending on picking results and selection image type.
index 82160b8bf9d59da87d103c2b23bdef02be9d17f5..18c1b450546c01e32ed5889e6481e16282dc9032 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <AIS_AnimationCamera.hxx>
 #include <AIS_AnimationObject.hxx>
+#include <AIS_Axis.hxx>
 #include <AIS_CameraFrustum.hxx>
 #include <AIS_ColorScale.hxx>
 #include <AIS_InteractiveContext.hxx>
@@ -31,6 +32,7 @@
 #include <AIS_Manipulator.hxx>
 #include <AIS_ViewCube.hxx>
 #include <AIS_Shape.hxx>
+#include <AIS_Point.hxx>
 #include <Aspect_DisplayConnection.hxx>
 #include <Aspect_Grid.hxx>
 #include <Aspect_TypeOfLine.hxx>
@@ -41,6 +43,8 @@
 #include <gp_Dir.hxx>
 #include <gp_Pln.hxx>
 #include <gp_Pnt.hxx>
+#include <Geom_Axis2Placement.hxx>
+#include <Geom_CartesianPoint.hxx>
 #include <Graphic3d_ArrayOfPolylines.hxx>
 #include <Graphic3d_AspectFillArea3d.hxx>
 #include <Graphic3d_AspectMarker3d.hxx>
@@ -92,6 +96,7 @@
 #include <V3d_SpotLight.hxx>
 #include <V3d_Trihedron.hxx>
 #include <V3d_Viewer.hxx>
+#include <UnitsAPI.hxx>
 
 #include <tcl.h>
 
@@ -7077,6 +7082,201 @@ static Standard_Integer VMoveTo (Draw_Interpretor& theDI,
   return 0;
 }
 
+//=======================================================================
+//function : VSelectByAxis
+//purpose  :
+//=======================================================================
+static Standard_Integer VSelectByAxis (Draw_Interpretor& theDI,
+                                       Standard_Integer theNbArgs,
+                                       const char**     theArgVec)
+{
+  const Handle(AIS_InteractiveContext)& aContext = ViewerTest::GetAISContext();
+  const Handle(V3d_View)&               aView    = ViewerTest::CurrentView();
+  if (aContext.IsNull())
+  {
+    Message::SendFail ("Error: no active viewer");
+    return 1;
+  }
+
+  TCollection_AsciiString aName;
+  gp_XYZ anAxisLocation(RealLast(), RealLast(), RealLast());
+  gp_XYZ anAxisDirection(RealLast(), RealLast(), RealLast());
+  Standard_Boolean isOnlyTop = true;
+  Standard_Boolean toShowNormal = false;
+  for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+  {
+    TCollection_AsciiString anArgStr (theArgVec[anArgIter]);
+    anArgStr.LowerCase();
+    if (anArgStr == "-display")
+    {
+      if (anArgIter + 1 >= theNbArgs)
+      {
+        Message::SendFail() << "Syntax error at argument '" << anArgStr << "'";
+        return 1;
+      }
+      aName = theArgVec[++anArgIter];
+    }
+    else if (anArgStr == "-onlytop")
+    {
+      isOnlyTop = true;
+      if (anArgIter + 1 < theNbArgs
+        && Draw::ParseOnOff (theArgVec[anArgIter + 1], isOnlyTop))
+      {
+        ++anArgIter;
+      }
+    }
+    else if (anArgStr == "-shownormal")
+    {
+      toShowNormal = true;
+      if (anArgIter + 1 < theNbArgs
+        && Draw::ParseOnOff (theArgVec[anArgIter + 1], toShowNormal))
+      {
+        ++anArgIter;
+      }
+    }
+    else if (Precision::IsInfinite(anAxisLocation.X())
+          && anArgStr.IsRealValue())
+    {
+      anAxisLocation.SetX (anArgStr.RealValue());
+    }
+    else if (Precision::IsInfinite(anAxisLocation.Y())
+          && anArgStr.IsRealValue())
+    {
+      anAxisLocation.SetY (anArgStr.RealValue());
+    }
+    else if (Precision::IsInfinite(anAxisLocation.Z())
+          && anArgStr.IsRealValue())
+    {
+      anAxisLocation.SetZ (anArgStr.RealValue());
+    }
+    else if (Precision::IsInfinite(anAxisDirection.X())
+          && anArgStr.IsRealValue())
+    {
+      anAxisDirection.SetX (anArgStr.RealValue());
+    }
+    else if (Precision::IsInfinite(anAxisDirection.Y())
+          && anArgStr.IsRealValue())
+    {
+      anAxisDirection.SetY (anArgStr.RealValue());
+    }
+    else if (Precision::IsInfinite(anAxisDirection.Z())
+          && anArgStr.IsRealValue())
+    {
+      anAxisDirection.SetZ (anArgStr.RealValue());
+    }
+    else
+    {
+      Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'";
+      return 1;
+    }
+  }
+
+  if (Precision::IsInfinite (anAxisLocation.X()) ||
+      Precision::IsInfinite (anAxisLocation.Y()) ||
+      Precision::IsInfinite (anAxisLocation.Z()) ||
+      Precision::IsInfinite (anAxisDirection.X()) ||
+      Precision::IsInfinite (anAxisDirection.Y()) ||
+      Precision::IsInfinite (anAxisDirection.Z()))
+  {
+    Message::SendFail() << "Invalid axis location and direction";
+    return 1;
+  }
+
+  gp_Ax1 anAxis(anAxisLocation, anAxisDirection);
+  gp_Pnt aTopPnt;
+  if (!ViewerTest::CurrentEventManager()->PickAxis (aTopPnt, aContext, aView, anAxis))
+  {
+    theDI << "There are no any intersections with this axis.";
+    return 0;
+  }
+  NCollection_Sequence<gp_Pnt> aPoints;
+  NCollection_Sequence<Graphic3d_Vec3> aNormals;
+  NCollection_Sequence<Standard_Real> aNormalLengths;
+  for (Standard_Integer aPickIter = 1; aPickIter <= aContext->MainSelector()->NbPicked(); ++aPickIter)
+  {
+    const SelectMgr_SortCriterion& aPickedData = aContext->MainSelector()->PickedData (aPickIter);
+    aPoints.Append (aPickedData.Point);
+    aNormals.Append (aPickedData.Normal);
+    Standard_Real aNormalLength = 1.0;
+    if (!aPickedData.Entity.IsNull())
+    {
+      aNormalLength = 0.2 * aPickedData.Entity->BoundingBox().Size().maxComp();
+    }
+    aNormalLengths.Append (aNormalLength);
+  }
+  if (!aName.IsEmpty())
+  {
+    Standard_Boolean wasAuto = aContext->GetAutoActivateSelection();
+    aContext->SetAutoActivateSelection (false);
+
+    // Display axis
+    Quantity_Color anAxisColor = Quantity_NOC_GREEN;
+    Handle(Geom_Axis2Placement) anAx2Axis =
+      new Geom_Axis2Placement (gp_Ax2(anAxisLocation, anAxisDirection));
+    Handle(AIS_Axis) anAISAxis = new AIS_Axis (anAx2Axis, AIS_TOAX_ZAxis);
+    const Handle(Prs3d_Drawer)& anAxisDrawer = anAISAxis->Attributes();
+    anAxisDrawer->SetOwnDatumAspects();
+    Standard_Real aLength = UnitsAPI::AnyToLS (250000., "mm");
+    anAxisDrawer->DatumAspect()->SetAxisLength (aLength, aLength, aLength);
+    anAxisDrawer->DatumAspect()->SetDrawLabels (false);
+    anAxisDrawer->DatumAspect()->SetDrawArrows (true);
+    anAISAxis->SetColor (Quantity_NOC_GREEN);
+    anAISAxis->SetAxis2Placement (anAx2Axis, AIS_TOAX_ZAxis); // This is workaround to update axis length
+    ViewerTest::Display (TCollection_AsciiString(aName) + "_axis", anAISAxis, false);
+
+    // Display axis start point
+    Handle(AIS_Point) anAISStartPnt = new AIS_Point (new Geom_CartesianPoint (anAxisLocation));
+    anAISStartPnt->SetMarker (Aspect_TOM_O);
+    anAISStartPnt->SetColor (anAxisColor);
+    ViewerTest::Display (TCollection_AsciiString(aName) + "_start", anAISStartPnt, false);
+
+    Standard_Integer anIndex = 0;
+    for (NCollection_Sequence<gp_Pnt>::Iterator aPntIter(aPoints); aPntIter.More(); aPntIter.Next(), anIndex++)
+    {
+      const gp_Pnt& aPoint = aPntIter.Value();
+
+      // Display normals in intersection points
+      if (toShowNormal)
+      {
+        const Graphic3d_Vec3& aNormal = aNormals.Value (anIndex + 1);
+        Standard_Real aNormalLength = aNormalLengths.Value (anIndex + 1);
+        if (aNormal.SquareModulus() > ShortRealEpsilon())
+        {
+          Handle(Geom_Axis2Placement) anAx2Normal =
+            new Geom_Axis2Placement(gp_Ax2(aPoint, gp_Dir((Standard_Real )aNormal.x(), (Standard_Real )aNormal.y(), (Standard_Real )aNormal.z())));
+          Handle(AIS_Axis) anAISNormal = new AIS_Axis (anAx2Normal, AIS_TOAX_ZAxis);
+          const Handle(Prs3d_Drawer)& aNormalDrawer = anAISNormal->Attributes();
+          aNormalDrawer->SetOwnDatumAspects();
+          aNormalDrawer->DatumAspect()->SetAxisLength (aNormalLength, aNormalLength, aNormalLength);
+          aNormalDrawer->DatumAspect()->SetDrawLabels (false);
+          aNormalDrawer->DatumAspect()->SetDrawArrows (true);
+          anAISNormal->SetColor (Quantity_NOC_BLUE);
+          anAISNormal->SetAxis2Placement (anAx2Normal, AIS_TOAX_ZAxis); // This is workaround to update axis length
+          anAISNormal->SetInfiniteState (false);
+          ViewerTest::Display (TCollection_AsciiString(aName) + "_normal_" + anIndex, anAISNormal, false);
+        }
+      }
+
+      // Display intersection points
+      Handle(Geom_CartesianPoint) anIntersectPnt = new Geom_CartesianPoint (aPoint);
+      Handle(AIS_Point) anAISIntersectPoint = new AIS_Point (anIntersectPnt);
+      anAISIntersectPoint->SetMarker (Aspect_TOM_PLUS);
+      anAISIntersectPoint->SetColor (Quantity_NOC_RED);
+      ViewerTest::Display (TCollection_AsciiString(aName) + "_intersect_" + anIndex, anAISIntersectPoint, true);
+    }
+
+    aContext->SetAutoActivateSelection (wasAuto);
+  }
+
+  Standard_Integer anIndex = 0;
+  for (NCollection_Sequence<gp_Pnt>::Iterator anIter(aPoints); anIter.More(); anIter.Next(), anIndex++)
+  {
+    const gp_Pnt& aPnt = anIter.Value();
+    theDI << aPnt.X() << " " << aPnt.Y() << " " << aPnt.Z() << "\n";
+  }
+  return 0;
+}
+
 namespace
 {
   //! Global map storing all animations registered in ViewerTest.
@@ -14528,6 +14728,13 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n\t\t: Emulates cursor movement to pixel position (x,y)."
     "\n\t\t:   -reset resets current highlighting",
     __FILE__, VMoveTo, group);
+  theCommands.Add ("vselaxis",
+              "vselaxis x y z dx dy dz [-onlyTop 0|1] [-display Name] [-showNormal 0|1]"
+    "\n\t\t: Provides intersection by given axis and print result intersection points"
+    "\n\t\t:   -onlyTop       switches On/Off mode to find only top point or all"
+    "\n\t\t:   -display Name  displays intersecting axis and result intersection points for debug goals"
+    "\n\t\t:   -showNormal    adds displaying of normal in intersection point or not",
+    __FILE__, VSelectByAxis, group);
   theCommands.Add ("vviewparams",
               "vviewparams [-args] [-scale [s]]"
       "\n\t\t:             [-eye [x y z]] [-at [x y z]] [-up [x y z]]"
diff --git a/tests/vselect/axis/A1 b/tests/vselect/axis/A1
new file mode 100644 (file)
index 0000000..da55199
--- /dev/null
@@ -0,0 +1,35 @@
+puts "========"
+puts "0032338: Visualization - provide straightforward interface for ray-picking"
+puts "========"
+puts ""
+
+pload MODELING VISUALIZATION
+psphere s 2
+box b -min -1 -1 2 -max 1 1 4
+vclear
+vinit View1
+vdisplay -dispMode 1 s
+vsettransparency s 0.5
+vdisplay -dispMode 1 b
+vsettransparency b 0.5
+vaxis a 1 -2 2 1 2 2
+vpoint p 0 0 5
+vdisplay p
+vaspects p -markerType O
+vaxo
+vfit
+if { [vselaxis -4 0 6 0 0 -1 -display sel_a0] != "There are no any intersections with this axis." } { puts "Error: there should be no any intersections" }
+
+set pointlist1 [vselaxis 0 0 6 0 0 -1 -display sel_a2 -onlytop 0]
+regexp {([-0-9.+eE]+ [-0-9.+eE]+ [-0-9.+eE]+)\s([-0-9.+eE]+ [-0-9.+eE]+ [-0-9.+eE]+)\s([-0-9.+eE]+ [-0-9.+eE]+ [-0-9.+eE]+)} ${pointlist1} full p1 p2 p3
+checkpoint "point1" $p1 {0 0 5} 0.001
+checkpoint "point2" $p2 {0 0 4} 0.001
+checkpoint "point3" $p3 {0 0 2} 0.001
+
+set pointlist2 [vselaxis 1 0 6 0 0 -1 -display sel_a1 -onlytop 0 -shownormal]
+regexp {([-0-9.+eE]+ [-0-9.+eE]+ [-0-9.+eE]+)\s([-0-9.+eE]+ [-0-9.+eE]+ [-0-9.+eE]+)\s([-0-9.+eE]+ [-0-9.+eE]+ [-0-9.+eE]+)} ${pointlist2} full p1 p2 p3
+checkpoint "point1" $p1 {1 0 4} 0.0001
+checkpoint "point2" $p2 {1 0 2} 0.0001
+checkpoint "point3" $p3 {1 0 1.7410396881859338} 0.0001
+
+vdump ${imagedir}/${casename}.png