e7c8d38c6a75db1dfc4afa1272dce945ca6f21a4
[occt.git] / src / SelectMgr / SelectMgr_AxisIntersector.cxx
1 // Copyright (c) 2021 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
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.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <SelectMgr_AxisIntersector.hxx>
15
16 #include <Bnd_Range.hxx>
17 #include <BVH_Tools.hxx>
18 #include <Precision.hxx>
19 #include <SelectBasics_PickResult.hxx>
20 #include <SelectMgr_ViewClipRange.hxx>
21
22 // =======================================================================
23 // function : Constructor
24 // purpose  :
25 // =======================================================================
26 SelectMgr_AxisIntersector::SelectMgr_AxisIntersector()
27 {
28 }
29
30 // =======================================================================
31 // function : Init
32 // purpose  :
33 // =======================================================================
34 void SelectMgr_AxisIntersector::Init (const gp_Ax1& theAxis)
35 {
36   mySelectionType = SelectMgr_SelectionType_Point;
37   myAxis = theAxis;
38 }
39
40 // =======================================================================
41 // function : Build
42 // purpose  :
43 // =======================================================================
44 void SelectMgr_AxisIntersector::Build()
45 {
46 }
47
48 // =======================================================================
49 // function : ScaleAndTransform
50 // purpose  :
51 // =======================================================================
52 Handle(SelectMgr_BaseIntersector) SelectMgr_AxisIntersector::ScaleAndTransform (const Standard_Integer theScaleFactor,
53                                                                                 const gp_GTrsf& theTrsf,
54                                                                                 const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
55 {
56   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
57     "Error! SelectMgr_AxisIntersector::ScaleAndTransform() should be called after selection axis initialization");
58
59   (void )theScaleFactor;
60   (void )theBuilder;
61   if (theTrsf.Form() == gp_Identity)
62   {
63     return new SelectMgr_AxisIntersector();
64   }
65
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);
72
73   Handle(SelectMgr_AxisIntersector) aRes = new SelectMgr_AxisIntersector();
74   aRes->myAxis = gp_Ax1(aTransformedLoc, gp_Dir(aTransformedDir));
75   aRes->mySelectionType = mySelectionType;
76   return aRes;
77 }
78
79 // =======================================================================
80 // function : hasIntersection
81 // purpose  :
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
87 {
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))
93   {
94     return Standard_False;
95   }
96   return Standard_True;
97 }
98
99 // =======================================================================
100 // function : hasIntersection
101 // purpose  :
102 // =======================================================================
103 Standard_Boolean SelectMgr_AxisIntersector::hasIntersection (const gp_Pnt& thePnt,
104                                                              Standard_Real& theDepth) const
105 {
106   const gp_Pnt& anAxisLoc = myAxis.Location();
107   const gp_Dir& anAxisDir = myAxis.Direction();
108
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()))
112   {
113     return Standard_False;
114   }
115   theDepth = anAxisLoc.Distance (thePnt);
116   return Standard_True;
117 }
118
119 // =======================================================================
120 // function : raySegmentDistance
121 // purpose  :
122 // =======================================================================
123 Standard_Boolean SelectMgr_AxisIntersector::raySegmentDistance (const gp_Pnt& theSegPnt1,
124                                                                 const gp_Pnt& theSegPnt2,
125                                                                 SelectBasics_PickResult& thePickResult) const
126 {
127   gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
128   gp_XYZ aV = myAxis.Direction().XYZ();
129   gp_XYZ aW = theSegPnt1.XYZ() - myAxis.Location().XYZ();
130
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())
135   {
136     // Lines have no intersection
137     thePickResult.Invalidate();
138     return false;
139   }
140
141   Standard_Real aParam = anUWNormVec.Dot (anUVNormVec) / anUVNormVec.SquareModulus();
142   if (aParam < 0.0)
143   {
144     // Intersection is out of axis start point
145     thePickResult.Invalidate();
146     return false;
147   }
148
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())
153   {
154     // Intersection point doesn't lie on the segment
155     thePickResult.Invalidate();
156     return false;
157   }
158
159   thePickResult.SetDepth (myAxis.Location().Distance (anIntersectPnt));
160   thePickResult.SetPickedPoint (anIntersectPnt);
161   return true;
162 }
163
164 // =======================================================================
165 // function : rayPlaneIntersection
166 // purpose  :
167 // =======================================================================
168 bool SelectMgr_AxisIntersector::rayPlaneIntersection (const gp_Vec& thePlane,
169                                                       const gp_Pnt& thePntOnPlane,
170                                                       SelectBasics_PickResult& thePickResult) const
171 {
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);
176
177   if (Abs (aD) < Precision::Confusion())
178   {
179     thePickResult.Invalidate();
180     return false;
181   }
182
183   Standard_Real aParam = aN / aD;
184   if (aParam < 0.0)
185   {
186     thePickResult.Invalidate();
187     return false;
188   }
189
190   gp_Pnt aClosestPnt = myAxis.Location().XYZ() + anU * aParam;
191   thePickResult.SetDepth (myAxis.Location().Distance (aClosestPnt));
192   thePickResult.SetPickedPoint (aClosestPnt);
193   return true;
194 }
195
196 // =======================================================================
197 // function : Overlaps
198 // purpose  :
199 // =======================================================================
200 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const SelectMgr_Vec3& theBoxMin,
201                                                       const SelectMgr_Vec3& theBoxMax,
202                                                       Standard_Boolean*     theInside) const
203 {
204   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
205     "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
206
207   (void )theInside;
208   Standard_Real aTimeEnter, aTimeLeave;
209   if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
210   {
211     return Standard_False;
212   }
213   if (theInside != NULL)
214   {
215     *theInside &= (aTimeEnter >= 0.0);
216   }
217   return Standard_True;
218 }
219
220 // =======================================================================
221 // function : Overlaps
222 // purpose  :
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
228 {
229   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
230     "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
231
232   Standard_Real aTimeEnter, aTimeLeave;
233   if (!hasIntersection (theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
234   {
235     return Standard_False;
236   }
237
238   Standard_Real aDepth = 0.0;
239   Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
240   aRange.GetMin (aDepth);
241
242   if (!theClipRange.GetNearestDepth (aRange, aDepth))
243   {
244     return Standard_False;
245   }
246
247   thePickResult.SetDepth (aDepth);
248
249   return Standard_True;
250 }
251
252 // =======================================================================
253 // function : Overlaps
254 // purpose  :
255 // =======================================================================
256 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt,
257                                                       const SelectMgr_ViewClipRange& theClipRange,
258                                                       SelectBasics_PickResult& thePickResult) const
259 {
260   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
261     "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
262
263   Standard_Real aDepth = 0.0;
264   if (!hasIntersection (thePnt, aDepth))
265   {
266     return Standard_False;
267   }
268
269   thePickResult.SetDepth (aDepth);
270   thePickResult.SetPickedPoint (thePnt);
271
272   return !theClipRange.IsClipped (thePickResult.Depth());
273 }
274
275 // =======================================================================
276 // function : Overlaps
277 // purpose  :
278 // =======================================================================
279 Standard_Boolean SelectMgr_AxisIntersector::Overlaps (const gp_Pnt& thePnt) const
280 {
281   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
282     "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
283
284   Standard_Real aDepth = 0.0;
285   return hasIntersection (thePnt, aDepth);
286 }
287
288 // =======================================================================
289 // function : Overlaps
290 // purpose  :
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
296 {
297   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
298     "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
299
300   if (!raySegmentDistance (thePnt1, thePnt2, thePickResult))
301   {
302     return Standard_False;
303   }
304
305   return !theClipRange.IsClipped (thePickResult.Depth());
306 }
307
308 // =======================================================================
309 // function : Overlaps
310 // purpose  :
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
316 {
317   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
318     "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
319
320   if (theSensType == Select3D_TOS_BOUNDARY)
321   {
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)
328     {
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))
332       {
333         aMatchingSegmentsNb++;
334         thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
335       }
336     }
337
338     if (aMatchingSegmentsNb == -1)
339     {
340       return Standard_False;
341     }
342   }
343   else if (theSensType == Select3D_TOS_INTERIOR)
344   {
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())
353     {
354       // treat degenerated polygon as point
355       return Overlaps (theArrayOfPnts.First(), theClipRange, thePickResult);
356     }
357     else if (!rayPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
358     {
359       return Standard_False;
360     }
361   }
362
363   return !theClipRange.IsClipped (thePickResult.Depth());
364 }
365
366 // =======================================================================
367 // function : Overlaps
368 // purpose  :
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
376 {
377   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
378     "Error! SelectMgr_AxisIntersector::Overlaps() should be called after selection axis initialization");
379
380   if (theSensType == Select3D_TOS_BOUNDARY)
381   {
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);
385   }
386   else if (theSensType == Select3D_TOS_INTERIOR)
387   {
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())
394     {
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));
401     }
402
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())
406     {
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)
411       {
412         const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
413         const gp_Pnt& anEndPnt  = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
414         if (raySegmentDistance (aStartPnt, anEndPnt, aPickResult))
415         {
416           thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
417         }
418       }
419       thePickResult.SetSurfaceNormal (aTriangleNormal);
420       return !theClipRange.IsClipped (thePickResult.Depth());
421     }
422
423     // check if intersection point belongs to triangle's interior part
424     const gp_XYZ anEdge = (thePnt1.XYZ() - myAxis.Location().XYZ()) * (1.0 / anAlpha);
425
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]);
430
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;
433     if (isInterior)
434     {
435       thePickResult.SetDepth (myAxis.Location().Distance (aPtOnPlane));
436       thePickResult.SetPickedPoint (aPtOnPlane);
437       thePickResult.SetSurfaceNormal (aTriangleNormal);
438       return !theClipRange.IsClipped (thePickResult.Depth());
439     }
440
441     Standard_Real aMinDist = RealLast();
442     Standard_Integer aNearestEdgeIdx1 = -1;
443     for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
444     {
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)
449       {
450         aMinDist = aDist;
451         aNearestEdgeIdx1 = anEdgeIdx;
452       }
453     }
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()))
458     {
459       aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
460     }
461     if (raySegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult))
462     {
463       thePickResult.SetSurfaceNormal (aTriangleNormal);
464     }
465   }
466
467   return !theClipRange.IsClipped (thePickResult.Depth());
468 }
469
470 //=======================================================================
471 // function : GetNearPnt
472 // purpose  :
473 //=======================================================================
474 const gp_Pnt& SelectMgr_AxisIntersector::GetNearPnt() const
475 {
476   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
477     "Error! SelectMgr_AxisIntersector::GetNearPnt() should be called after selection axis initialization");
478
479   return myAxis.Location();
480 }
481
482 //=======================================================================
483 // function : GetFarPnt
484 // purpose  :
485 //=======================================================================
486 const gp_Pnt& SelectMgr_AxisIntersector::GetFarPnt() const
487 {
488   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
489     "Error! SelectMgr_AxisIntersector::GetFarPnt() should be called after selection axis initialization");
490
491   static gp_Pnt anInfPnt(RealLast(), RealLast(), RealLast());
492   return anInfPnt;
493 }
494
495 //=======================================================================
496 // function : GetViewRayDirection
497 // purpose  :
498 //=======================================================================
499 const gp_Dir& SelectMgr_AxisIntersector::GetViewRayDirection() const
500 {
501   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
502     "Error! SelectMgr_AxisIntersector::GetViewRayDirection() should be called after selection axis initialization");
503
504   return myAxis.Direction();
505 }
506
507 // =======================================================================
508 // function : DistToGeometryCenter
509 // purpose  :
510 // =======================================================================
511 Standard_Real SelectMgr_AxisIntersector::DistToGeometryCenter (const gp_Pnt& theCOG) const
512 {
513   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
514     "Error! SelectMgr_AxisIntersector::DistToGeometryCenter() should be called after selection axis initialization");
515
516   return theCOG.Distance (myAxis.Location());
517 }
518
519 // =======================================================================
520 // function : DetectedPoint
521 // purpose  :
522 // =======================================================================
523 gp_Pnt SelectMgr_AxisIntersector::DetectedPoint (const Standard_Real theDepth) const
524 {
525   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point,
526     "Error! SelectMgr_AxisIntersector::DetectedPoint() should be called after selection axis initialization");
527
528   return myAxis.Location().XYZ() + myAxis.Direction().XYZ() * theDepth;
529 }
530
531 //=======================================================================
532 //function : DumpJson
533 //purpose  : 
534 //=======================================================================
535 void SelectMgr_AxisIntersector::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
536 {
537   OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_AxisIntersector)
538   OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_BaseIntersector)
539
540   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myAxis)
541 }