&& !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 :
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,
{
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)();
SelectMgr_AndFilter.hxx
SelectMgr_AndOrFilter.cxx
SelectMgr_AndOrFilter.hxx
+SelectMgr_AxisIntersector.cxx
+SelectMgr_AxisIntersector.hxx
SelectMgr_BaseIntersector.cxx
SelectMgr_BaseIntersector.hxx
SelectMgr_BaseFrustum.cxx
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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
#include <SelectMgr_SelectingVolumeManager.hxx>
#include <Graphic3d_SequenceOfHClipPlane.hxx>
+#include <SelectMgr_AxisIntersector.hxx>
#include <SelectMgr_RectangularFrustum.hxx>
#include <SelectMgr_TriangularFrustumSet.hxx>
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 :
//! 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);
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.
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.
#include <AIS_AnimationCamera.hxx>
#include <AIS_AnimationObject.hxx>
+#include <AIS_Axis.hxx>
#include <AIS_CameraFrustum.hxx>
#include <AIS_ColorScale.hxx>
#include <AIS_InteractiveContext.hxx>
#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>
#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>
#include <V3d_SpotLight.hxx>
#include <V3d_Trihedron.hxx>
#include <V3d_Viewer.hxx>
+#include <UnitsAPI.hxx>
#include <tcl.h>
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.
"\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]]"
--- /dev/null
+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