From 0461e7fd03d0042559db8fc630771dfa93ee9d86 Mon Sep 17 00:00:00 2001 From: osa Date: Tue, 18 May 2021 10:07:59 +0300 Subject: [PATCH] 0032338: Visualization - provide straightforward interface for ray-picking --- src/AIS/AIS_ViewController.cxx | 25 + src/AIS/AIS_ViewController.hxx | 12 + src/BVH/BVH_Tools.hxx | 2 +- src/SelectMgr/FILES | 2 + src/SelectMgr/SelectMgr_AxisIntersector.cxx | 541 ++++++++++++++++++ src/SelectMgr/SelectMgr_AxisIntersector.hxx | 147 +++++ .../SelectMgr_SelectingVolumeManager.cxx | 16 + .../SelectMgr_SelectingVolumeManager.hxx | 3 + src/SelectMgr/SelectMgr_ViewerSelector3d.cxx | 16 + src/SelectMgr/SelectMgr_ViewerSelector3d.hxx | 6 + src/ViewerTest/ViewerTest_ViewerCommands.cxx | 207 +++++++ tests/vselect/axis/A1 | 35 ++ 12 files changed, 1011 insertions(+), 1 deletion(-) create mode 100644 src/SelectMgr/SelectMgr_AxisIntersector.cxx create mode 100644 src/SelectMgr/SelectMgr_AxisIntersector.hxx create mode 100644 tests/vselect/axis/A1 diff --git a/src/AIS/AIS_ViewController.cxx b/src/AIS/AIS_ViewController.cxx index f93aeb93a8..63ab92ebfd 100644 --- a/src/AIS/AIS_ViewController.cxx +++ b/src/AIS/AIS_ViewController.cxx @@ -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 : diff --git a/src/AIS/AIS_ViewController.hxx b/src/AIS/AIS_ViewController.hxx index 982f222235..eefabc6cee 100644 --- a/src/AIS/AIS_ViewController.hxx +++ b/src/AIS/AIS_ViewController.hxx @@ -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, diff --git a/src/BVH/BVH_Tools.hxx b/src/BVH/BVH_Tools.hxx index 847d904a56..b90034778b 100644 --- a/src/BVH/BVH_Tools.hxx +++ b/src/BVH/BVH_Tools.hxx @@ -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::min)() : (std::numeric_limits::max)(); aNodeMax[i] = (theBoxCMax[i] - theRayOrigin[i]) < 0 ? (std::numeric_limits::min)() : (std::numeric_limits::max)(); diff --git a/src/SelectMgr/FILES b/src/SelectMgr/FILES index 8b457da1d6..82bc6096f0 100755 --- a/src/SelectMgr/FILES +++ b/src/SelectMgr/FILES @@ -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 index 0000000000..e7c8d38c6a --- /dev/null +++ b/src/SelectMgr/SelectMgr_AxisIntersector.cxx @@ -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 + +#include +#include +#include +#include +#include + +// ======================================================================= +// 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 aRay(SelectMgr_Vec3(anAxisLoc.X(), anAxisLoc.Y(), anAxisLoc.Z()), + SelectMgr_Vec3(anAxisDir.X(), anAxisDir.Y(), anAxisDir.Z())); + if (!BVH_Tools::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 index 0000000000..69cf55cabd --- /dev/null +++ b/src/SelectMgr/SelectMgr_AxisIntersector.hxx @@ -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 + +#include + +//! 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 diff --git a/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx b/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx index b7ef91848b..b9130f5419 100644 --- a/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx +++ b/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -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 : diff --git a/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx b/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx index 76f2d04e34..2d0b687b66 100644 --- a/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx +++ b/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx @@ -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); diff --git a/src/SelectMgr/SelectMgr_ViewerSelector3d.cxx b/src/SelectMgr/SelectMgr_ViewerSelector3d.cxx index 94bf57236e..94c40d478a 100644 --- a/src/SelectMgr/SelectMgr_ViewerSelector3d.cxx +++ b/src/SelectMgr/SelectMgr_ViewerSelector3d.cxx @@ -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. diff --git a/src/SelectMgr/SelectMgr_ViewerSelector3d.hxx b/src/SelectMgr/SelectMgr_ViewerSelector3d.hxx index 2aa5b38819..4bd237286f 100644 --- a/src/SelectMgr/SelectMgr_ViewerSelector3d.hxx +++ b/src/SelectMgr/SelectMgr_ViewerSelector3d.hxx @@ -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. diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index 82160b8bf9..18c1b45054 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -92,6 +96,7 @@ #include #include #include +#include #include @@ -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 aPoints; + NCollection_Sequence aNormals; + NCollection_Sequence 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::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::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 index 0000000000..da551995d2 --- /dev/null +++ b/tests/vselect/axis/A1 @@ -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