1 // Copyright (c) 2021 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <SelectMgr_AxisIntersector.hxx>
16 #include <Bnd_Range.hxx>
17 #include <BVH_Tools.hxx>
18 #include <Precision.hxx>
19 #include <SelectBasics_PickResult.hxx>
20 #include <SelectMgr_ViewClipRange.hxx>
22 // =======================================================================
23 // function : Constructor
25 // =======================================================================
26 SelectMgr_AxisIntersector::SelectMgr_AxisIntersector()
31 // =======================================================================
32 // function : ~SelectMgr_AxisIntersector
34 // =======================================================================
35 SelectMgr_AxisIntersector::~SelectMgr_AxisIntersector()
40 // =======================================================================
43 // =======================================================================
44 void SelectMgr_AxisIntersector::Init (const gp_Ax1& theAxis)
46 mySelectionType = SelectMgr_SelectionType_Point;
50 // =======================================================================
53 // =======================================================================
54 void SelectMgr_AxisIntersector::Build()
58 // =======================================================================
59 // function : ScaleAndTransform
61 // =======================================================================
62 Handle(SelectMgr_BaseIntersector) SelectMgr_AxisIntersector::ScaleAndTransform (const Standard_Integer theScaleFactor,
63 const gp_GTrsf& theTrsf,
64 const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
66 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
67 "Error! SelectMgr_AxisIntersector::ScaleAndTransform() should be called after selection axis initialization");
69 (void )theScaleFactor;
71 if (theTrsf.Form() == gp_Identity)
73 return new SelectMgr_AxisIntersector();
76 gp_Pnt aTransformedLoc = myAxis.Location();
77 theTrsf.Transforms (aTransformedLoc.ChangeCoord());
78 gp_XYZ aTransformedDir = myAxis.Direction().XYZ();
79 gp_GTrsf aTrsf = theTrsf;
80 aTrsf.SetTranslationPart (gp_XYZ(0., 0., 0.));
81 aTrsf.Transforms (aTransformedDir);
83 Handle(SelectMgr_AxisIntersector) aRes = new SelectMgr_AxisIntersector();
84 aRes->myAxis = gp_Ax1(aTransformedLoc, gp_Dir(aTransformedDir));
85 aRes->mySelectionType = mySelectionType;
89 // =======================================================================
90 // function : hasIntersection
92 // =======================================================================
93 Standard_Boolean SelectMgr_AxisIntersector::hasIntersection (const SelectMgr_Vec3& theBoxMin,
94 const SelectMgr_Vec3& theBoxMax,
95 Standard_Real& theTimeEnter,
96 Standard_Real& theTimeLeave) const
98 const gp_Pnt& anAxisLoc = myAxis.Location();
99 const gp_Dir& anAxisDir = myAxis.Direction();
100 BVH_Ray<Standard_Real, 3> aRay(SelectMgr_Vec3(anAxisLoc.X(), anAxisLoc.Y(), anAxisLoc.Z()),
101 SelectMgr_Vec3(anAxisDir.X(), anAxisDir.Y(), anAxisDir.Z()));
102 if (!BVH_Tools<Standard_Real, 3>::RayBoxIntersection (aRay, theBoxMin, theBoxMax, theTimeEnter, theTimeLeave))
104 return Standard_False;
106 return Standard_True;
109 // =======================================================================
110 // function : hasIntersection
112 // =======================================================================
113 Standard_Boolean SelectMgr_AxisIntersector::hasIntersection (const gp_Pnt& thePnt,
114 Standard_Real& theDepth) const
116 const gp_Pnt& anAxisLoc = myAxis.Location();
117 const gp_Dir& anAxisDir = myAxis.Direction();
119 // Check that vectors are co-directed (thePnt lies on this axis)
120 gp_Dir aDirToPnt(thePnt.XYZ() - anAxisLoc.XYZ());
121 if (!anAxisDir.IsEqual (aDirToPnt, Precision::Angular()))
123 return Standard_False;
125 theDepth = anAxisLoc.Distance (thePnt);
126 return Standard_True;
129 // =======================================================================
130 // function : raySegmentDistance
132 // =======================================================================
133 Standard_Boolean SelectMgr_AxisIntersector::raySegmentDistance (const gp_Pnt& theSegPnt1,
134 const gp_Pnt& theSegPnt2,
135 SelectBasics_PickResult& thePickResult) const
137 const gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
138 const gp_XYZ aV = myAxis.Direction().XYZ();
139 const gp_XYZ aW = theSegPnt1.XYZ() - myAxis.Location().XYZ();
141 const gp_XYZ anUVNormVec = aV.Crossed (anU);
142 const Standard_Real anUVNormVecMod = anUVNormVec.Modulus();
143 if (anUVNormVecMod <= Precision::Confusion())
145 // Lines have no intersection
146 thePickResult.Invalidate();
150 const gp_XYZ anUWNormVec = aW.Crossed (anU);
151 const Standard_Real anUWNormVecMod = anUWNormVec.Modulus();
152 if (anUWNormVecMod <= Precision::Confusion())
154 // Lines have no intersection
155 thePickResult.Invalidate();
159 const Standard_Real aParam = anUWNormVec.Dot (anUVNormVec) / anUVNormVecMod;
162 // Intersection is out of axis start point
163 thePickResult.Invalidate();
167 const gp_XYZ anIntersectPnt = myAxis.Location().XYZ() + aV * aParam;
168 if ((anIntersectPnt - theSegPnt1.XYZ()).Modulus() +
169 (anIntersectPnt - theSegPnt2.XYZ()).Modulus() >
170 anU.Modulus() + Precision::Confusion())
172 // Intersection point doesn't lie on the segment
173 thePickResult.Invalidate();
177 thePickResult.SetDepth (myAxis.Location().Distance (anIntersectPnt));
178 thePickResult.SetPickedPoint (anIntersectPnt);
182 // =======================================================================
183 // function : rayPlaneIntersection
185 // =======================================================================
186 bool SelectMgr_AxisIntersector::rayPlaneIntersection (const gp_Vec& thePlane,
187 const gp_Pnt& thePntOnPlane,
188 SelectBasics_PickResult& thePickResult) const
190 gp_XYZ anU = myAxis.Direction().XYZ();
191 gp_XYZ aW = myAxis.Location().XYZ() - thePntOnPlane.XYZ();
192 Standard_Real aD = thePlane.Dot (anU);
193 Standard_Real aN = -thePlane.Dot (aW);
195 if (Abs (aD) < Precision::Confusion())
197 thePickResult.Invalidate();
201 Standard_Real aParam = aN / aD;
204 thePickResult.Invalidate();
208 gp_Pnt aClosestPnt = myAxis.Location().XYZ() + anU * aParam;
209 thePickResult.SetDepth (myAxis.Location().Distance (aClosestPnt));
210 thePickResult.SetPickedPoint (aClosestPnt);
214 // =======================================================================
215 // function : OverlapsBox
217 // =======================================================================
218 Standard_Boolean SelectMgr_AxisIntersector::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
219 const SelectMgr_Vec3& theBoxMax,
220 Standard_Boolean* theInside) const
222 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
223 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
226 Standard_Real aTimeEnter, aTimeLeave;
227 if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
229 return Standard_False;
231 if (theInside != NULL)
233 *theInside &= (aTimeEnter >= 0.0);
235 return Standard_True;
238 // =======================================================================
239 // function : OverlapsBox
241 // =======================================================================
242 Standard_Boolean SelectMgr_AxisIntersector::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
243 const SelectMgr_Vec3& theBoxMax,
244 const SelectMgr_ViewClipRange& theClipRange,
245 SelectBasics_PickResult& thePickResult) const
247 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
248 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
250 Standard_Real aTimeEnter, aTimeLeave;
251 if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
253 return Standard_False;
256 Standard_Real aDepth = 0.0;
257 Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
258 aRange.GetMin (aDepth);
260 if (!theClipRange.GetNearestDepth (aRange, aDepth))
262 return Standard_False;
265 thePickResult.SetDepth (aDepth);
267 return Standard_True;
270 // =======================================================================
271 // function : OverlapsPoint
273 // =======================================================================
274 Standard_Boolean SelectMgr_AxisIntersector::OverlapsPoint (const gp_Pnt& thePnt,
275 const SelectMgr_ViewClipRange& theClipRange,
276 SelectBasics_PickResult& thePickResult) const
278 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
279 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
281 Standard_Real aDepth = 0.0;
282 if (!hasIntersection (thePnt, aDepth))
284 return Standard_False;
287 thePickResult.SetDepth (aDepth);
288 thePickResult.SetPickedPoint (thePnt);
290 return !theClipRange.IsClipped (thePickResult.Depth());
293 // =======================================================================
294 // function : OverlapsPoint
296 // =======================================================================
297 Standard_Boolean SelectMgr_AxisIntersector::OverlapsPoint (const gp_Pnt& thePnt) const
299 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
300 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
302 Standard_Real aDepth = 0.0;
303 return hasIntersection (thePnt, aDepth);
306 // =======================================================================
307 // function : OverlapsSegment
309 // =======================================================================
310 Standard_Boolean SelectMgr_AxisIntersector::OverlapsSegment (const gp_Pnt& thePnt1,
311 const gp_Pnt& thePnt2,
312 const SelectMgr_ViewClipRange& theClipRange,
313 SelectBasics_PickResult& thePickResult) const
315 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
316 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
318 if (!raySegmentDistance (thePnt1, thePnt2, thePickResult))
320 return Standard_False;
323 return !theClipRange.IsClipped (thePickResult.Depth());
326 // =======================================================================
327 // function : OverlapsPolygon
329 // =======================================================================
330 Standard_Boolean SelectMgr_AxisIntersector::OverlapsPolygon (const TColgp_Array1OfPnt& theArrayOfPnts,
331 Select3D_TypeOfSensitivity theSensType,
332 const SelectMgr_ViewClipRange& theClipRange,
333 SelectBasics_PickResult& thePickResult) const
335 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
336 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
338 if (theSensType == Select3D_TOS_BOUNDARY)
340 Standard_Integer aMatchingSegmentsNb = -1;
341 SelectBasics_PickResult aPickResult;
342 thePickResult.Invalidate();
343 const Standard_Integer aLower = theArrayOfPnts.Lower();
344 const Standard_Integer anUpper = theArrayOfPnts.Upper();
345 for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
347 const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
348 const gp_Pnt& aEndPnt = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
349 if (raySegmentDistance (aStartPnt, aEndPnt, aPickResult))
351 aMatchingSegmentsNb++;
352 thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
356 if (aMatchingSegmentsNb == -1)
358 return Standard_False;
361 else if (theSensType == Select3D_TOS_INTERIOR)
363 Standard_Integer aStartIdx = theArrayOfPnts.Lower();
364 const gp_XYZ& aPnt1 = theArrayOfPnts.Value (aStartIdx).XYZ();
365 const gp_XYZ& aPnt2 = theArrayOfPnts.Value (aStartIdx + 1).XYZ();
366 const gp_XYZ& aPnt3 = theArrayOfPnts.Value (aStartIdx + 2).XYZ();
367 const gp_XYZ aVec1 = aPnt1 - aPnt2;
368 const gp_XYZ aVec2 = aPnt3 - aPnt2;
369 gp_Vec aPolyNorm = aVec2.Crossed (aVec1);
370 if (aPolyNorm.Magnitude() <= Precision::Confusion())
372 // treat degenerated polygon as point
373 return OverlapsPoint (theArrayOfPnts.First(), theClipRange, thePickResult);
375 else if (!rayPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
377 return Standard_False;
381 return !theClipRange.IsClipped (thePickResult.Depth());
384 // =======================================================================
385 // function : OverlapsTriangle
387 // =======================================================================
388 Standard_Boolean SelectMgr_AxisIntersector::OverlapsTriangle (const gp_Pnt& thePnt1,
389 const gp_Pnt& thePnt2,
390 const gp_Pnt& thePnt3,
391 Select3D_TypeOfSensitivity theSensType,
392 const SelectMgr_ViewClipRange& theClipRange,
393 SelectBasics_PickResult& thePickResult) const
395 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
396 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
398 if (theSensType == Select3D_TOS_BOUNDARY)
400 const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
401 const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
402 return OverlapsPolygon (aPntsArray, Select3D_TOS_BOUNDARY, theClipRange, thePickResult);
404 else if (theSensType == Select3D_TOS_INTERIOR)
406 gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
407 const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
408 thePnt3.XYZ() - thePnt2.XYZ(),
409 thePnt1.XYZ() - thePnt3.XYZ() };
410 aTriangleNormal = aTrEdges[2].Crossed (aTrEdges[0]);
411 if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
413 // consider degenerated triangle as point or segment
414 return aTrEdges[0].SquareModulus() > gp::Resolution()
415 ? OverlapsSegment (thePnt1, thePnt2, theClipRange, thePickResult)
416 : (aTrEdges[1].SquareModulus() > gp::Resolution()
417 ? OverlapsSegment (thePnt2, thePnt3, theClipRange, thePickResult)
418 : OverlapsPoint (thePnt1, theClipRange, thePickResult));
421 const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
422 const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myAxis.Direction().XYZ());
423 if (Abs (anAlpha) < gp::Resolution())
425 // handle the case when triangle normal and selecting frustum direction are orthogonal
426 SelectBasics_PickResult aPickResult;
427 thePickResult.Invalidate();
428 for (Standard_Integer anEdgeIter = 0; anEdgeIter < 3; ++anEdgeIter)
430 const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
431 const gp_Pnt& anEndPnt = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
432 if (raySegmentDistance (aStartPnt, anEndPnt, aPickResult))
434 thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
437 thePickResult.SetSurfaceNormal (aTriangleNormal);
438 return thePickResult.IsValid()
439 && !theClipRange.IsClipped (thePickResult.Depth());
442 // check if intersection point belongs to triangle's interior part
443 const gp_XYZ anEdge = (thePnt1.XYZ() - myAxis.Location().XYZ()) * (1.0 / anAlpha);
445 const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
446 const gp_XYZ aVec = myAxis.Direction().XYZ().Crossed (anEdge);
447 const Standard_Real anU = aVec.Dot (aTrEdges[2]);
448 const Standard_Real aV = aVec.Dot (aTrEdges[0]);
450 const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
451 const gp_Pnt aPtOnPlane = myAxis.Location().XYZ() + myAxis.Direction().XYZ() * aTime;
454 thePickResult.SetDepth (myAxis.Location().Distance (aPtOnPlane));
455 thePickResult.SetPickedPoint (aPtOnPlane);
456 thePickResult.SetSurfaceNormal (aTriangleNormal);
457 return !theClipRange.IsClipped (thePickResult.Depth());
460 Standard_Real aMinDist = RealLast();
461 Standard_Integer aNearestEdgeIdx1 = -1;
462 for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
464 gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
465 Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
466 Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
467 if (aDist < aMinDist)
470 aNearestEdgeIdx1 = anEdgeIdx;
473 Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
474 const gp_Vec aVec12 (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]);
475 if (aVec12.SquareMagnitude() > gp::Resolution()
476 && myAxis.Direction().IsParallel (aVec12, Precision::Angular()))
478 aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
480 if (raySegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult))
482 thePickResult.SetSurfaceNormal (aTriangleNormal);
486 return thePickResult.IsValid()
487 && !theClipRange.IsClipped (thePickResult.Depth());
490 //=======================================================================
491 // function : GetNearPnt
493 //=======================================================================
494 const gp_Pnt& SelectMgr_AxisIntersector::GetNearPnt() const
496 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
497 "Error! SelectMgr_AxisIntersector::GetNearPnt() should be called after selection axis initialization");
499 return myAxis.Location();
502 //=======================================================================
503 // function : GetFarPnt
505 //=======================================================================
506 const gp_Pnt& SelectMgr_AxisIntersector::GetFarPnt() const
508 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
509 "Error! SelectMgr_AxisIntersector::GetFarPnt() should be called after selection axis initialization");
511 static gp_Pnt anInfPnt(RealLast(), RealLast(), RealLast());
515 //=======================================================================
516 // function : GetViewRayDirection
518 //=======================================================================
519 const gp_Dir& SelectMgr_AxisIntersector::GetViewRayDirection() const
521 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
522 "Error! SelectMgr_AxisIntersector::GetViewRayDirection() should be called after selection axis initialization");
524 return myAxis.Direction();
527 // =======================================================================
528 // function : DistToGeometryCenter
530 // =======================================================================
531 Standard_Real SelectMgr_AxisIntersector::DistToGeometryCenter (const gp_Pnt& theCOG) const
533 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
534 "Error! SelectMgr_AxisIntersector::DistToGeometryCenter() should be called after selection axis initialization");
536 return theCOG.Distance (myAxis.Location());
539 // =======================================================================
540 // function : DetectedPoint
542 // =======================================================================
543 gp_Pnt SelectMgr_AxisIntersector::DetectedPoint (const Standard_Real theDepth) const
545 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
546 "Error! SelectMgr_AxisIntersector::DetectedPoint() should be called after selection axis initialization");
548 return myAxis.Location().XYZ() + myAxis.Direction().XYZ() * theDepth;
551 //=======================================================================
552 //function : DumpJson
554 //=======================================================================
555 void SelectMgr_AxisIntersector::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
557 OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_AxisIntersector)
558 OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_BaseIntersector)
560 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myAxis)