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()
30 // =======================================================================
33 // =======================================================================
34 void SelectMgr_AxisIntersector::Init (const gp_Ax1& theAxis)
36 mySelectionType = SelectMgr_SelectionType_Point;
40 // =======================================================================
43 // =======================================================================
44 void SelectMgr_AxisIntersector::Build()
48 // =======================================================================
49 // function : ScaleAndTransform
51 // =======================================================================
52 Handle(SelectMgr_BaseIntersector) SelectMgr_AxisIntersector::ScaleAndTransform (const Standard_Integer theScaleFactor,
53 const gp_GTrsf& theTrsf,
54 const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
56 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
57 "Error! SelectMgr_AxisIntersector::ScaleAndTransform() should be called after selection axis initialization");
59 (void )theScaleFactor;
61 if (theTrsf.Form() == gp_Identity)
63 return new SelectMgr_AxisIntersector();
66 gp_Pnt aTransformedLoc = myAxis.Location();
67 theTrsf.Transforms (aTransformedLoc.ChangeCoord());
68 gp_XYZ aTransformedDir = myAxis.Direction().XYZ();
69 gp_GTrsf aTrsf = theTrsf;
70 aTrsf.SetTranslationPart (gp_XYZ(0., 0., 0.));
71 aTrsf.Transforms (aTransformedDir);
73 Handle(SelectMgr_AxisIntersector) aRes = new SelectMgr_AxisIntersector();
74 aRes->myAxis = gp_Ax1(aTransformedLoc, gp_Dir(aTransformedDir));
75 aRes->mySelectionType = mySelectionType;
79 // =======================================================================
80 // function : hasIntersection
82 // =======================================================================
83 Standard_Boolean SelectMgr_AxisIntersector::hasIntersection (const SelectMgr_Vec3& theBoxMin,
84 const SelectMgr_Vec3& theBoxMax,
85 Standard_Real& theTimeEnter,
86 Standard_Real& theTimeLeave) const
88 const gp_Pnt& anAxisLoc = myAxis.Location();
89 const gp_Dir& anAxisDir = myAxis.Direction();
90 BVH_Ray<Standard_Real, 3> aRay(SelectMgr_Vec3(anAxisLoc.X(), anAxisLoc.Y(), anAxisLoc.Z()),
91 SelectMgr_Vec3(anAxisDir.X(), anAxisDir.Y(), anAxisDir.Z()));
92 if (!BVH_Tools<Standard_Real, 3>::RayBoxIntersection (aRay, theBoxMin, theBoxMax, theTimeEnter, theTimeLeave))
94 return Standard_False;
99 // =======================================================================
100 // function : hasIntersection
102 // =======================================================================
103 Standard_Boolean SelectMgr_AxisIntersector::hasIntersection (const gp_Pnt& thePnt,
104 Standard_Real& theDepth) const
106 const gp_Pnt& anAxisLoc = myAxis.Location();
107 const gp_Dir& anAxisDir = myAxis.Direction();
109 // Check that vectors are co-directed (thePnt lies on this axis)
110 gp_Dir aDirToPnt(thePnt.XYZ() - anAxisLoc.XYZ());
111 if (!anAxisDir.IsEqual (aDirToPnt, Precision::Angular()))
113 return Standard_False;
115 theDepth = anAxisLoc.Distance (thePnt);
116 return Standard_True;
119 // =======================================================================
120 // function : raySegmentDistance
122 // =======================================================================
123 Standard_Boolean SelectMgr_AxisIntersector::raySegmentDistance (const gp_Pnt& theSegPnt1,
124 const gp_Pnt& theSegPnt2,
125 SelectBasics_PickResult& thePickResult) const
127 gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
128 gp_XYZ aV = myAxis.Direction().XYZ();
129 gp_XYZ aW = theSegPnt1.XYZ() - myAxis.Location().XYZ();
131 gp_XYZ anUVNormVec = aV.Crossed (anU);
132 gp_XYZ anUWNormVec = aW.Crossed (anU);
133 if (anUVNormVec.Modulus() <= Precision::Confusion() ||
134 anUWNormVec.Modulus() <= Precision::Confusion())
136 // Lines have no intersection
137 thePickResult.Invalidate();
141 Standard_Real aParam = anUWNormVec.Dot (anUVNormVec) / anUVNormVec.SquareModulus();
144 // Intersection is out of axis start point
145 thePickResult.Invalidate();
149 gp_XYZ anIntersectPnt = myAxis.Location().XYZ() + aV * aParam;
150 if ((anIntersectPnt - theSegPnt1.XYZ()).SquareModulus() +
151 (anIntersectPnt - theSegPnt2.XYZ()).SquareModulus() >
152 anU.SquareModulus() + Precision::Confusion())
154 // Intersection point doesn't lie on the segment
155 thePickResult.Invalidate();
159 thePickResult.SetDepth (myAxis.Location().Distance (anIntersectPnt));
160 thePickResult.SetPickedPoint (anIntersectPnt);
164 // =======================================================================
165 // function : rayPlaneIntersection
167 // =======================================================================
168 bool SelectMgr_AxisIntersector::rayPlaneIntersection (const gp_Vec& thePlane,
169 const gp_Pnt& thePntOnPlane,
170 SelectBasics_PickResult& thePickResult) const
172 gp_XYZ anU = myAxis.Direction().XYZ();
173 gp_XYZ aW = myAxis.Location().XYZ() - thePntOnPlane.XYZ();
174 Standard_Real aD = thePlane.Dot (anU);
175 Standard_Real aN = -thePlane.Dot (aW);
177 if (Abs (aD) < Precision::Confusion())
179 thePickResult.Invalidate();
183 Standard_Real aParam = aN / aD;
186 thePickResult.Invalidate();
190 gp_Pnt aClosestPnt = myAxis.Location().XYZ() + anU * aParam;
191 thePickResult.SetDepth (myAxis.Location().Distance (aClosestPnt));
192 thePickResult.SetPickedPoint (aClosestPnt);
196 // =======================================================================
197 // function : Overlaps
199 // =======================================================================
200 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const SelectMgr_Vec3& theBoxMin,
201 const SelectMgr_Vec3& theBoxMax,
202 Standard_Boolean* theInside) const
204 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
205 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
208 Standard_Real aTimeEnter, aTimeLeave;
209 if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
211 return Standard_False;
213 if (theInside != NULL)
215 *theInside &= (aTimeEnter >= 0.0);
217 return Standard_True;
220 // =======================================================================
221 // function : Overlaps
223 // =======================================================================
224 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const SelectMgr_Vec3& theBoxMin,
225 const SelectMgr_Vec3& theBoxMax,
226 const SelectMgr_ViewClipRange& theClipRange,
227 SelectBasics_PickResult& thePickResult) const
229 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
230 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
232 Standard_Real aTimeEnter, aTimeLeave;
233 if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
235 return Standard_False;
238 Standard_Real aDepth = 0.0;
239 Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
240 aRange.GetMin (aDepth);
242 if (!theClipRange.GetNearestDepth (aRange, aDepth))
244 return Standard_False;
247 thePickResult.SetDepth (aDepth);
249 return Standard_True;
252 // =======================================================================
253 // function : Overlaps
255 // =======================================================================
256 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt,
257 const SelectMgr_ViewClipRange& theClipRange,
258 SelectBasics_PickResult& thePickResult) const
260 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
261 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
263 Standard_Real aDepth = 0.0;
264 if (!hasIntersection (thePnt, aDepth))
266 return Standard_False;
269 thePickResult.SetDepth (aDepth);
270 thePickResult.SetPickedPoint (thePnt);
272 return !theClipRange.IsClipped (thePickResult.Depth());
275 // =======================================================================
276 // function : Overlaps
278 // =======================================================================
279 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt) const
281 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
282 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
284 Standard_Real aDepth = 0.0;
285 return hasIntersection (thePnt, aDepth);
288 // =======================================================================
289 // function : Overlaps
291 // =======================================================================
292 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt1,
293 const gp_Pnt& thePnt2,
294 const SelectMgr_ViewClipRange& theClipRange,
295 SelectBasics_PickResult& thePickResult) const
297 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
298 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
300 if (!raySegmentDistance (thePnt1, thePnt2, thePickResult))
302 return Standard_False;
305 return !theClipRange.IsClipped (thePickResult.Depth());
308 // =======================================================================
309 // function : Overlaps
311 // =======================================================================
312 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const TColgp_Array1OfPnt& theArrayOfPnts,
313 Select3D_TypeOfSensitivity theSensType,
314 const SelectMgr_ViewClipRange& theClipRange,
315 SelectBasics_PickResult& thePickResult) const
317 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
318 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
320 if (theSensType == Select3D_TOS_BOUNDARY)
322 Standard_Integer aMatchingSegmentsNb = -1;
323 SelectBasics_PickResult aPickResult;
324 thePickResult.Invalidate();
325 const Standard_Integer aLower = theArrayOfPnts.Lower();
326 const Standard_Integer anUpper = theArrayOfPnts.Upper();
327 for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
329 const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
330 const gp_Pnt& aEndPnt = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
331 if (raySegmentDistance (aStartPnt, aEndPnt, aPickResult))
333 aMatchingSegmentsNb++;
334 thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
338 if (aMatchingSegmentsNb == -1)
340 return Standard_False;
343 else if (theSensType == Select3D_TOS_INTERIOR)
345 Standard_Integer aStartIdx = theArrayOfPnts.Lower();
346 const gp_XYZ& aPnt1 = theArrayOfPnts.Value (aStartIdx).XYZ();
347 const gp_XYZ& aPnt2 = theArrayOfPnts.Value (aStartIdx + 1).XYZ();
348 const gp_XYZ& aPnt3 = theArrayOfPnts.Value (aStartIdx + 2).XYZ();
349 const gp_XYZ aVec1 = aPnt1 - aPnt2;
350 const gp_XYZ aVec2 = aPnt3 - aPnt2;
351 gp_Vec aPolyNorm = aVec2.Crossed (aVec1);
352 if (aPolyNorm.Magnitude() <= Precision::Confusion())
354 // treat degenerated polygon as point
355 return Overlaps (theArrayOfPnts.First(), theClipRange, thePickResult);
357 else if (!rayPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
359 return Standard_False;
363 return !theClipRange.IsClipped (thePickResult.Depth());
366 // =======================================================================
367 // function : Overlaps
369 // =======================================================================
370 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt1,
371 const gp_Pnt& thePnt2,
372 const gp_Pnt& thePnt3,
373 Select3D_TypeOfSensitivity theSensType,
374 const SelectMgr_ViewClipRange& theClipRange,
375 SelectBasics_PickResult& thePickResult) const
377 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
378 "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
380 if (theSensType == Select3D_TOS_BOUNDARY)
382 const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
383 const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
384 return Overlaps (aPntsArray, Select3D_TOS_BOUNDARY, theClipRange, thePickResult);
386 else if (theSensType == Select3D_TOS_INTERIOR)
388 gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
389 const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
390 thePnt3.XYZ() - thePnt2.XYZ(),
391 thePnt1.XYZ() - thePnt3.XYZ() };
392 aTriangleNormal = aTrEdges[2].Crossed (aTrEdges[0]);
393 if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
395 // consider degenerated triangle as point or segment
396 return aTrEdges[0].SquareModulus() > gp::Resolution()
397 ? Overlaps (thePnt1, thePnt2, theClipRange, thePickResult)
398 : (aTrEdges[1].SquareModulus() > gp::Resolution()
399 ? Overlaps (thePnt2, thePnt3, theClipRange, thePickResult)
400 : Overlaps (thePnt1, theClipRange, thePickResult));
403 const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
404 const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myAxis.Direction().XYZ());
405 if (Abs (anAlpha) < gp::Resolution())
407 // handle the case when triangle normal and selecting frustum direction are orthogonal
408 SelectBasics_PickResult aPickResult;
409 thePickResult.Invalidate();
410 for (Standard_Integer anEdgeIter = 0; anEdgeIter < 3; ++anEdgeIter)
412 const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
413 const gp_Pnt& anEndPnt = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
414 if (raySegmentDistance (aStartPnt, anEndPnt, aPickResult))
416 thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
419 thePickResult.SetSurfaceNormal (aTriangleNormal);
420 return !theClipRange.IsClipped (thePickResult.Depth());
423 // check if intersection point belongs to triangle's interior part
424 const gp_XYZ anEdge = (thePnt1.XYZ() - myAxis.Location().XYZ()) * (1.0 / anAlpha);
426 const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
427 const gp_XYZ aVec = myAxis.Direction().XYZ().Crossed (anEdge);
428 const Standard_Real anU = aVec.Dot (aTrEdges[2]);
429 const Standard_Real aV = aVec.Dot (aTrEdges[0]);
431 const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
432 const gp_Pnt aPtOnPlane = myAxis.Location().XYZ() + myAxis.Direction().XYZ() * aTime;
435 thePickResult.SetDepth (myAxis.Location().Distance (aPtOnPlane));
436 thePickResult.SetPickedPoint (aPtOnPlane);
437 thePickResult.SetSurfaceNormal (aTriangleNormal);
438 return !theClipRange.IsClipped (thePickResult.Depth());
441 Standard_Real aMinDist = RealLast();
442 Standard_Integer aNearestEdgeIdx1 = -1;
443 for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
445 gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
446 Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
447 Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
448 if (aDist < aMinDist)
451 aNearestEdgeIdx1 = anEdgeIdx;
454 Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
455 const gp_Vec aVec12 (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]);
456 if (aVec12.SquareMagnitude() > gp::Resolution()
457 && myAxis.Direction().IsParallel (aVec12, Precision::Angular()))
459 aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
461 if (raySegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult))
463 thePickResult.SetSurfaceNormal (aTriangleNormal);
467 return !theClipRange.IsClipped (thePickResult.Depth());
470 //=======================================================================
471 // function : GetNearPnt
473 //=======================================================================
474 const gp_Pnt& SelectMgr_AxisIntersector::GetNearPnt() const
476 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
477 "Error! SelectMgr_AxisIntersector::GetNearPnt() should be called after selection axis initialization");
479 return myAxis.Location();
482 //=======================================================================
483 // function : GetFarPnt
485 //=======================================================================
486 const gp_Pnt& SelectMgr_AxisIntersector::GetFarPnt() const
488 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
489 "Error! SelectMgr_AxisIntersector::GetFarPnt() should be called after selection axis initialization");
491 static gp_Pnt anInfPnt(RealLast(), RealLast(), RealLast());
495 //=======================================================================
496 // function : GetViewRayDirection
498 //=======================================================================
499 const gp_Dir& SelectMgr_AxisIntersector::GetViewRayDirection() const
501 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
502 "Error! SelectMgr_AxisIntersector::GetViewRayDirection() should be called after selection axis initialization");
504 return myAxis.Direction();
507 // =======================================================================
508 // function : DistToGeometryCenter
510 // =======================================================================
511 Standard_Real SelectMgr_AxisIntersector::DistToGeometryCenter (const gp_Pnt& theCOG) const
513 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
514 "Error! SelectMgr_AxisIntersector::DistToGeometryCenter() should be called after selection axis initialization");
516 return theCOG.Distance (myAxis.Location());
519 // =======================================================================
520 // function : DetectedPoint
522 // =======================================================================
523 gp_Pnt SelectMgr_AxisIntersector::DetectedPoint (const Standard_Real theDepth) const
525 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
526 "Error! SelectMgr_AxisIntersector::DetectedPoint() should be called after selection axis initialization");
528 return myAxis.Location().XYZ() + myAxis.Direction().XYZ() * theDepth;
531 //=======================================================================
532 //function : DumpJson
534 //=======================================================================
535 void SelectMgr_AxisIntersector::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
537 OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_AxisIntersector)
538 OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_BaseIntersector)
540 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myAxis)