1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-14 13:30:48 +03:00

0032281: Visualization - add Select3D_SensitiveCylinder

- implemented Select3D_SensitiveCylinder class performing an analytical intersection with an untriangulated cone/cylinder
- added tests
This commit is contained in:
mkrylova
2021-07-05 15:06:50 +03:00
committed by bugmaster
parent 8a77384b0c
commit 9dd8af261f
32 changed files with 1568 additions and 4 deletions

View File

@@ -547,6 +547,66 @@ Standard_Boolean SelectMgr_AxisIntersector::OverlapsSphere (const gp_Pnt& theCen
return Standard_True;
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_AxisIntersector::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
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 = 0.0, aTimeLeave = 0.0;
gp_Trsf aTrsfInv = theTrsf.Inverted();
gp_Pnt aLoc = myAxis.Location() .Transformed (aTrsfInv);
gp_Dir aRayDir = myAxis.Direction().Transformed (aTrsfInv);
if (!RayCylinderIntersection (theBottomRad, theTopRad, theHeight, aLoc, aRayDir, aTimeEnter, aTimeLeave))
{
return false;
}
Standard_Real aDepth = 0.0;
Bnd_Range aRange (Max (aTimeEnter, 0.0), aTimeLeave);
aRange.GetMin (aDepth);
if (!theClipRange.GetNearestDepth (aRange, aDepth))
{
return false;
}
thePickResult.SetDepth (aDepth);
return true;
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_AxisIntersector::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside) const
{
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point,
"Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
Standard_Real aTimeEnter = 0.0, aTimeLeave = 0.0;
gp_Trsf aTrsfInv = theTrsf.Inverted();
gp_Pnt aLoc = myAxis.Location() .Transformed (aTrsfInv);
gp_Dir aRayDir = myAxis.Direction().Transformed (aTrsfInv);
if (!RayCylinderIntersection (theBottomRad, theTopRad, theHeight, aLoc, aRayDir, aTimeEnter, aTimeLeave))
{
return false;
}
if (theInside != NULL)
{
*theInside &= (aTimeEnter >= 0.0);
}
return true;
}
//=======================================================================
// function : GetNearPnt
// purpose :

View File

@@ -111,6 +111,23 @@ public:
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside = NULL) const Standard_OVERRIDE;
public:
//! Measures distance between start axis point and given point theCOG.

View File

@@ -14,6 +14,9 @@
#include <SelectMgr_BaseIntersector.hxx>
#include <Graphic3d_Camera.hxx>
#include <gp_Ax3.hxx>
#include <algorithm>
IMPLEMENT_STANDARD_RTTIEXT(SelectMgr_BaseIntersector, Standard_Transient)
@@ -164,6 +167,132 @@ Standard_Boolean SelectMgr_BaseIntersector::RaySphereIntersection (const gp_Pnt&
return Standard_True;
}
//=======================================================================
// function : RayCylinderIntersection
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_BaseIntersector::RayCylinderIntersection (const Standard_Real theBottomRadius,
const Standard_Real theTopRadius,
const Standard_Real theHeight,
const gp_Pnt& theLoc,
const gp_Dir& theRayDir,
Standard_Real& theTimeEnter,
Standard_Real& theTimeLeave) const
{
Standard_Integer aNbIntersections = 0;
Standard_Real anIntersections[4] = { RealLast(), RealLast(), RealLast(), RealLast() };
//NCollection_Vector<Standard_Real> anIntersections; // vector for all intersections
// Check intersections with end faces
// point of intersection theRayDir and z = 0
if (theRayDir.Z() != 0)
{
const Standard_Real aTime1 = (0 - theLoc.Z()) / theRayDir.Z();
const Standard_Real aX1 = theLoc.X() + theRayDir.X() * aTime1;
const Standard_Real anY1 = theLoc.Y() + theRayDir.Y() * aTime1;
if (aX1 * aX1 + anY1 * anY1 <= theBottomRadius * theBottomRadius)
{
anIntersections[aNbIntersections++] = aTime1;
}
// point of intersection theRayDir and z = theHeight
const Standard_Real aTime2 = (theHeight - theLoc.Z()) / theRayDir.Z();
const Standard_Real aX2 = theLoc.X() + theRayDir.X() * aTime2;
const Standard_Real anY2 = theLoc.Y() + theRayDir.Y() * aTime2;
if (aX2 * aX2 + anY2 * anY2 <= theTopRadius * theTopRadius)
{
anIntersections[aNbIntersections++] = aTime2;
}
}
// ray intersection with cone / truncated cone
if (theTopRadius != theBottomRadius)
{
const Standard_Real aTriangleHeight = Min (theBottomRadius, theTopRadius) * theHeight /
(Abs (theBottomRadius - theTopRadius));
gp_Ax3 aSystem;
if (theBottomRadius > theTopRadius)
{
aSystem.SetLocation (gp_Pnt (0, 0, theHeight + aTriangleHeight));
aSystem.SetDirection (-gp::DZ());
}
else
{
aSystem.SetLocation (gp_Pnt (0, 0, -aTriangleHeight));
aSystem.SetDirection (gp::DZ());
}
gp_Trsf aTrsfCone;
aTrsfCone.SetTransformation (gp_Ax3(), aSystem);
const gp_Pnt aPnt (theLoc.Transformed (aTrsfCone));
const gp_Dir aDir (theRayDir.Transformed (aTrsfCone));
const Standard_Real aMaxRad = Max (theBottomRadius, theTopRadius);
const Standard_Real aConeHeight = theHeight + aTriangleHeight;
// solving quadratic equation anA * T^2 + 2 * aK * T + aC = 0
const Standard_Real anA = aDir.X() * aDir.X() / (aMaxRad * aMaxRad)
+ aDir.Y() * aDir.Y() / (aMaxRad * aMaxRad)
- aDir.Z() * aDir.Z() / (aConeHeight * aConeHeight);
const Standard_Real aK = aDir.X() * aPnt.X() / (aMaxRad * aMaxRad)
+ aDir.Y() * aPnt.Y() / (aMaxRad * aMaxRad)
- aDir.Z() * aPnt.Z() / (aConeHeight * aConeHeight);
const Standard_Real aC = aPnt.X() * aPnt.X() / (aMaxRad * aMaxRad)
+ aPnt.Y() * aPnt.Y() / (aMaxRad * aMaxRad)
- aPnt.Z() * aPnt.Z() / (aConeHeight * aConeHeight);
Standard_Real aDiscr = aK * aK - anA * aC;
if (aDiscr > 0)
{
const Standard_Real aTimeEnterCone = (-aK - Sqrt (aDiscr)) / anA;
const Standard_Real aTimeLeaveCone = (-aK + Sqrt (aDiscr)) / anA;
const Standard_Real aZFromRoot1 = aPnt.Z() + aTimeEnterCone * aDir.Z();
const Standard_Real aZFromRoot2 = aPnt.Z() + aTimeLeaveCone * aDir.Z();
if (aZFromRoot1 > aTriangleHeight && aZFromRoot1 < aConeHeight)
{
anIntersections[aNbIntersections++] = aTimeEnterCone;
}
if (aZFromRoot2 > aTriangleHeight && aZFromRoot2 < aConeHeight)
{
anIntersections[aNbIntersections++] = aTimeLeaveCone;
}
}
}
else // ray intersection with cylinder
{
const gp_Pnt2d aLoc2d (theLoc.X(), theLoc.Y());
const gp_Vec2d aRayDir2d (theRayDir.X(), theRayDir.Y());
// solving quadratic equation anA * T^2 + 2 * aK * T + aC = 0
const Standard_Real anA = aRayDir2d.Dot (aRayDir2d);
const Standard_Real aK = aLoc2d.XY().Dot (aRayDir2d.XY());
const Standard_Real aC = aLoc2d.XY().Dot (aLoc2d.XY()) - theTopRadius * theTopRadius;
const Standard_Real aDiscr = aK * aK - anA * aC;
if (aDiscr > 0)
{
const Standard_Real aRoot1 = (-aK + Sqrt (aDiscr)) / anA;
const Standard_Real aRoot2 = (-aK - Sqrt (aDiscr)) / anA;
const Standard_Real aZFromRoot1 = theLoc.Z() + aRoot1 * theRayDir.Z();
const Standard_Real aZFromRoot2 = theLoc.Z() + aRoot2 * theRayDir.Z();
if (aZFromRoot1 > 0 && aZFromRoot1 < theHeight)
{
anIntersections[aNbIntersections++] = aRoot1;
}
if (aZFromRoot2 > 0 && aZFromRoot2 < theHeight)
{
anIntersections[aNbIntersections++] = aRoot2;
}
}
}
if (aNbIntersections == 0)
{
return false;
}
std::sort (anIntersections, anIntersections + aNbIntersections);
theTimeEnter = anIntersections[0];
if (aNbIntersections > 1)
{
theTimeLeave = anIntersections[1];
}
return true;
}
//=======================================================================
// function : DistToGeometryCenter
// purpose :

View File

@@ -181,6 +181,23 @@ public:
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const = 0;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const = 0;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside = NULL) const = 0;
public:
//! Measures distance between 3d projection of user-picked
@@ -206,6 +223,16 @@ public:
Standard_Real& theTimeEnter,
Standard_Real& theTimeLeave) const;
//! Checks whether the ray that starts at the point theLoc and directs with the direction theRayDir intersects
//! with the cylinder (or cone) with radiuses theBottomRad and theTopRad and height theHeights
Standard_EXPORT virtual Standard_Boolean RayCylinderIntersection (const Standard_Real theBottomRadius,
const Standard_Real theTopRadius,
const Standard_Real theHeight,
const gp_Pnt& theLoc,
const gp_Dir& theRayDir,
Standard_Real& theTimeEnter,
Standard_Real& theTimeLeave) const;
DEFINE_STANDARD_RTTIEXT(SelectMgr_BaseIntersector,Standard_Transient)
protected:

View File

@@ -93,8 +93,34 @@ protected:
const Standard_Real theRadius,
Standard_Boolean* theInside = NULL) const;
//! Intersection test between defined volume and given cylinder (or cone).
Standard_Boolean hasCylinderOverlap (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside = NULL) const;
//! Checking whether the point thePnt is inside the shape with borders theVertices.
//! thePnt and theVertices lie in the same plane.
Standard_Boolean IsDotInside (const gp_Pnt& thePnt,
const TColgp_Array1OfPnt& theVertices) const;
private:
//! Return true if one segment enclosed between the points thePnt1Seg1 and thePnt2Seg1
//! intersects another segment that enclosed between thePnt1Seg2 and thePnt2Seg2.
Standard_Boolean isSegmentsIntersect (const gp_Pnt& thePnt1Seg1,
const gp_Pnt& thePnt2Seg1,
const gp_Pnt& thePnt1Seg2,
const gp_Pnt& thePnt2Seg2) const;
//! Checking whether the borders theVertices of the shape intersect
//! the cylinder (or cone) end face with the center theCenter and radius theRadius
Standard_Boolean isIntersectCylinderEndFace (const Standard_Real theRad,
const gp_Pnt& theCenter,
const gp_Trsf& theTrsf,
const TColgp_Array1OfPnt& theVertices) const;
//! Checks if AABB and frustum are separated along the given axis
Standard_Boolean isSeparated (const SelectMgr_Vec3& theBoxMin,
const SelectMgr_Vec3& theBoxMax,

View File

@@ -13,6 +13,7 @@
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <gp_Pln.hxx>
#include <NCollection_Vector.hxx>
#include <Poly_Array1OfTriangle.hxx>
#include <Standard_Assert.hxx>
@@ -147,7 +148,6 @@ Standard_Boolean SelectMgr_Frustum<N>::hasBoxOverlap (const SelectMgr_Vec3& theM
}
const Standard_Integer anIncFactor = (Camera()->IsOrthographic() && N == 4) ? 2 : 1;
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < N + 1; aPlaneIdx += anIncFactor)
{
const gp_XYZ& aPlane = myPlanes[aPlaneIdx].XYZ();
@@ -207,7 +207,6 @@ template <int N>
Standard_Boolean SelectMgr_Frustum<N>::hasPointOverlap (const gp_Pnt& thePnt) const
{
const Standard_Integer anIncFactor = (Camera()->IsOrthographic() && N == 4) ? 2 : 1;
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < N + 1; aPlaneIdx += anIncFactor)
{
const Standard_Real aPointProj = myPlanes[aPlaneIdx].XYZ().Dot (thePnt.XYZ());
@@ -516,6 +515,259 @@ Standard_Boolean SelectMgr_Frustum<N>::hasSphereOverlap (const gp_Pnt& thePnt,
return IsBoundaryIntersectSphere (aCenterProj, theRadius, aNorm, aBoundaries, isBoundaryInside);
}
// =======================================================================
// function : IsDotInside
// purpose :
// =======================================================================
template<int N>
Standard_Boolean SelectMgr_Frustum<N>::IsDotInside (const gp_Pnt& thePnt,
const TColgp_Array1OfPnt& theVertices) const
{
Standard_Real anAngle = 0.0;
for (Standard_Integer aVertIdx = 0; aVertIdx < theVertices.Size(); aVertIdx++)
{
const gp_Pnt aVert1 = theVertices[aVertIdx];
const gp_Pnt aVert2 = (aVertIdx == (theVertices.Size() - 1) ? theVertices[0] : theVertices[aVertIdx + 1]);
const gp_Vec aVec1 (thePnt, aVert1);
const gp_Vec aVec2 (thePnt, aVert2);
anAngle += aVec1.Angle (aVec2);
}
if (Abs (anAngle - 2.0 * M_PI) < Precision::Angular())
{
return true;
}
return false;
}
// =======================================================================
// function : isSegmentsIntersect
// purpose :
// =======================================================================
template<int N>
Standard_Boolean SelectMgr_Frustum<N>::isSegmentsIntersect (const gp_Pnt& thePnt1Seg1,
const gp_Pnt& thePnt2Seg1,
const gp_Pnt& thePnt1Seg2,
const gp_Pnt& thePnt2Seg2) const
{
const gp_Mat aMatPln (thePnt2Seg1.X() - thePnt1Seg1.X(), thePnt2Seg1.Y() - thePnt1Seg1.Y(), thePnt2Seg1.Z() - thePnt1Seg1.Z(),
thePnt1Seg2.X() - thePnt1Seg1.X(), thePnt1Seg2.Y() - thePnt1Seg1.Y(), thePnt1Seg2.Z() - thePnt1Seg1.Z(),
thePnt2Seg2.X() - thePnt1Seg1.X(), thePnt2Seg2.Y() - thePnt1Seg1.Y(), thePnt2Seg2.Z() - thePnt1Seg1.Z());
if (Abs (aMatPln.Determinant()) > Precision::Confusion())
{
return false;
}
Standard_Real aFst[4] = {thePnt1Seg1.X(), thePnt2Seg1.X(), thePnt1Seg2.X(), thePnt2Seg2.X()};
Standard_Real aSnd[4] = {thePnt1Seg1.Y(), thePnt2Seg1.Y(), thePnt1Seg2.Y(), thePnt2Seg2.Y()};
if (aFst[0] == aFst[2] && aFst[1] == aFst[3])
{
aFst[0] = thePnt1Seg1.Z();
aFst[1] = thePnt2Seg1.Z();
aFst[2] = thePnt1Seg2.Z();
aFst[3] = thePnt2Seg2.Z();
}
if (aSnd[0] == aSnd[2]
&& aSnd[1] == aSnd[3])
{
aSnd[0] = thePnt1Seg1.Z();
aSnd[1] = thePnt2Seg1.Z();
aSnd[2] = thePnt1Seg2.Z();
aSnd[3] = thePnt2Seg2.Z();
}
const gp_Mat2d aMat (gp_XY (aFst[0] - aFst[1], aSnd[0] - aSnd[1]),
gp_XY (aFst[3] - aFst[2], aSnd[3] - aSnd[2]));
const gp_Mat2d aMatU (gp_XY (aFst[0] - aFst[2], aSnd[0] - aSnd[2]),
gp_XY (aFst[3] - aFst[2], aSnd[3] - aSnd[2]));
const gp_Mat2d aMatV (gp_XY (aFst[0] - aFst[1], aSnd[0] - aSnd[1]),
gp_XY (aFst[0] - aFst[2], aSnd[0] - aSnd[2]));
if (aMat.Determinant() == 0.0)
{
return false;
}
const Standard_Real anU = aMatU.Determinant() / aMat.Determinant();
const Standard_Real aV = aMatV.Determinant() / aMat.Determinant();
if (anU >= 0.0 && anU <= 1.0
&& aV >= 0.0 && aV <= 1.0)
{
return true;
}
return false;
}
// =======================================================================
// function : isIntersectCylinderEndFace
// purpose :
// =======================================================================
template<int N>
Standard_Boolean SelectMgr_Frustum<N>::isIntersectCylinderEndFace (const Standard_Real theRad,
const gp_Pnt& theCenter,
const gp_Trsf& theTrsf,
const TColgp_Array1OfPnt& theVertices) const
{
const gp_Trsf aTrsfInv = theTrsf.Inverted();
const gp_Dir aRayDir = gp_Dir (myEdgeDirs[N == 4 ? 4 : 0]).Transformed (aTrsfInv);
if (aRayDir.Z() == 0.0)
{
return false;
}
for (Standard_Integer anIdx = theVertices.Lower(); anIdx <= theVertices.Upper(); anIdx++)
{
const gp_Pnt aPntStart = theVertices.Value (anIdx).Transformed (aTrsfInv);
const gp_Pnt aPntFinish = anIdx == theVertices.Upper()
? theVertices.Value (theVertices.Lower()).Transformed (aTrsfInv)
: theVertices.Value (anIdx + 1).Transformed (aTrsfInv);
// Project points on the end face plane
const Standard_Real aParam1 = (theCenter.Z() - aPntStart.Z()) / aRayDir.Z();
const Standard_Real aX1 = aPntStart.X() + aRayDir.X() * aParam1;
const Standard_Real anY1 = aPntStart.Y() + aRayDir.Y() * aParam1;
const Standard_Real aParam2 = (theCenter.Z() - aPntFinish.Z()) / aRayDir.Z();
const Standard_Real aX2 = aPntFinish.X() + aRayDir.X() * aParam2;
const Standard_Real anY2 = aPntFinish.Y() + aRayDir.Y() * aParam2;
// Solving quadratic equation anA * T^2 + 2 * aK * T + aC = 0
const Standard_Real anA = (aX1 - aX2) * (aX1 - aX2) + (anY1 - anY2) * (anY1 - anY2);
const Standard_Real aK = aX1 * (aX2 - aX1) + anY1 * (anY2 - anY1);
const Standard_Real aC = aX1 * aX1 + anY1 * anY1 - theRad * theRad;
const Standard_Real aDiscr = aK * aK - anA * aC;
if (aDiscr >= 0.0)
{
const Standard_Real aT1 = (-aK + Sqrt (aDiscr)) / anA;
const Standard_Real aT2 = (-aK - Sqrt (aDiscr)) / anA;
if ((aT1 >= 0 && aT1 <= 1) || (aT2 >= 0 && aT2 <= 1))
{
return true;
}
}
}
return false;
}
// =======================================================================
// function : hasCylinderOverlap
// purpose :
// =======================================================================
template<int N>
Standard_Boolean SelectMgr_Frustum<N>::hasCylinderOverlap (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside) const
{
const gp_Dir aCylNorm (gp::DZ().Transformed (theTrsf));
const gp_Pnt aBottomCenter (gp::Origin().Transformed (theTrsf));
const gp_Pnt aTopCenter = aBottomCenter.XYZ() + aCylNorm.XYZ() * theHeight;
const gp_Dir aViewRayDir = gp_Dir (myEdgeDirs[N == 4 ? 4 : 0]);
const gp_Pln aPln (myVertices[0], aViewRayDir);
Standard_Real aCoefA, aCoefB, aCoefC, aCoefD;
aPln.Coefficients (aCoefA, aCoefB, aCoefC, aCoefD);
const Standard_Real aTBottom = -(aBottomCenter.XYZ().Dot (aViewRayDir.XYZ()) + aCoefD);
const gp_Pnt aBottomCenterProject (aCoefA * aTBottom + aBottomCenter.X(),
aCoefB * aTBottom + aBottomCenter.Y(),
aCoefC * aTBottom + aBottomCenter.Z());
const Standard_Real aTTop = -(aTopCenter.XYZ().Dot (aViewRayDir.XYZ()) + aCoefD);
const gp_Pnt aTopCenterProject (aCoefA * aTTop + aTopCenter.X(),
aCoefB * aTTop + aTopCenter.Y(),
aCoefC * aTTop + aTopCenter.Z());
gp_Vec aCylNormProject (0, 0, 0);
if (aTopCenterProject.Distance (aBottomCenterProject) > 0.0)
{
aCylNormProject = gp_Vec ((aTopCenterProject.XYZ() - aBottomCenterProject.XYZ())
/ aTopCenterProject.Distance (aBottomCenterProject));
}
gp_Pnt aPoints[6];
const gp_Dir aDirEndFaces = (aCylNorm.IsParallel (aViewRayDir, Precision::Angular()))
? gp::DY().Transformed (theTrsf)
: aCylNorm.Crossed (aViewRayDir);
const Standard_Real anAngle = aCylNorm.Angle (aViewRayDir);
aPoints[0] = aBottomCenterProject.XYZ() - aCylNormProject.XYZ() * theBottomRad * Abs (Cos (anAngle));
aPoints[1] = aBottomCenterProject.XYZ() + aDirEndFaces.XYZ() * theBottomRad;
aPoints[2] = aTopCenterProject.XYZ() + aDirEndFaces.XYZ() * theTopRad;
aPoints[3] = aTopCenterProject.XYZ() + aCylNormProject.XYZ() * theTopRad * Abs (Cos (anAngle));
aPoints[4] = aTopCenterProject.XYZ() - aDirEndFaces.XYZ() * theTopRad;
aPoints[5] = aBottomCenterProject.XYZ() - aDirEndFaces.XYZ() * theBottomRad;
const TColgp_Array1OfPnt aPointsArr (aPoints[0], 0, 5);
gp_Pnt aVerticesBuf[N];
TColgp_Array1OfPnt aVertices (aVerticesBuf[0], 0, N - 1);
const Standard_Integer anIncFactor = (Camera()->IsOrthographic() && N == 4) ? 2 : 1;
if (anIncFactor == 2)
{
const Standard_Integer anIndices[] = { 0, 2, 6, 4 };
for (Standard_Integer anIdx = 0; anIdx < N; anIdx++)
{
aVertices.SetValue (anIdx, myVertices[anIndices[anIdx]]);
}
}
else
{
for (Standard_Integer anIdx = 0; anIdx < N; anIdx++)
{
aVertices.SetValue (anIdx, myVertices[anIdx]);
}
}
for (Standard_Integer anIdx = 0; anIdx < N; anIdx++)
{
if ((aCylNormProject.Dot (aCylNormProject) == 0.0
&& aVertices.Value (anIdx).Distance (aPoints[0]) <= Max (theTopRad, theBottomRad))
|| IsDotInside (aVertices.Value (anIdx), aPointsArr))
{
if (theInside != NULL)
{
*theInside = false;
}
return true;
}
}
for (Standard_Integer anIdx = aVertices.Lower(); anIdx <= aVertices.Upper(); anIdx++)
{
const gp_Pnt aPnt1Seg = aVertices[anIdx];
const gp_Pnt aPnt2Seg = (anIdx == aVertices.Upper()) ? aVertices[aVertices.Lower()] : aVertices[anIdx + 1];
if (isSegmentsIntersect (aPoints[1], aPoints[2], aPnt1Seg, aPnt2Seg)
|| isSegmentsIntersect (aPoints[4], aPoints[5], aPnt1Seg, aPnt2Seg)
|| isSegmentsIntersect (aPoints[4], aPoints[2], aPnt1Seg, aPnt2Seg)
|| isSegmentsIntersect (aPoints[1], aPoints[5], aPnt1Seg, aPnt2Seg))
{
if (theInside != NULL)
{
*theInside = false;
}
return true;
}
}
if (isIntersectCylinderEndFace (theBottomRad, gp_Pnt (0, 0, 0), theTrsf, aVertices)
|| isIntersectCylinderEndFace (theTopRad, gp_Pnt (0, 0, theHeight), theTrsf, aVertices))
{
if (theInside != NULL)
{
*theInside = false;
}
return true;
}
bool isCylInsideRec = true;
for (int i = 0; i < 6; ++i)
{
isCylInsideRec &= IsDotInside (aPoints[i], aVertices);
}
if (theInside != NULL)
{
*theInside &= isCylInsideRec;
}
return isCylInsideRec;
}
//=======================================================================
//function : DumpJson
//purpose :

View File

@@ -740,6 +740,65 @@ Standard_Boolean SelectMgr_RectangularFrustum::OverlapsTriangle (const gp_Pnt& t
return !theClipRange.IsClipped (thePickResult.Depth());
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const
{
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
Standard_Real aTimeEnter = 0.0, aTimeLeave = 0.0;
const gp_Trsf aTrsfInv = theTrsf.Inverted();
const gp_Pnt aLoc = myNearPickedPnt.Transformed (aTrsfInv);
const gp_Dir aRayDir = myViewRayDir .Transformed (aTrsfInv);
if (!RayCylinderIntersection (theBottomRad, theTopRad, theHeight, aLoc, aRayDir, aTimeEnter, aTimeLeave))
{
return Standard_False;
}
thePickResult.SetDepth (aTimeEnter * myScale);
if (theClipRange.IsClipped (thePickResult.Depth()))
{
thePickResult.SetDepth (aTimeLeave * myScale);
}
const gp_Pnt aPntOnCylinder (aLoc.XYZ() + aRayDir.XYZ() * thePickResult.Depth());
if (Abs (aPntOnCylinder.Z()) < Precision::Confusion())
{
thePickResult.SetSurfaceNormal (-gp::DZ().Transformed (theTrsf));
}
else if (Abs (aPntOnCylinder.Z() - theHeight) < Precision::Confusion())
{
thePickResult.SetSurfaceNormal (gp::DZ().Transformed (theTrsf));
}
else
{
thePickResult.SetSurfaceNormal (gp_Vec (aPntOnCylinder.X(), aPntOnCylinder.Y(), 0.0).Transformed (theTrsf));
}
thePickResult.SetPickedPoint (aPntOnCylinder);
return !theClipRange.IsClipped (thePickResult.Depth());
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside) const
{
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
return hasCylinderOverlap (theBottomRad, theTopRad, theHeight, theTrsf, theInside);
}
// =======================================================================
// function : GetMousePosition
// purpose :

View File

@@ -144,6 +144,23 @@ public:
const Standard_Real theRadius,
Standard_Boolean* theInside) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside = NULL) const Standard_OVERRIDE;
//! Measures distance between 3d projection of user-picked
//! screen point and given point theCOG.
//! It makes sense only for frustums built on a single point.

View File

@@ -418,6 +418,40 @@ Standard_Boolean SelectMgr_SelectingVolumeManager::OverlapsSphere (const gp_Pnt&
return myActiveSelectingVolume->OverlapsSphere (theCenter, theRadius, theInside);
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_SelectingVolumeManager::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
SelectBasics_PickResult& thePickResult) const
{
if (myActiveSelectingVolume.IsNull())
{
return false;
}
return myActiveSelectingVolume->OverlapsCylinder (theBottomRad, theTopRad, theHeight, theTrsf, myViewClipRange, thePickResult);
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_SelectingVolumeManager::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside) const
{
if (myActiveSelectingVolume.IsNull())
{
return false;
}
return myActiveSelectingVolume->OverlapsCylinder (theBottomRad, theTopRad, theHeight, theTrsf, theInside);
}
//=======================================================================
// function : DistToGeometryCenter
// purpose : Measures distance between 3d projection of user-picked

View File

@@ -162,6 +162,22 @@ public:
const Standard_Real theRadius,
Standard_Boolean* theInside = NULL) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside = NULL) const Standard_OVERRIDE;
//! Measures distance between 3d projection of user-picked
//! screen point and given point theCOG
Standard_EXPORT virtual Standard_Real DistToGeometryCenter (const gp_Pnt& theCOG) const Standard_OVERRIDE;

View File

@@ -67,6 +67,56 @@ namespace
NCollection_DataMap<Handle(Select3D_SensitiveEntity), Quantity_Color> myMapEntityColors;
};
//! Help class for filling pixel with random color.
class GeneratedEntityTypeColorFiller : public SelectMgr_SelectionImageFiller
{
public:
GeneratedEntityTypeColorFiller (Image_PixMap& thePixMap,
SelectMgr_ViewerSelector* theSelector)
: SelectMgr_SelectionImageFiller (thePixMap, theSelector)
{
// generate per-entity colors in the order as they have been activated
for (SelectMgr_SelectableObjectSet::Iterator anObjIter (theSelector->SelectableObjects()); anObjIter.More(); anObjIter.Next())
{
const Handle(SelectMgr_SelectableObject)& anObj = anObjIter.Value();
for (SelectMgr_SequenceOfSelection::Iterator aSelIter (anObj->Selections()); aSelIter.More(); aSelIter.Next())
{
const Handle(SelectMgr_Selection)& aSel = aSelIter.Value();
for (NCollection_Vector<Handle(SelectMgr_SensitiveEntity)>::Iterator aSelEntIter (aSel->Entities()); aSelEntIter.More(); aSelEntIter.Next())
{
const Handle(SelectMgr_SensitiveEntity)& aSens = aSelEntIter.Value();
if (!myMapEntityColors.IsBound (aSens->BaseSensitive()->DynamicType()))
{
Quantity_Color aColor;
randomPastelColor (aColor);
myMapEntityColors.Bind (aSens->BaseSensitive()->DynamicType(), aColor);
}
}
}
}
}
virtual void Fill (const Standard_Integer theCol,
const Standard_Integer theRow,
const Standard_Integer thePicked) Standard_OVERRIDE
{
if (thePicked < 1
|| thePicked > myMainSel->NbPicked())
{
myImage->SetPixelColor (theCol, theRow, Quantity_Color(Quantity_NOC_BLACK));
return;
}
const Handle(Select3D_SensitiveEntity)& aPickedEntity = myMainSel->PickedEntity (thePicked);
Quantity_Color aColor (Quantity_NOC_BLACK);
myMapEntityColors.Find (aPickedEntity->DynamicType(), aColor);
myImage->SetPixelColor (theCol, theRow, aColor);
}
protected:
NCollection_DataMap<Handle(Standard_Type), Quantity_Color> myMapEntityColors;
};
//! Help class for filling pixel with normalized depth of ray.
class NormalizedDepthFiller : public SelectMgr_SelectionImageFiller
{
@@ -384,6 +434,10 @@ Handle(SelectMgr_SelectionImageFiller) SelectMgr_SelectionImageFiller::CreateFil
{
return new GeneratedEntityColorFiller (thePixMap, theSelector);
}
case StdSelect_TypeOfSelectionImage_ColoredEntityType:
{
return new GeneratedEntityTypeColorFiller (thePixMap, theSelector);
}
case StdSelect_TypeOfSelectionImage_ColoredOwner:
{
return new GeneratedOwnerColorFiller (thePixMap, theSelector);

View File

@@ -329,6 +329,36 @@ Standard_Boolean SelectMgr_TriangularFrustum::OverlapsSphere (const gp_Pnt& theC
return hasSphereOverlap (theCenter, theRadius);
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_TriangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const
{
(void)theClipRange;
(void)thePickResult;
return hasCylinderOverlap (theBottomRad, theTopRad, theHeight, theTrsf);
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_TriangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside) const
{
(void) theInside;
return hasCylinderOverlap (theBottomRad, theTopRad, theHeight, theTrsf);
}
// =======================================================================
// function : Clear
// purpose : Nullifies the handle for corresponding builder instance to prevent

View File

@@ -117,6 +117,23 @@ public: //! @name SAT Tests for different objects
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside = NULL) const Standard_OVERRIDE;
public:
//! Nullifies the handle to corresponding builder instance to prevent memory leaks

View File

@@ -498,6 +498,123 @@ Standard_Boolean SelectMgr_TriangularFrustumSet::OverlapsSphere (const gp_Pnt& t
return Standard_False;
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_TriangularFrustumSet::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const
{
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Polyline,
"Error! SelectMgr_TriangularFrustumSet::Overlaps() should be called after selection frustum initialization");
for (SelectMgr_TriangFrustums::Iterator anIter (myFrustums); anIter.More(); anIter.Next())
{
if (anIter.Value()->OverlapsCylinder (theBottomRad, theTopRad, theHeight, theTrsf, theClipRange, thePickResult))
{
return true;
}
}
return false;
}
//=======================================================================
// function : OverlapsCylinder
// purpose :
//=======================================================================
Standard_Boolean SelectMgr_TriangularFrustumSet::OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside) const
{
const gp_Dir aCylNorm (gp::DZ().Transformed (theTrsf));
const gp_Pnt aBottomCenter (gp::Origin().Transformed (theTrsf));
const gp_Pnt aTopCenter = aBottomCenter.XYZ() + aCylNorm.XYZ() * theHeight;
const gp_Vec aVecPlane1 (myFrustums.First()->myVertices[0], myFrustums.First()->myVertices[1]);
const gp_Vec aVecPlane2 (myFrustums.First()->myVertices[0], myFrustums.First()->myVertices[2]);
const gp_Dir aDirNorm (aVecPlane1.Crossed (aVecPlane2));
const Standard_Real anAngle = aCylNorm.Angle (aDirNorm);
const Standard_Real aCosAngle = Cos (anAngle);
const gp_Pln aPln (myFrustums.First()->myVertices[0], aDirNorm);
Standard_Real aCoefA, aCoefB, aCoefC, aCoefD;
aPln.Coefficients (aCoefA, aCoefB, aCoefC, aCoefD);
const Standard_Real aTBottom = -(aBottomCenter.XYZ().Dot (aDirNorm.XYZ()) + aCoefD) / aDirNorm.Dot (aDirNorm);
const gp_Pnt aBottomCenterProject (aCoefA * aTBottom + aBottomCenter.X(),
aCoefB * aTBottom + aBottomCenter.Y(),
aCoefC * aTBottom + aBottomCenter.Z());
const Standard_Real aTTop = -(aTopCenter.XYZ().Dot (aDirNorm.XYZ()) + aCoefD) / aDirNorm.Dot (aDirNorm);
const gp_Pnt aTopCenterProject (aCoefA * aTTop + aTopCenter.X(),
aCoefB * aTTop + aTopCenter.Y(),
aCoefC * aTTop + aTopCenter.Z());
gp_XYZ aCylNormProject;
const gp_XYZ aTopBottomVec = aTopCenterProject.XYZ() - aBottomCenterProject.XYZ();
const Standard_Real aTopBottomDist = aTopBottomVec.Modulus();
if (aTopBottomDist > 0.0)
{
aCylNormProject = aTopBottomVec / aTopBottomDist;
}
gp_Pnt aPoints[6];
aPoints[0] = aBottomCenterProject.XYZ() - aCylNormProject * theBottomRad * Abs (aCosAngle);
aPoints[1] = aTopCenterProject.XYZ() + aCylNormProject * theTopRad * Abs (aCosAngle);
const gp_Dir aDirEndFaces = (aCylNorm.IsParallel (aDirNorm, Precision::Angular()))
? gp::DY().Transformed (theTrsf)
: aCylNorm.Crossed (aDirNorm);
aPoints[2] = aTopCenterProject.XYZ() + aDirEndFaces.XYZ() * theTopRad;
aPoints[3] = aTopCenterProject.XYZ() - aDirEndFaces.XYZ() * theTopRad;
aPoints[4] = aBottomCenterProject.XYZ() + aDirEndFaces.XYZ() * theBottomRad;
aPoints[5] = aBottomCenterProject.XYZ() - aDirEndFaces.XYZ() * theBottomRad;
gp_Pnt aVerticesBuf[3];
TColgp_Array1OfPnt aVertices (aVerticesBuf[0], 0, 2);
bool isCylInsideTriangSet = true;
for (int i = 0; i < 6; ++i)
{
bool isInside = false;
for (SelectMgr_TriangFrustums::Iterator anIter (myFrustums); anIter.More(); anIter.Next())
{
for (int anIdx = 0; anIdx < 3; anIdx++)
{
aVertices[anIdx] = anIter.Value()->myVertices[anIdx];
}
if (anIter.Value()->IsDotInside (aPoints[i], aVertices))
{
isInside = true;
break;
}
}
isCylInsideTriangSet &= isInside;
}
if (theInside != NULL)
{
*theInside &= isCylInsideTriangSet;
}
if (isCylInsideTriangSet)
{
return true;
}
for (SelectMgr_TriangFrustums::Iterator anIter (myFrustums); anIter.More(); anIter.Next())
{
if (anIter.Value()->OverlapsCylinder (theBottomRad, theTopRad, theHeight, theTrsf, theInside))
{
return true;
}
}
return false;
}
// =======================================================================
// function : GetPlanes
// purpose :

View File

@@ -118,6 +118,23 @@ public:
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
const SelectMgr_ViewClipRange& theClipRange,
SelectBasics_PickResult& thePickResult) const Standard_OVERRIDE;
//! Returns true if selecting volume is overlapped by cylinder (or cone) with radiuses theBottomRad
//! and theTopRad, height theHeight and transformation to apply theTrsf.
Standard_EXPORT virtual Standard_Boolean OverlapsCylinder (const Standard_Real theBottomRad,
const Standard_Real theTopRad,
const Standard_Real theHeight,
const gp_Trsf& theTrsf,
Standard_Boolean* theInside = NULL) const Standard_OVERRIDE;
//! Stores plane equation coefficients (in the following form:
//! Ax + By + Cz + D = 0) to the given vector
Standard_EXPORT virtual void GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const Standard_OVERRIDE;