0033661: Data Exchange, Step Import - Tessellated GDTs are not imported
[occt.git] / src / SelectMgr / SelectMgr_RectangularFrustum.cxx
1 // Created on: 2014-05-22
2 // Created by: Varvara POSKONINA
3 // Copyright (c) 2005-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <SelectMgr_RectangularFrustum.hxx>
17
18 #include <BVH_Tools.hxx>
19 #include <gp_Pln.hxx>
20 #include <NCollection_Vector.hxx>
21 #include <Poly_Array1OfTriangle.hxx>
22 #include <SelectMgr_FrustumBuilder.hxx>
23 #include <SelectMgr_ViewClipRange.hxx>
24
25 // =======================================================================
26 // function : Constructor
27 // purpose  :
28 // =======================================================================
29 SelectMgr_RectangularFrustum::SelectMgr_RectangularFrustum()
30   : myScale(1.0)
31 {
32 }
33
34 // =======================================================================
35 // function : segmentSegmentDistance
36 // purpose  :
37 // =======================================================================
38 void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
39                                                            const gp_Pnt& theSegPnt2,
40                                                            SelectBasics_PickResult& thePickResult) const
41 {
42   gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
43   gp_XYZ aV = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane
44   gp_XYZ aW = theSegPnt1.XYZ() - myNearPickedPnt.XYZ();
45
46   Standard_Real anA = anU.Dot (anU);
47   Standard_Real aB = anU.Dot (aV);
48   Standard_Real aC = aV.Dot (aV);
49   Standard_Real aD = anU.Dot (aW);
50   Standard_Real anE = aV.Dot (aW);
51   Standard_Real aCoef = anA * aC - aB * aB;
52   Standard_Real aSn = aCoef;
53   Standard_Real aTc, aTn, aTd = aCoef;
54
55   if (aCoef < gp::Resolution())
56   {
57     aTn = anE;
58     aTd = aC;
59   }
60   else
61   {
62     aSn = (aB * anE - aC * aD);
63     aTn = (anA * anE - aB * aD);
64     if (aSn < 0.0)
65     {
66       aTn = anE;
67       aTd = aC;
68     }
69     else if (aSn > aCoef)
70     {
71       aTn = anE + aB;
72       aTd = aC;
73     }
74   }
75
76   if (aTn < 0.0)
77   {
78     aTn = 0.0;
79   }
80   else if (aTn > aTd)
81   {
82     aTn = aTd;
83   }
84   aTc = (Abs (aTd) < gp::Resolution() ? 0.0 : aTn / aTd);
85
86   const gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + aV * aTc;
87   thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
88
89   const gp_Vec aPickedVec = aClosestPnt.XYZ() - theSegPnt1.XYZ();
90   const gp_Vec aFigureVec = theSegPnt2.XYZ()  - theSegPnt1.XYZ();
91   const Standard_Real aPickedVecMod = aPickedVec.Magnitude();
92   const Standard_Real aFigureVecMod = aFigureVec.Magnitude();
93   if (aPickedVecMod <= gp::Resolution()
94    || aFigureVecMod <= gp::Resolution())
95   {
96     thePickResult.SetPickedPoint (aClosestPnt);
97     return;
98   }
99
100   const Standard_Real aCosOfAngle = aFigureVec.Dot (aPickedVec) / (aPickedVecMod * aFigureVecMod);
101   const Standard_Real aSegPntShift = Min(aFigureVecMod, Max(0.0, aCosOfAngle * aPickedVecMod));
102   thePickResult.SetPickedPoint (theSegPnt1.XYZ() + aFigureVec.XYZ() * (aSegPntShift / aFigureVecMod));
103 }
104
105 // =======================================================================
106 // function : segmentPlaneIntersection
107 // purpose  :
108 // =======================================================================
109 bool SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePlane,
110                                                              const gp_Pnt& thePntOnPlane,
111                                                              SelectBasics_PickResult& thePickResult) const
112 {
113   gp_XYZ anU = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane by > 1.0 check
114   gp_XYZ aW = myNearPickedPnt.XYZ() - thePntOnPlane.XYZ();
115   Standard_Real aD = thePlane.Dot (anU);
116   Standard_Real aN = -thePlane.Dot (aW);
117
118   if (Abs (aD) < Precision::Confusion())
119   {
120     if (Abs (aN) < Precision::Angular())
121     {
122       thePickResult.Invalidate();
123       return false;
124     }
125     else
126     {
127       thePickResult.Invalidate();
128       return false;
129     }
130   }
131
132   Standard_Real aParam = aN / aD;
133   if (aParam < 0.0 || aParam > 1.0) // > 1.0 check could be removed for an infinite ray and anU=myViewRayDir
134   {
135     thePickResult.Invalidate();
136     return false;
137   }
138
139   gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + anU * aParam;
140   thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
141   return true;
142 }
143
144 namespace
145 {
146   // =======================================================================
147   // function : computeFrustum
148   // purpose  : Computes base frustum data: its vertices and edge directions
149   // =======================================================================
150   void computeFrustum (const gp_Pnt2d theMinPnt, const gp_Pnt2d& theMaxPnt,
151                        const Handle(SelectMgr_FrustumBuilder)& theBuilder,
152                        gp_Pnt* theVertices, gp_Vec* theEdges)
153   {
154     // LeftTopNear
155     theVertices[0] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
156                                                         theMaxPnt.Y(),
157                                                         0.0);
158     // LeftTopFar
159     theVertices[1] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
160                                                         theMaxPnt.Y(),
161                                                         1.0);
162     // LeftBottomNear
163     theVertices[2] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
164                                                         theMinPnt.Y(),
165                                                         0.0);
166     // LeftBottomFar
167     theVertices[3] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
168                                                         theMinPnt.Y(),
169                                                         1.0);
170     // RightTopNear
171     theVertices[4] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
172                                                         theMaxPnt.Y(),
173                                                         0.0);
174     // RightTopFar
175     theVertices[5] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
176                                                         theMaxPnt.Y(),
177                                                         1.0);
178     // RightBottomNear
179     theVertices[6] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
180                                                         theMinPnt.Y(),
181                                                         0.0);
182     // RightBottomFar
183     theVertices[7] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
184                                                         theMinPnt.Y(),
185                                                         1.0);
186
187     // Horizontal
188     theEdges[0] = theVertices[4].XYZ() - theVertices[0].XYZ();
189     // Vertical
190     theEdges[1] = theVertices[2].XYZ() - theVertices[0].XYZ();
191     // LeftLower
192     theEdges[2] = theVertices[2].XYZ() - theVertices[3].XYZ();
193     // RightLower
194     theEdges[3] = theVertices[6].XYZ() - theVertices[7].XYZ();
195     // LeftUpper
196     theEdges[4] = theVertices[0].XYZ() - theVertices[1].XYZ();
197     // RightUpper
198     theEdges[5] = theVertices[4].XYZ() - theVertices[5].XYZ();
199   }
200
201   // =======================================================================
202   // function : computeNormals
203   // purpose  : Computes normals to frustum faces
204   // =======================================================================
205   void computeNormals (const gp_Vec* theEdges, gp_Vec* theNormals)
206   {
207     // Top
208     theNormals[0] = theEdges[0].Crossed (theEdges[4]);
209     // Bottom
210     theNormals[1] = theEdges[2].Crossed (theEdges[0]);
211     // Left
212     theNormals[2] = theEdges[4].Crossed (theEdges[1]);
213     // Right
214     theNormals[3] = theEdges[1].Crossed (theEdges[5]);
215     // Near
216     theNormals[4] = theEdges[0].Crossed (theEdges[1]);
217     // Far
218     theNormals[5] = -theNormals[4];
219   }
220 }
221
222 // =======================================================================
223 // function : cacheVertexProjections
224 // purpose  : Caches projection of frustum's vertices onto its plane directions
225 //            and {i, j, k}
226 // =======================================================================
227 void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const
228 {
229   if (theFrustum->Camera()->IsOrthographic())
230   {
231     // project vertices onto frustum normals
232     // Since orthographic view volume's faces are always a pairwise translation of
233     // one another, only 2 vertices that belong to opposite faces can be projected
234     // to simplify calculations.
235     Standard_Integer aVertIdxs[6] = { LeftTopNear, LeftBottomNear,       // opposite planes in height direction
236                                       LeftBottomNear, RightBottomNear,   // opposite planes in width direcion
237                                       LeftBottomFar, RightBottomNear };  // opposite planes in depth direction
238     for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 5; aPlaneIdx += 2)
239     {
240       Standard_Real aProj1 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx]].XYZ());
241       Standard_Real aProj2 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx + 1]].XYZ());
242       theFrustum->myMinVertsProjections[aPlaneIdx] = Min (aProj1, aProj2);
243       theFrustum->myMaxVertsProjections[aPlaneIdx] = Max (aProj1, aProj2);
244     }
245   }
246   else
247   {
248     // project all vertices onto frustum normals
249     for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
250     {
251       Standard_Real aMax = -DBL_MAX;
252       Standard_Real aMin = DBL_MAX;
253       const gp_XYZ& aPlane = theFrustum->myPlanes[aPlaneIdx].XYZ();
254       for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
255       {
256         Standard_Real aProjection = aPlane.Dot (theFrustum->myVertices[aVertIdx].XYZ());
257         aMin = Min (aMin, aProjection);
258         aMax = Max (aMax, aProjection);
259       }
260       theFrustum->myMinVertsProjections[aPlaneIdx] = aMin;
261       theFrustum->myMaxVertsProjections[aPlaneIdx] = aMax;
262     }
263   }
264
265   // project vertices onto {i, j, k}
266   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
267   {
268     Standard_Real aMax = -DBL_MAX;
269     Standard_Real aMin = DBL_MAX;
270     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
271     {
272       const gp_XYZ& aVert = theFrustum->myVertices[aVertIdx].XYZ();
273       aMax = Max (aVert.GetData()[aDim], aMax);
274       aMin = Min (aVert.GetData()[aDim], aMin);
275     }
276     theFrustum->myMaxOrthoVertsProjections[aDim] = aMax;
277     theFrustum->myMinOrthoVertsProjections[aDim] = aMin;
278   }
279 }
280
281 // =======================================================================
282 // function : Init
283 // purpose  :
284 // =======================================================================
285 void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d &thePoint)
286 {
287   mySelectionType = SelectMgr_SelectionType_Point;
288   mySelRectangle.SetMousePos (thePoint);
289 }
290
291 // =======================================================================
292 // function : Init
293 // purpose  :
294 // =======================================================================
295 void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d& theMinPnt,
296                                          const gp_Pnt2d& theMaxPnt)
297 {
298   mySelectionType = SelectMgr_SelectionType_Box;
299   mySelRectangle.SetMinPnt (theMinPnt);
300   mySelRectangle.SetMaxPnt (theMaxPnt);
301 }
302
303 // =======================================================================
304 // function : Build
305 // purpose  :
306 // =======================================================================
307 void SelectMgr_RectangularFrustum::Build()
308 {
309   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
310     "Error! SelectMgr_RectangularFrustum::Build() should be called after selection frustum initialization");
311   gp_Pnt2d aMinPnt, aMaxPnt;
312   if (mySelectionType == SelectMgr_SelectionType_Point)
313   {
314     const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
315     myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 0.0);
316     myFarPickedPnt  = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 1.0);
317
318     aMinPnt.SetCoord (aMousePos.X() - myPixelTolerance * 0.5,
319                       aMousePos.Y() - myPixelTolerance * 0.5);
320     aMaxPnt.SetCoord (aMousePos.X() + myPixelTolerance * 0.5,
321                       aMousePos.Y() + myPixelTolerance * 0.5);
322   }
323   else
324   {
325     aMinPnt = mySelRectangle.MinPnt();
326     aMaxPnt = mySelRectangle.MaxPnt();
327     myNearPickedPnt = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
328                                                         (aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
329                                                         0.0);
330     myFarPickedPnt  = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
331                                                         (aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
332                                                         1.0);
333   }
334
335   myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ();
336
337   // calculate base frustum characteristics: vertices and edge directions
338   computeFrustum (aMinPnt, aMaxPnt, myBuilder, myVertices, myEdgeDirs);
339
340   // compute frustum normals
341   computeNormals (myEdgeDirs, myPlanes);
342
343   // compute vertices projections onto frustum normals and
344   // {i, j, k} vectors and store them to corresponding class fields
345   cacheVertexProjections (this);
346
347   myScale = 1.0;
348 }
349
350 // =======================================================================
351 // function : ScaleAndTransform
352 // purpose  : IMPORTANT: Scaling makes sense only for frustum built on a single point!
353 //            Note that this method does not perform any checks on type of the frustum.
354 //            Returns a copy of the frustum resized according to the scale factor given
355 //            and transforms it using the matrix given.
356 //            There are no default parameters, but in case if:
357 //                - transformation only is needed: @theScaleFactor must be initialized
358 //                  as any negative value;
359 //                - scale only is needed: @theTrsf must be set to gp_Identity.
360 // =======================================================================
361 Handle(SelectMgr_BaseIntersector) SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
362                                                                                    const gp_GTrsf& theTrsf,
363                                                                                    const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
364 {
365   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
366     "Error! SelectMgr_RectangularFrustum::ScaleAndTransform() should be called after selection frustum initialization");
367
368   Standard_ASSERT_RAISE (theScaleFactor >= 0,
369     "Error! Pixel tolerance for selection should not be negative");
370
371   Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
372   const Standard_Boolean isToScale = theScaleFactor != 1;
373   const Standard_Boolean isToTrsf  = theTrsf.Form() != gp_Identity;
374
375   if (!isToScale && !isToTrsf)
376   {
377     aRes->SetBuilder (theBuilder);
378     return aRes;
379   }
380
381   aRes->SetCamera (myCamera);
382   const SelectMgr_RectangularFrustum* aRef = this;
383
384   if (isToScale)
385   {
386     aRes->myNearPickedPnt = myNearPickedPnt;
387     aRes->myFarPickedPnt  = myFarPickedPnt;
388     aRes->myViewRayDir    = myViewRayDir;
389
390     const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
391     const gp_Pnt2d aMinPnt (aMousePos.X() - theScaleFactor * 0.5,
392                             aMousePos.Y() - theScaleFactor * 0.5);
393     const gp_Pnt2d aMaxPnt (aMousePos.X() + theScaleFactor * 0.5,
394                             aMousePos.Y() + theScaleFactor * 0.5);
395
396     // recompute base frustum characteristics from scratch
397     computeFrustum (aMinPnt, aMaxPnt, myBuilder, aRes->myVertices, aRes->myEdgeDirs);
398
399     aRef = aRes.get();
400   }
401
402   if (isToTrsf)
403   {
404     const Standard_Real aRefScale = aRef->myFarPickedPnt.SquareDistance (aRef->myNearPickedPnt);
405
406     gp_Pnt aPoint = aRef->myNearPickedPnt;
407     theTrsf.Transforms (aPoint.ChangeCoord());
408     aRes->myNearPickedPnt = aPoint;
409
410     aPoint.SetXYZ (aRef->myFarPickedPnt.XYZ());
411     theTrsf.Transforms (aPoint.ChangeCoord());
412     aRes->myFarPickedPnt = aPoint;
413
414     aRes->myViewRayDir = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
415
416     for (Standard_Integer anIt = 0; anIt < 8; anIt++)
417     {
418       aPoint = aRef->myVertices[anIt];
419       theTrsf.Transforms (aPoint.ChangeCoord());
420       aRes->myVertices[anIt] = aPoint;
421     }
422
423     // Horizontal
424     aRes->myEdgeDirs[0] = aRes->myVertices[4].XYZ() - aRes->myVertices[0].XYZ();
425     // Vertical
426     aRes->myEdgeDirs[1] = aRes->myVertices[2].XYZ() - aRes->myVertices[0].XYZ();
427     // LeftLower
428     aRes->myEdgeDirs[2] = aRes->myVertices[2].XYZ() - aRes->myVertices[3].XYZ();
429     // RightLower
430     aRes->myEdgeDirs[3] = aRes->myVertices[6].XYZ() - aRes->myVertices[7].XYZ();
431     // LeftUpper
432     aRes->myEdgeDirs[4] = aRes->myVertices[0].XYZ() - aRes->myVertices[1].XYZ();
433     // RightUpper
434     aRes->myEdgeDirs[5] = aRes->myVertices[4].XYZ() - aRes->myVertices[5].XYZ();
435
436     // Compute scale to transform depth from local coordinate system to world coordinate system
437     aRes->myScale = Sqrt (aRefScale / aRes->myFarPickedPnt.SquareDistance (aRes->myNearPickedPnt));
438   }
439
440   aRes->SetBuilder (theBuilder);
441
442   // compute frustum normals
443   computeNormals (aRes->myEdgeDirs, aRes->myPlanes);
444
445   cacheVertexProjections (aRes.get());
446
447   aRes->mySelectionType = mySelectionType;
448   aRes->mySelRectangle = mySelRectangle;
449   return aRes;
450 }
451
452 // =======================================================================
453 // function : CopyWithBuilder
454 // purpose  : Returns a copy of the frustum using the given frustum builder configuration.
455 //            Returned frustum should be re-constructed before being used.
456 // =======================================================================
457 Handle(SelectMgr_BaseIntersector) SelectMgr_RectangularFrustum::CopyWithBuilder (const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
458 {
459   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
460     "Error! SelectMgr_RectangularFrustum::CopyWithBuilder() should be called after selection frustum initialization");
461
462   Standard_ASSERT_RAISE (!theBuilder.IsNull(), 
463     "Error! SelectMgr_RectangularFrustum::CopyWithBuilder() should be called with valid builder");
464
465   Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
466   aRes->mySelectionType = mySelectionType;
467   aRes->mySelRectangle = mySelRectangle;
468   aRes->myPixelTolerance = myPixelTolerance;
469   aRes->SetBuilder (theBuilder);
470
471   return aRes;
472 }
473
474 // =======================================================================
475 // function : IsScalable
476 // purpose  :
477 // =======================================================================
478 Standard_Boolean SelectMgr_RectangularFrustum::IsScalable() const
479 {
480   return mySelectionType == SelectMgr_SelectionType_Point;
481 }
482
483 // =======================================================================
484 // function : OverlapsBox
485 // purpose  : Returns true if selecting volume is overlapped by
486 //            axis-aligned bounding box with minimum corner at point
487 //            theMinPnt and maximum at point theMaxPnt
488 // =======================================================================
489 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
490                                                             const SelectMgr_Vec3& theBoxMax,
491                                                             Standard_Boolean*     theInside) const
492 {
493   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
494     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
495
496   return hasBoxOverlap (theBoxMin, theBoxMax, theInside);
497 }
498
499 // =======================================================================
500 // function : OverlapsBox
501 // purpose  : SAT intersection test between defined volume and
502 //            given axis-aligned box
503 // =======================================================================
504 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
505                                                             const SelectMgr_Vec3& theBoxMax,
506                                                             const SelectMgr_ViewClipRange& theClipRange,
507                                                             SelectBasics_PickResult& thePickResult) const
508 {
509   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
510     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
511
512   if (!hasBoxOverlap (theBoxMin, theBoxMax))
513     return Standard_False;
514
515   Standard_Real aDepth = 0.0;
516   BVH_Ray<Standard_Real, 3> aRay(SelectMgr_Vec3(myNearPickedPnt.X(), myNearPickedPnt.Y(), myNearPickedPnt.Z()),
517                                  SelectMgr_Vec3(myViewRayDir.X(), myViewRayDir.Y(), myViewRayDir.Z()));
518   Standard_Real aTimeEnter, aTimeLeave;
519   if (!BVH_Tools<Standard_Real, 3>::RayBoxIntersection (aRay, theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
520   {
521     gp_Pnt aNearestPnt (RealLast(), RealLast(), RealLast());
522     aNearestPnt.SetX (Max (Min (myNearPickedPnt.X(), theBoxMax.x()), theBoxMin.x()));
523     aNearestPnt.SetY (Max (Min (myNearPickedPnt.Y(), theBoxMax.y()), theBoxMin.y()));
524     aNearestPnt.SetZ (Max (Min (myNearPickedPnt.Z(), theBoxMax.z()), theBoxMin.z()));
525
526     aDepth = aNearestPnt.Distance (myNearPickedPnt);
527     thePickResult.SetDepth (aDepth);
528     return !theClipRange.IsClipped (thePickResult.Depth());
529   }
530
531   Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
532   aRange.GetMin (aDepth);
533
534   if (!theClipRange.GetNearestDepth (aRange, aDepth))
535   {
536     return Standard_False;
537   }
538
539   thePickResult.SetDepth (aDepth);
540
541   return Standard_True;
542 }
543
544 // =======================================================================
545 // function : OverlapsPoint
546 // purpose  : Intersection test between defined volume and given point
547 // =======================================================================
548 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt,
549                                                               const SelectMgr_ViewClipRange& theClipRange,
550                                                               SelectBasics_PickResult& thePickResult) const
551 {
552   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
553     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
554
555   if (!hasPointOverlap (thePnt))
556     return Standard_False;
557
558   gp_XYZ aV = thePnt.XYZ() - myNearPickedPnt.XYZ();
559   const Standard_Real aDepth = aV.Dot (myViewRayDir.XYZ());
560
561   thePickResult.SetDepth (Abs (aDepth) * myScale);
562   thePickResult.SetPickedPoint (thePnt);
563
564   return !theClipRange.IsClipped (thePickResult.Depth());
565 }
566
567 // =======================================================================
568 // function : OverlapsPoint
569 // purpose  : Intersection test between defined volume and given point
570 // =======================================================================
571 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt) const
572 {
573   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
574     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
575
576   return hasPointOverlap (thePnt);
577 }
578
579 // =======================================================================
580 // function : OverlapsSegment
581 // purpose  : Checks if line segment overlaps selecting frustum
582 // =======================================================================
583 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSegment (const gp_Pnt& thePnt1,
584                                                                 const gp_Pnt& thePnt2,
585                                                                 const SelectMgr_ViewClipRange& theClipRange,
586                                                                 SelectBasics_PickResult& thePickResult) const
587 {
588   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
589     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
590
591   if (!hasSegmentOverlap (thePnt1, thePnt2))
592     return Standard_False;
593
594   segmentSegmentDistance (thePnt1, thePnt2, thePickResult);
595
596   return !theClipRange.IsClipped (thePickResult.Depth());
597 }
598
599 // =======================================================================
600 // function : OverlapsPolygon
601 // purpose  : SAT intersection test between defined volume and given
602 //            ordered set of points, representing line segments. The test
603 //            may be considered of interior part or boundary line defined
604 //            by segments depending on given sensitivity type
605 // =======================================================================
606 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPolygon (const TColgp_Array1OfPnt& theArrayOfPnts,
607                                                                 Select3D_TypeOfSensitivity theSensType,
608                                                                 const SelectMgr_ViewClipRange& theClipRange,
609                                                                 SelectBasics_PickResult& thePickResult) const
610 {
611   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
612     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
613
614   if (theSensType == Select3D_TOS_BOUNDARY)
615   {
616     Standard_Integer aMatchingSegmentsNb = -1;
617     SelectBasics_PickResult aPickResult;
618     thePickResult.Invalidate();
619     const Standard_Integer aLower  = theArrayOfPnts.Lower();
620     const Standard_Integer anUpper = theArrayOfPnts.Upper();
621     for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
622     {
623       const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
624       const gp_Pnt& aEndPnt   = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
625       if (hasSegmentOverlap (aStartPnt, aEndPnt))
626       {
627         aMatchingSegmentsNb++;
628         segmentSegmentDistance (aStartPnt, aEndPnt, aPickResult);
629         thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
630       }
631     }
632
633     if (aMatchingSegmentsNb == -1)
634       return Standard_False;
635   }
636   else if (theSensType == Select3D_TOS_INTERIOR)
637   {
638     gp_Vec aPolyNorm (gp_XYZ (RealLast(), RealLast(), RealLast()));
639     if (!hasPolygonOverlap (theArrayOfPnts, aPolyNorm))
640     {
641       return Standard_False;
642     }
643
644     if (aPolyNorm.Magnitude() <= Precision::Confusion())
645     {
646       // treat degenerated polygon as point
647       return OverlapsPoint (theArrayOfPnts.First(), theClipRange, thePickResult);
648     }
649     else if (!segmentPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
650     {
651       return Standard_False;
652     }
653   }
654
655   return !theClipRange.IsClipped (thePickResult.Depth());
656 }
657
658 // =======================================================================
659 // function : OverlapsTriangle
660 // purpose  : SAT intersection test between defined volume and given
661 //            triangle. The test may be considered of interior part or
662 //            boundary line defined by triangle vertices depending on
663 //            given sensitivity type
664 // =======================================================================
665 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsTriangle (const gp_Pnt& thePnt1,
666                                                                  const gp_Pnt& thePnt2,
667                                                                  const gp_Pnt& thePnt3,
668                                                                  Select3D_TypeOfSensitivity theSensType,
669                                                                  const SelectMgr_ViewClipRange& theClipRange,
670                                                                  SelectBasics_PickResult& thePickResult) const
671 {
672   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
673     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
674
675   if (theSensType == Select3D_TOS_BOUNDARY)
676   {
677     const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
678     const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
679     return OverlapsPolygon (aPntsArray, Select3D_TOS_BOUNDARY, theClipRange, thePickResult);
680   }
681   else if (theSensType == Select3D_TOS_INTERIOR)
682   {
683     gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
684     if (!hasTriangleOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
685     {
686       return Standard_False;
687     }
688
689     const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
690                                  thePnt3.XYZ() - thePnt2.XYZ(),
691                                  thePnt1.XYZ() - thePnt3.XYZ() };
692           if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
693     {
694       // consider degenerated triangle as point or segment
695       return aTrEdges[0].SquareModulus() > gp::Resolution()
696            ? OverlapsSegment (thePnt1, thePnt2, theClipRange, thePickResult)
697            : (aTrEdges[1].SquareModulus() > gp::Resolution()
698             ? OverlapsSegment (thePnt2, thePnt3, theClipRange, thePickResult)
699             : OverlapsPoint (thePnt1, theClipRange, thePickResult));
700     }
701
702     const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
703     const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myViewRayDir.XYZ());
704     if (Abs (anAlpha) < gp::Resolution())
705     {
706       // handle the case when triangle normal and selecting frustum direction are orthogonal
707       SelectBasics_PickResult aPickResult;
708       thePickResult.Invalidate();
709       for (Standard_Integer anEdgeIter = 0; anEdgeIter < 3; ++anEdgeIter)
710       {
711         const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
712         const gp_Pnt& anEndPnt  = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
713         segmentSegmentDistance (aStartPnt, anEndPnt, aPickResult);
714         thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
715       }
716       thePickResult.SetSurfaceNormal (aTriangleNormal);
717       return !theClipRange.IsClipped (thePickResult.Depth());
718     }
719
720     // check if intersection point belongs to triangle's interior part
721     const gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
722
723     const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
724     const gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
725     const Standard_Real anU = aVec.Dot (aTrEdges[2]);
726     const Standard_Real aV  = aVec.Dot (aTrEdges[0]);
727
728     const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
729     const gp_Pnt aPtOnPlane = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
730     if (isInterior)
731     {
732       thePickResult.SetDepth (myNearPickedPnt.Distance (aPtOnPlane) * myScale);
733       thePickResult.SetPickedPoint (aPtOnPlane);
734       thePickResult.SetSurfaceNormal (aTriangleNormal);
735       return !theClipRange.IsClipped (thePickResult.Depth());
736     }
737
738     Standard_Real aMinDist = RealLast();
739     Standard_Integer aNearestEdgeIdx1 = -1;
740     for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
741     {
742       gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
743       Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
744       Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
745       if (aDist < aMinDist)
746       {
747         aMinDist = aDist;
748         aNearestEdgeIdx1 = anEdgeIdx;
749       }
750     }
751     Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
752     const gp_Vec aVec12 (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]);
753     if (aVec12.SquareMagnitude() > gp::Resolution()
754      && myViewRayDir.IsParallel (aVec12, Precision::Angular()))
755     {
756       aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
757     }
758     segmentSegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult);
759     thePickResult.SetSurfaceNormal (aTriangleNormal);
760   }
761
762   return !theClipRange.IsClipped (thePickResult.Depth());
763 }
764
765 //=======================================================================
766 // function : OverlapsCylinder
767 // purpose  :
768 //=======================================================================
769 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
770                                                                  const Standard_Real theTopRad,
771                                                                  const Standard_Real theHeight,
772                                                                  const gp_Trsf& theTrsf,
773                                                                  const Standard_Boolean theIsHollow,
774                                                                  const SelectMgr_ViewClipRange& theClipRange,
775                                                                  SelectBasics_PickResult& thePickResult) const
776 {
777   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
778     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
779   Standard_Real aTimes[2] = { 0.0, 0.0 };
780   const gp_Trsf aTrsfInv = theTrsf.Inverted();
781   const gp_Pnt  aLoc     = myNearPickedPnt.Transformed (aTrsfInv);
782   const gp_Dir  aRayDir  = myViewRayDir   .Transformed (aTrsfInv);
783   if (!RayCylinderIntersection (theBottomRad, theTopRad, theHeight, aLoc, aRayDir, theIsHollow, aTimes[0], aTimes[1]))
784   {
785     return Standard_False;
786   }
787
788   Standard_Integer aResTime = 0;
789   thePickResult.SetDepth (aTimes[aResTime] * myScale);
790   if (theClipRange.IsClipped (thePickResult.Depth()))
791   {
792     aResTime = 1;
793     thePickResult.SetDepth (aTimes[aResTime] * myScale);
794   }
795
796   const gp_Pnt aPntOnCylinder = aLoc.XYZ() + aRayDir.XYZ() * aTimes[aResTime];
797   if (Abs (aPntOnCylinder.Z()) < Precision::Confusion())
798   {
799     thePickResult.SetSurfaceNormal (-gp::DZ().Transformed (theTrsf));
800   }
801   else if (Abs (aPntOnCylinder.Z() - theHeight) < Precision::Confusion())
802   {
803     thePickResult.SetSurfaceNormal (gp::DZ().Transformed (theTrsf));
804   }
805   else
806   {
807     thePickResult.SetSurfaceNormal (gp_Vec (aPntOnCylinder.X(), aPntOnCylinder.Y(), 0.0).Transformed (theTrsf));
808   }
809   thePickResult.SetPickedPoint (aPntOnCylinder.Transformed (theTrsf));
810   return !theClipRange.IsClipped (thePickResult.Depth());
811 }
812
813 //=======================================================================
814 // function : OverlapsCircle
815 // purpose  :
816 //=======================================================================
817 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCircle (const Standard_Real theRadius,
818                                                                const gp_Trsf& theTrsf,
819                                                                const Standard_Boolean theIsFilled,
820                                                                const SelectMgr_ViewClipRange& theClipRange,
821                                                                SelectBasics_PickResult& thePickResult) const
822 {
823   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
824     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
825   Standard_Real aTime = 0.0;
826   const gp_Trsf aTrsfInv = theTrsf.Inverted();
827   const gp_Pnt  aLoc = myNearPickedPnt.Transformed (aTrsfInv);
828   const gp_Dir  aRayDir = myViewRayDir.Transformed (aTrsfInv);
829   if (!theIsFilled)
830   {
831     if (!hasCircleOverlap (theRadius, theTrsf, theIsFilled, NULL))
832     {
833       return Standard_False;
834     }
835     if (aRayDir.Z() != 0)
836     {
837       aTime = (0 - aLoc.Z()) / aRayDir.Z();
838     }
839   }
840   else if (!RayCircleIntersection (theRadius, aLoc, aRayDir, theIsFilled, aTime))
841   {
842     return Standard_False;
843   }
844
845   thePickResult.SetDepth (aTime * myScale);
846   if (theClipRange.IsClipped (thePickResult.Depth()))
847   {
848     thePickResult.SetDepth (aTime * myScale);
849   }
850
851   const gp_Pnt aPntOnCircle = aLoc.XYZ() + aRayDir.XYZ() * aTime;
852   if (Abs (aPntOnCircle.Z()) < Precision::Confusion())
853   {
854     thePickResult.SetSurfaceNormal (-gp::DZ().Transformed (theTrsf));
855   }
856   else
857   {
858     thePickResult.SetSurfaceNormal (gp_Vec (aPntOnCircle.X(), aPntOnCircle.Y(), 0.0).Transformed (theTrsf));
859   }
860   thePickResult.SetPickedPoint (aPntOnCircle.Transformed (theTrsf));
861   return !theClipRange.IsClipped (thePickResult.Depth());
862 }
863
864 //=======================================================================
865 // function : isIntersectCircle
866 // purpose  :
867 //=======================================================================
868 Standard_Boolean SelectMgr_RectangularFrustum::isIntersectCircle (const Standard_Real theRadius,
869                                                                   const gp_Pnt& theCenter,
870                                                                   const gp_Trsf& theTrsf,
871                                                                   const TColgp_Array1OfPnt& theVertices) const
872 {
873   const gp_Trsf aTrsfInv = theTrsf.Inverted();
874   const gp_Dir aRayDir = gp_Dir (myEdgeDirs[4 == 4 ? 4 : 0]).Transformed (aTrsfInv);
875   if (aRayDir.Z() == 0.0)
876   {
877     return false;
878   }
879
880   for (Standard_Integer anIdx = theVertices.Lower(); anIdx <= theVertices.Upper(); anIdx++)
881   {
882     const gp_Pnt aPntStart = theVertices.Value (anIdx).Transformed (aTrsfInv);
883     const gp_Pnt aPntFinish = anIdx == theVertices.Upper()
884       ? theVertices.Value (theVertices.Lower()).Transformed (aTrsfInv)
885       : theVertices.Value (anIdx + 1).Transformed (aTrsfInv);
886
887     // Project points on the end face plane
888     const Standard_Real aParam1 = (theCenter.Z() - aPntStart.Z()) / aRayDir.Z();
889     const Standard_Real aX1 = aPntStart.X() + aRayDir.X() * aParam1;
890     const Standard_Real anY1 = aPntStart.Y() + aRayDir.Y() * aParam1;
891
892     const Standard_Real aParam2 = (theCenter.Z() - aPntFinish.Z()) / aRayDir.Z();
893     const Standard_Real aX2 = aPntFinish.X() + aRayDir.X() * aParam2;
894     const Standard_Real anY2 = aPntFinish.Y() + aRayDir.Y() * aParam2;
895
896     // Solving quadratic equation anA * T^2 + 2 * aK * T + aC = 0
897     const Standard_Real anA = (aX1 - aX2) * (aX1 - aX2) + (anY1 - anY2) * (anY1 - anY2);
898     const Standard_Real aK = aX1 * (aX2 - aX1) + anY1 * (anY2 - anY1);
899     const Standard_Real aC = aX1 * aX1 + anY1 * anY1 - theRadius * theRadius;
900
901     const Standard_Real aDiscr = aK * aK - anA * aC;
902     if (aDiscr >= 0.0)
903     {
904       const Standard_Real aT1 = (-aK + Sqrt (aDiscr)) / anA;
905       const Standard_Real aT2 = (-aK - Sqrt (aDiscr)) / anA;
906       if ((aT1 >= 0 && aT1 <= 1) || (aT2 >= 0 && aT2 <= 1))
907       {
908         return true;
909       }
910     }
911   }
912   return false;
913 }
914
915 //=======================================================================
916 // function : isSegmentsIntersect
917 // purpose  :
918 //=======================================================================
919 Standard_Boolean SelectMgr_RectangularFrustum::isSegmentsIntersect (const gp_Pnt& thePnt1Seg1,
920                                                                     const gp_Pnt& thePnt2Seg1,
921                                                                     const gp_Pnt& thePnt1Seg2,
922                                                                     const gp_Pnt& thePnt2Seg2) const
923 {
924   const gp_Mat aMatPln (thePnt2Seg1.X() - thePnt1Seg1.X(), thePnt2Seg1.Y() - thePnt1Seg1.Y(), thePnt2Seg1.Z() - thePnt1Seg1.Z(),
925                         thePnt1Seg2.X() - thePnt1Seg1.X(), thePnt1Seg2.Y() - thePnt1Seg1.Y(), thePnt1Seg2.Z() - thePnt1Seg1.Z(),
926                         thePnt2Seg2.X() - thePnt1Seg1.X(), thePnt2Seg2.Y() - thePnt1Seg1.Y(), thePnt2Seg2.Z() - thePnt1Seg1.Z());
927   if (Abs (aMatPln.Determinant()) > Precision::Confusion())
928   {
929     return false;
930   }
931
932   Standard_Real aFst[4] = { thePnt1Seg1.X(), thePnt2Seg1.X(), thePnt1Seg2.X(), thePnt2Seg2.X() };
933   Standard_Real aSnd[4] = { thePnt1Seg1.Y(), thePnt2Seg1.Y(), thePnt1Seg2.Y(), thePnt2Seg2.Y() };
934   if (aFst[0] == aFst[2] && aFst[1] == aFst[3])
935   {
936     aFst[0] = thePnt1Seg1.Z();
937     aFst[1] = thePnt2Seg1.Z();
938     aFst[2] = thePnt1Seg2.Z();
939     aFst[3] = thePnt2Seg2.Z();
940   }
941   if (aSnd[0] == aSnd[2]
942    && aSnd[1] == aSnd[3])
943   {
944     aSnd[0] = thePnt1Seg1.Z();
945     aSnd[1] = thePnt2Seg1.Z();
946     aSnd[2] = thePnt1Seg2.Z();
947     aSnd[3] = thePnt2Seg2.Z();
948   }
949   const gp_Mat2d aMat (gp_XY (aFst[0] - aFst[1], aSnd[0] - aSnd[1]),
950                        gp_XY (aFst[3] - aFst[2], aSnd[3] - aSnd[2]));
951
952   const gp_Mat2d aMatU (gp_XY (aFst[0] - aFst[2], aSnd[0] - aSnd[2]),
953                         gp_XY (aFst[3] - aFst[2], aSnd[3] - aSnd[2]));
954
955   const gp_Mat2d aMatV (gp_XY (aFst[0] - aFst[1], aSnd[0] - aSnd[1]),
956                         gp_XY (aFst[0] - aFst[2], aSnd[0] - aSnd[2]));
957   if (aMat.Determinant() == 0.0)
958   {
959     return false;
960   }
961
962   const Standard_Real anU = aMatU.Determinant() / aMat.Determinant();
963   const Standard_Real aV = aMatV.Determinant() / aMat.Determinant();
964   if (anU >= 0.0 && anU <= 1.0
965    && aV >= 0.0 && aV <= 1.0)
966   {
967     return true;
968   }
969   return false;
970 }
971
972 //=======================================================================
973 // function : OverlapsCylinder
974 // purpose  :
975 //=======================================================================
976 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
977                                                                  const Standard_Real theTopRad,
978                                                                  const Standard_Real theHeight,
979                                                                  const gp_Trsf& theTrsf,
980                                                                  const Standard_Boolean theIsHollow,
981                                                                  Standard_Boolean* theInside) const
982 {
983   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
984     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
985
986   return hasCylinderOverlap (theBottomRad, theTopRad, theHeight, theTrsf, theIsHollow, theInside);
987 }
988
989 //=======================================================================
990 // function : OverlapsCircle
991 // purpose  :
992 //=======================================================================
993 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCircle (const Standard_Real theRadius,
994                                                                const gp_Trsf& theTrsf,
995                                                                const Standard_Boolean theIsFilled,
996                                                                Standard_Boolean* theInside) const
997 {
998   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
999     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
1000
1001   return hasCircleOverlap (theRadius, theTrsf, theIsFilled, theInside);
1002 }
1003
1004 // =======================================================================
1005 // function : GetMousePosition
1006 // purpose  :
1007 // =======================================================================
1008 const gp_Pnt2d& SelectMgr_RectangularFrustum::GetMousePosition() const
1009 {
1010   if (mySelectionType == SelectMgr_SelectionType_Point)
1011   {
1012     return mySelRectangle.MousePos();
1013   }
1014   return base_type::GetMousePosition();
1015 }
1016
1017 // =======================================================================
1018 // function : OverlapsSphere
1019 // purpose  :
1020 // =======================================================================
1021 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& theCenter,
1022                                                                const Standard_Real theRadius,
1023                                                                const SelectMgr_ViewClipRange& theClipRange,
1024                                                                SelectBasics_PickResult& thePickResult) const
1025 {
1026   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
1027     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
1028   Standard_Real aTimeEnter = 0.0, aTimeLeave = 0.0;
1029   if (!RaySphereIntersection (theCenter, theRadius, myNearPickedPnt, myViewRayDir, aTimeEnter, aTimeLeave))
1030   {
1031     return Standard_False;
1032   }
1033
1034   thePickResult.SetDepth (aTimeEnter * myScale);
1035   if (theClipRange.IsClipped (thePickResult.Depth()))
1036   {
1037     thePickResult.SetDepth (aTimeLeave * myScale);
1038   }
1039   gp_Pnt aPntOnSphere (myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * thePickResult.Depth() / myScale);
1040   gp_Vec aNormal (aPntOnSphere.XYZ() - theCenter.XYZ());
1041   thePickResult.SetPickedPoint (aPntOnSphere);
1042   thePickResult.SetSurfaceNormal (aNormal);
1043   return !theClipRange.IsClipped (thePickResult.Depth());
1044 }
1045
1046 // =======================================================================
1047 // function : OverlapsSphere
1048 // purpose  :
1049 // =======================================================================
1050 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& theCenter,
1051                                                                const Standard_Real theRadius,
1052                                                                Standard_Boolean* theInside) const
1053 {
1054   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
1055     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
1056   return hasSphereOverlap (theCenter, theRadius, theInside);
1057 }
1058
1059 // =======================================================================
1060 // function : DistToGeometryCenter
1061 // purpose  : Measures distance between 3d projection of user-picked
1062 //            screen point and given point theCOG
1063 // =======================================================================
1064 Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG) const
1065 {
1066   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
1067     "Error! SelectMgr_RectangularFrustum::DistToGeometryCenter() should be called after selection frustum initialization");
1068
1069   return theCOG.Distance (myNearPickedPnt) * myScale;
1070 }
1071
1072 // =======================================================================
1073 // function : DetectedPoint
1074 // purpose  : Calculates the point on a view ray that was detected during
1075 //            the run of selection algo by given depth
1076 // =======================================================================
1077 gp_Pnt SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
1078 {
1079   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point,
1080     "SelectMgr_RectangularFrustum::DetectedPoint() should be called only for Point selection type");
1081   return myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * theDepth / myScale;
1082 }
1083
1084 // =======================================================================
1085 // function : GetPlanes
1086 // purpose  :
1087 // =======================================================================
1088 void SelectMgr_RectangularFrustum::GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const
1089 {
1090   thePlaneEquations.Clear();
1091
1092   SelectMgr_Vec4 anEquation;
1093   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
1094   {
1095     const gp_Vec& aPlaneNorm = Camera()->IsOrthographic() && aPlaneIdx % 2 == 1 ?
1096       myPlanes[aPlaneIdx - 1].Reversed() : myPlanes[aPlaneIdx];
1097     anEquation.x() = aPlaneNorm.X();
1098     anEquation.y() = aPlaneNorm.Y();
1099     anEquation.z() = aPlaneNorm.Z();
1100     anEquation.w() = - (aPlaneNorm.XYZ().Dot (myVertices[aPlaneIdx % 2 == 0 ? aPlaneIdx : aPlaneIdx + 2].XYZ()));
1101     thePlaneEquations.Append (anEquation);
1102   }
1103 }
1104
1105 //=======================================================================
1106 //function : DumpJson
1107 //purpose  : 
1108 //=======================================================================
1109 void SelectMgr_RectangularFrustum::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
1110 {
1111   OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_RectangularFrustum)
1112   OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_Frustum)
1113
1114   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myNearPickedPnt)
1115   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myFarPickedPnt)
1116   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myViewRayDir)
1117   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MinPnt())
1118   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MaxPnt())
1119
1120   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myScale)
1121 }