0032337: Visualization - rename Overlaps() method in selection to more self-describab...
[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 <NCollection_Vector.hxx>
20 #include <Poly_Array1OfTriangle.hxx>
21 #include <SelectMgr_FrustumBuilder.hxx>
22 #include <SelectMgr_ViewClipRange.hxx>
23
24 // =======================================================================
25 // function : Constructor
26 // purpose  :
27 // =======================================================================
28 SelectMgr_RectangularFrustum::SelectMgr_RectangularFrustum()
29   : myScale(1.0)
30 {
31 }
32
33 // =======================================================================
34 // function : segmentSegmentDistance
35 // purpose  :
36 // =======================================================================
37 void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
38                                                            const gp_Pnt& theSegPnt2,
39                                                            SelectBasics_PickResult& thePickResult) const
40 {
41   gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
42   gp_XYZ aV = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane
43   gp_XYZ aW = theSegPnt1.XYZ() - myNearPickedPnt.XYZ();
44
45   Standard_Real anA = anU.Dot (anU);
46   Standard_Real aB = anU.Dot (aV);
47   Standard_Real aC = aV.Dot (aV);
48   Standard_Real aD = anU.Dot (aW);
49   Standard_Real anE = aV.Dot (aW);
50   Standard_Real aCoef = anA * aC - aB * aB;
51   Standard_Real aSn = aCoef;
52   Standard_Real aTc, aTn, aTd = aCoef;
53
54   if (aCoef < gp::Resolution())
55   {
56     aTn = anE;
57     aTd = aC;
58   }
59   else
60   {
61     aSn = (aB * anE - aC * aD);
62     aTn = (anA * anE - aB * aD);
63     if (aSn < 0.0)
64     {
65       aTn = anE;
66       aTd = aC;
67     }
68     else if (aSn > aCoef)
69     {
70       aTn = anE + aB;
71       aTd = aC;
72     }
73   }
74
75   if (aTn < 0.0)
76   {
77     aTn = 0.0;
78   }
79   else if (aTn > aTd)
80   {
81     aTn = aTd;
82   }
83   aTc = (Abs (aTd) < gp::Resolution() ? 0.0 : aTn / aTd);
84
85   const gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + aV * aTc;
86   thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
87
88   const gp_Vec aPickedVec = aClosestPnt.XYZ() - theSegPnt1.XYZ();
89   const gp_Vec aFigureVec = theSegPnt2.XYZ()  - theSegPnt1.XYZ();
90   const Standard_Real aPickedVecMod = aPickedVec.Magnitude();
91   const Standard_Real aFigureVecMod = aFigureVec.Magnitude();
92   if (aPickedVecMod <= gp::Resolution()
93    || aFigureVecMod <= gp::Resolution())
94   {
95     thePickResult.SetPickedPoint (aClosestPnt);
96     return;
97   }
98
99   const Standard_Real aCosOfAngle = aFigureVec.Dot (aPickedVec) / (aPickedVecMod * aFigureVecMod);
100   const Standard_Real aSegPntShift = Min(aFigureVecMod, Max(0.0, aCosOfAngle * aPickedVecMod));
101   thePickResult.SetPickedPoint (theSegPnt1.XYZ() + aFigureVec.XYZ() * (aSegPntShift / aFigureVecMod));
102 }
103
104 // =======================================================================
105 // function : segmentPlaneIntersection
106 // purpose  :
107 // =======================================================================
108 bool SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePlane,
109                                                              const gp_Pnt& thePntOnPlane,
110                                                              SelectBasics_PickResult& thePickResult) const
111 {
112   gp_XYZ anU = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane by > 1.0 check
113   gp_XYZ aW = myNearPickedPnt.XYZ() - thePntOnPlane.XYZ();
114   Standard_Real aD = thePlane.Dot (anU);
115   Standard_Real aN = -thePlane.Dot (aW);
116
117   if (Abs (aD) < Precision::Confusion())
118   {
119     if (Abs (aN) < Precision::Angular())
120     {
121       thePickResult.Invalidate();
122       return false;
123     }
124     else
125     {
126       thePickResult.Invalidate();
127       return false;
128     }
129   }
130
131   Standard_Real aParam = aN / aD;
132   if (aParam < 0.0 || aParam > 1.0) // > 1.0 check could be removed for an infinite ray and anU=myViewRayDir
133   {
134     thePickResult.Invalidate();
135     return false;
136   }
137
138   gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + anU * aParam;
139   thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
140   return true;
141 }
142
143 namespace
144 {
145   // =======================================================================
146   // function : computeFrustum
147   // purpose  : Computes base frustum data: its vertices and edge directions
148   // =======================================================================
149   void computeFrustum (const gp_Pnt2d theMinPnt, const gp_Pnt2d& theMaxPnt,
150                        const Handle(SelectMgr_FrustumBuilder)& theBuilder,
151                        gp_Pnt* theVertices, gp_Vec* theEdges)
152   {
153     // LeftTopNear
154     theVertices[0] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
155                                                         theMaxPnt.Y(),
156                                                         0.0);
157     // LeftTopFar
158     theVertices[1] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
159                                                         theMaxPnt.Y(),
160                                                         1.0);
161     // LeftBottomNear
162     theVertices[2] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
163                                                         theMinPnt.Y(),
164                                                         0.0);
165     // LeftBottomFar
166     theVertices[3] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
167                                                         theMinPnt.Y(),
168                                                         1.0);
169     // RightTopNear
170     theVertices[4] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
171                                                         theMaxPnt.Y(),
172                                                         0.0);
173     // RightTopFar
174     theVertices[5] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
175                                                         theMaxPnt.Y(),
176                                                         1.0);
177     // RightBottomNear
178     theVertices[6] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
179                                                         theMinPnt.Y(),
180                                                         0.0);
181     // RightBottomFar
182     theVertices[7] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
183                                                         theMinPnt.Y(),
184                                                         1.0);
185
186     // Horizontal
187     theEdges[0] = theVertices[4].XYZ() - theVertices[0].XYZ();
188     // Vertical
189     theEdges[1] = theVertices[2].XYZ() - theVertices[0].XYZ();
190     // LeftLower
191     theEdges[2] = theVertices[2].XYZ() - theVertices[3].XYZ();
192     // RightLower
193     theEdges[3] = theVertices[6].XYZ() - theVertices[7].XYZ();
194     // LeftUpper
195     theEdges[4] = theVertices[0].XYZ() - theVertices[1].XYZ();
196     // RightUpper
197     theEdges[5] = theVertices[4].XYZ() - theVertices[5].XYZ();
198   }
199
200   // =======================================================================
201   // function : computeNormals
202   // purpose  : Computes normals to frustum faces
203   // =======================================================================
204   void computeNormals (const gp_Vec* theEdges, gp_Vec* theNormals)
205   {
206     // Top
207     theNormals[0] = theEdges[0].Crossed (theEdges[4]);
208     // Bottom
209     theNormals[1] = theEdges[2].Crossed (theEdges[0]);
210     // Left
211     theNormals[2] = theEdges[4].Crossed (theEdges[1]);
212     // Right
213     theNormals[3] = theEdges[1].Crossed (theEdges[5]);
214     // Near
215     theNormals[4] = theEdges[0].Crossed (theEdges[1]);
216     // Far
217     theNormals[5] = -theNormals[4];
218   }
219 }
220
221 // =======================================================================
222 // function : cacheVertexProjections
223 // purpose  : Caches projection of frustum's vertices onto its plane directions
224 //            and {i, j, k}
225 // =======================================================================
226 void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const
227 {
228   if (theFrustum->myIsOrthographic)
229   {
230     // project vertices onto frustum normals
231     // Since orthographic view volume's faces are always a pairwise translation of
232     // one another, only 2 vertices that belong to opposite faces can be projected
233     // to simplify calculations.
234     Standard_Integer aVertIdxs[6] = { LeftTopNear, LeftBottomNear,       // opposite planes in height direction
235                                       LeftBottomNear, RightBottomNear,   // opposite planes in width direcion
236                                       LeftBottomFar, RightBottomNear };  // opposite planes in depth direction
237     for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 5; aPlaneIdx += 2)
238     {
239       Standard_Real aProj1 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx]].XYZ());
240       Standard_Real aProj2 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx + 1]].XYZ());
241       theFrustum->myMinVertsProjections[aPlaneIdx] = Min (aProj1, aProj2);
242       theFrustum->myMaxVertsProjections[aPlaneIdx] = Max (aProj1, aProj2);
243     }
244   }
245   else
246   {
247     // project all vertices onto frustum normals
248     for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
249     {
250       Standard_Real aMax = -DBL_MAX;
251       Standard_Real aMin = DBL_MAX;
252       const gp_XYZ& aPlane = theFrustum->myPlanes[aPlaneIdx].XYZ();
253       for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
254       {
255         Standard_Real aProjection = aPlane.Dot (theFrustum->myVertices[aVertIdx].XYZ());
256         aMin = Min (aMin, aProjection);
257         aMax = Max (aMax, aProjection);
258       }
259       theFrustum->myMinVertsProjections[aPlaneIdx] = aMin;
260       theFrustum->myMaxVertsProjections[aPlaneIdx] = aMax;
261     }
262   }
263
264   // project vertices onto {i, j, k}
265   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
266   {
267     Standard_Real aMax = -DBL_MAX;
268     Standard_Real aMin = DBL_MAX;
269     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
270     {
271       const gp_XYZ& aVert = theFrustum->myVertices[aVertIdx].XYZ();
272       aMax = Max (aVert.GetData()[aDim], aMax);
273       aMin = Min (aVert.GetData()[aDim], aMin);
274     }
275     theFrustum->myMaxOrthoVertsProjections[aDim] = aMax;
276     theFrustum->myMinOrthoVertsProjections[aDim] = aMin;
277   }
278 }
279
280 // =======================================================================
281 // function : Init
282 // purpose  :
283 // =======================================================================
284 void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d &thePoint)
285 {
286   mySelectionType = SelectMgr_SelectionType_Point;
287   mySelRectangle.SetMousePos (thePoint);
288 }
289
290 // =======================================================================
291 // function : Init
292 // purpose  :
293 // =======================================================================
294 void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d& theMinPnt,
295                                          const gp_Pnt2d& theMaxPnt)
296 {
297   mySelectionType = SelectMgr_SelectionType_Box;
298   mySelRectangle.SetMinPnt (theMinPnt);
299   mySelRectangle.SetMaxPnt (theMaxPnt);
300 }
301
302 // =======================================================================
303 // function : Build
304 // purpose  :
305 // =======================================================================
306 void SelectMgr_RectangularFrustum::Build()
307 {
308   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
309     "Error! SelectMgr_RectangularFrustum::Build() should be called after selection frustum initialization");
310   gp_Pnt2d aMinPnt, aMaxPnt;
311   if (mySelectionType == SelectMgr_SelectionType_Point)
312   {
313     const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
314     myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 0.0);
315     myFarPickedPnt  = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 1.0);
316
317     aMinPnt.SetCoord (aMousePos.X() - myPixelTolerance * 0.5,
318                       aMousePos.Y() - myPixelTolerance * 0.5);
319     aMaxPnt.SetCoord (aMousePos.X() + myPixelTolerance * 0.5,
320                       aMousePos.Y() + myPixelTolerance * 0.5);
321   }
322   else
323   {
324     aMinPnt = mySelRectangle.MinPnt();
325     aMaxPnt = mySelRectangle.MaxPnt();
326     myNearPickedPnt = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
327                                                         (aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
328                                                         0.0);
329     myFarPickedPnt  = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
330                                                         (aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
331                                                         1.0);
332   }
333
334   myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ();
335
336   // calculate base frustum characteristics: vertices and edge directions
337   computeFrustum (aMinPnt, aMaxPnt, myBuilder, myVertices, myEdgeDirs);
338
339   // compute frustum normals
340   computeNormals (myEdgeDirs, myPlanes);
341
342   // compute vertices projections onto frustum normals and
343   // {i, j, k} vectors and store them to corresponding class fields
344   cacheVertexProjections (this);
345
346   myScale = 1.0;
347 }
348
349 // =======================================================================
350 // function : ScaleAndTransform
351 // purpose  : IMPORTANT: Scaling makes sense only for frustum built on a single point!
352 //            Note that this method does not perform any checks on type of the frustum.
353 //            Returns a copy of the frustum resized according to the scale factor given
354 //            and transforms it using the matrix given.
355 //            There are no default parameters, but in case if:
356 //                - transformation only is needed: @theScaleFactor must be initialized
357 //                  as any negative value;
358 //                - scale only is needed: @theTrsf must be set to gp_Identity.
359 // =======================================================================
360 Handle(SelectMgr_BaseIntersector) SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
361                                                                                    const gp_GTrsf& theTrsf,
362                                                                                    const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
363 {
364   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
365     "Error! SelectMgr_RectangularFrustum::ScaleAndTransform() should be called after selection frustum initialization");
366
367   Standard_ASSERT_RAISE (theScaleFactor > 0,
368     "Error! Pixel tolerance for selection should be greater than zero");
369
370   Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
371   const Standard_Boolean isToScale = theScaleFactor != 1;
372   const Standard_Boolean isToTrsf  = theTrsf.Form() != gp_Identity;
373
374   if (!isToScale && !isToTrsf)
375   {
376     aRes->SetBuilder (theBuilder);
377     return aRes;
378   }
379
380   aRes->myIsOrthographic = myIsOrthographic;
381   const SelectMgr_RectangularFrustum* aRef = this;
382
383   if (isToScale)
384   {
385     aRes->myNearPickedPnt = myNearPickedPnt;
386     aRes->myFarPickedPnt  = myFarPickedPnt;
387     aRes->myViewRayDir    = myViewRayDir;
388
389     const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
390     const gp_Pnt2d aMinPnt (aMousePos.X() - theScaleFactor * 0.5,
391                             aMousePos.Y() - theScaleFactor * 0.5);
392     const gp_Pnt2d aMaxPnt (aMousePos.X() + theScaleFactor * 0.5,
393                             aMousePos.Y() + theScaleFactor * 0.5);
394
395     // recompute base frustum characteristics from scratch
396     computeFrustum (aMinPnt, aMaxPnt, myBuilder, aRes->myVertices, aRes->myEdgeDirs);
397
398     aRef = aRes.get();
399   }
400
401   if (isToTrsf)
402   {
403     const Standard_Real aRefScale = aRef->myFarPickedPnt.SquareDistance (aRef->myNearPickedPnt);
404
405     gp_Pnt aPoint = aRef->myNearPickedPnt;
406     theTrsf.Transforms (aPoint.ChangeCoord());
407     aRes->myNearPickedPnt = aPoint;
408
409     aPoint.SetXYZ (aRef->myFarPickedPnt.XYZ());
410     theTrsf.Transforms (aPoint.ChangeCoord());
411     aRes->myFarPickedPnt = aPoint;
412
413     aRes->myViewRayDir = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
414
415     for (Standard_Integer anIt = 0; anIt < 8; anIt++)
416     {
417       aPoint = aRef->myVertices[anIt];
418       theTrsf.Transforms (aPoint.ChangeCoord());
419       aRes->myVertices[anIt] = aPoint;
420     }
421
422     // Horizontal
423     aRes->myEdgeDirs[0] = aRes->myVertices[4].XYZ() - aRes->myVertices[0].XYZ();
424     // Vertical
425     aRes->myEdgeDirs[1] = aRes->myVertices[2].XYZ() - aRes->myVertices[0].XYZ();
426     // LeftLower
427     aRes->myEdgeDirs[2] = aRes->myVertices[2].XYZ() - aRes->myVertices[3].XYZ();
428     // RightLower
429     aRes->myEdgeDirs[3] = aRes->myVertices[6].XYZ() - aRes->myVertices[7].XYZ();
430     // LeftUpper
431     aRes->myEdgeDirs[4] = aRes->myVertices[0].XYZ() - aRes->myVertices[1].XYZ();
432     // RightUpper
433     aRes->myEdgeDirs[5] = aRes->myVertices[4].XYZ() - aRes->myVertices[5].XYZ();
434
435     // Compute scale to transform depth from local coordinate system to world coordinate system
436     aRes->myScale = Sqrt (aRefScale / aRes->myFarPickedPnt.SquareDistance (aRes->myNearPickedPnt));
437   }
438
439   // compute frustum normals
440   computeNormals (aRes->myEdgeDirs, aRes->myPlanes);
441
442   cacheVertexProjections (aRes.get());
443
444   aRes->mySelectionType = mySelectionType;
445   aRes->mySelRectangle = mySelRectangle;
446   aRes->SetBuilder (theBuilder);
447   return aRes;
448 }
449
450 // =======================================================================
451 // function : IsScalable
452 // purpose  :
453 // =======================================================================
454 Standard_Boolean SelectMgr_RectangularFrustum::IsScalable() const
455 {
456   return mySelectionType == SelectMgr_SelectionType_Point;
457 }
458
459 // =======================================================================
460 // function : OverlapsBox
461 // purpose  : Returns true if selecting volume is overlapped by
462 //            axis-aligned bounding box with minimum corner at point
463 //            theMinPnt and maximum at point theMaxPnt
464 // =======================================================================
465 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
466                                                             const SelectMgr_Vec3& theBoxMax,
467                                                             Standard_Boolean*     theInside) const
468 {
469   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
470     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
471
472   return hasBoxOverlap (theBoxMin, theBoxMax, theInside);
473 }
474
475 // =======================================================================
476 // function : OverlapsBox
477 // purpose  : SAT intersection test between defined volume and
478 //            given axis-aligned box
479 // =======================================================================
480 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
481                                                             const SelectMgr_Vec3& theBoxMax,
482                                                             const SelectMgr_ViewClipRange& theClipRange,
483                                                             SelectBasics_PickResult& thePickResult) const
484 {
485   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
486     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
487
488   if (!hasBoxOverlap (theBoxMin, theBoxMax))
489     return Standard_False;
490
491   Standard_Real aDepth = 0.0;
492   BVH_Ray<Standard_Real, 3> aRay(SelectMgr_Vec3(myNearPickedPnt.X(), myNearPickedPnt.Y(), myNearPickedPnt.Z()),
493                                  SelectMgr_Vec3(myViewRayDir.X(), myViewRayDir.Y(), myViewRayDir.Z()));
494   Standard_Real aTimeEnter, aTimeLeave;
495   if (!BVH_Tools<Standard_Real, 3>::RayBoxIntersection (aRay, theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
496   {
497     gp_Pnt aNearestPnt (RealLast(), RealLast(), RealLast());
498     aNearestPnt.SetX (Max (Min (myNearPickedPnt.X(), theBoxMax.x()), theBoxMin.x()));
499     aNearestPnt.SetY (Max (Min (myNearPickedPnt.Y(), theBoxMax.y()), theBoxMin.y()));
500     aNearestPnt.SetZ (Max (Min (myNearPickedPnt.Z(), theBoxMax.z()), theBoxMin.z()));
501
502     aDepth = aNearestPnt.Distance (myNearPickedPnt);
503     thePickResult.SetDepth (aDepth);
504     return !theClipRange.IsClipped (thePickResult.Depth());
505   }
506
507   Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
508   aRange.GetMin (aDepth);
509
510   if (!theClipRange.GetNearestDepth (aRange, aDepth))
511   {
512     return Standard_False;
513   }
514
515   thePickResult.SetDepth (aDepth);
516
517   return Standard_True;
518 }
519
520 // =======================================================================
521 // function : OverlapsPoint
522 // purpose  : Intersection test between defined volume and given point
523 // =======================================================================
524 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt,
525                                                               const SelectMgr_ViewClipRange& theClipRange,
526                                                               SelectBasics_PickResult& thePickResult) const
527 {
528   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
529     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
530
531   if (!hasPointOverlap (thePnt))
532     return Standard_False;
533
534   gp_XYZ aV = thePnt.XYZ() - myNearPickedPnt.XYZ();
535   const Standard_Real aDepth = aV.Dot (myViewRayDir.XYZ());
536
537   thePickResult.SetDepth (Abs (aDepth) * myScale);
538   thePickResult.SetPickedPoint (thePnt);
539
540   return !theClipRange.IsClipped (thePickResult.Depth());
541 }
542
543 // =======================================================================
544 // function : OverlapsPoint
545 // purpose  : Intersection test between defined volume and given point
546 // =======================================================================
547 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt) const
548 {
549   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
550     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
551
552   return hasPointOverlap (thePnt);
553 }
554
555 // =======================================================================
556 // function : OverlapsSegment
557 // purpose  : Checks if line segment overlaps selecting frustum
558 // =======================================================================
559 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSegment (const gp_Pnt& thePnt1,
560                                                                 const gp_Pnt& thePnt2,
561                                                                 const SelectMgr_ViewClipRange& theClipRange,
562                                                                 SelectBasics_PickResult& thePickResult) const
563 {
564   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
565     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
566
567   if (!hasSegmentOverlap (thePnt1, thePnt2))
568     return Standard_False;
569
570   segmentSegmentDistance (thePnt1, thePnt2, thePickResult);
571
572   return !theClipRange.IsClipped (thePickResult.Depth());
573 }
574
575 // =======================================================================
576 // function : OverlapsPolygon
577 // purpose  : SAT intersection test between defined volume and given
578 //            ordered set of points, representing line segments. The test
579 //            may be considered of interior part or boundary line defined
580 //            by segments depending on given sensitivity type
581 // =======================================================================
582 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPolygon (const TColgp_Array1OfPnt& theArrayOfPnts,
583                                                                 Select3D_TypeOfSensitivity theSensType,
584                                                                 const SelectMgr_ViewClipRange& theClipRange,
585                                                                 SelectBasics_PickResult& thePickResult) const
586 {
587   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
588     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
589
590   if (theSensType == Select3D_TOS_BOUNDARY)
591   {
592     Standard_Integer aMatchingSegmentsNb = -1;
593     SelectBasics_PickResult aPickResult;
594     thePickResult.Invalidate();
595     const Standard_Integer aLower  = theArrayOfPnts.Lower();
596     const Standard_Integer anUpper = theArrayOfPnts.Upper();
597     for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
598     {
599       const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
600       const gp_Pnt& aEndPnt   = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
601       if (hasSegmentOverlap (aStartPnt, aEndPnt))
602       {
603         aMatchingSegmentsNb++;
604         segmentSegmentDistance (aStartPnt, aEndPnt, aPickResult);
605         thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
606       }
607     }
608
609     if (aMatchingSegmentsNb == -1)
610       return Standard_False;
611   }
612   else if (theSensType == Select3D_TOS_INTERIOR)
613   {
614     gp_Vec aPolyNorm (gp_XYZ (RealLast(), RealLast(), RealLast()));
615     if (!hasPolygonOverlap (theArrayOfPnts, aPolyNorm))
616     {
617       return Standard_False;
618     }
619
620     if (aPolyNorm.Magnitude() <= Precision::Confusion())
621     {
622       // treat degenerated polygon as point
623       return OverlapsPoint (theArrayOfPnts.First(), theClipRange, thePickResult);
624     }
625     else if (!segmentPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
626     {
627       return Standard_False;
628     }
629   }
630
631   return !theClipRange.IsClipped (thePickResult.Depth());
632 }
633
634 // =======================================================================
635 // function : OverlapsTriangle
636 // purpose  : SAT intersection test between defined volume and given
637 //            triangle. The test may be considered of interior part or
638 //            boundary line defined by triangle vertices depending on
639 //            given sensitivity type
640 // =======================================================================
641 Standard_Boolean SelectMgr_RectangularFrustum::OverlapsTriangle (const gp_Pnt& thePnt1,
642                                                                  const gp_Pnt& thePnt2,
643                                                                  const gp_Pnt& thePnt3,
644                                                                  Select3D_TypeOfSensitivity theSensType,
645                                                                  const SelectMgr_ViewClipRange& theClipRange,
646                                                                  SelectBasics_PickResult& thePickResult) const
647 {
648   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
649     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
650
651   if (theSensType == Select3D_TOS_BOUNDARY)
652   {
653     const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
654     const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
655     return OverlapsPolygon (aPntsArray, Select3D_TOS_BOUNDARY, theClipRange, thePickResult);
656   }
657   else if (theSensType == Select3D_TOS_INTERIOR)
658   {
659     gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
660     if (!hasTriangleOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
661     {
662       return Standard_False;
663     }
664
665     const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
666                                  thePnt3.XYZ() - thePnt2.XYZ(),
667                                  thePnt1.XYZ() - thePnt3.XYZ() };
668           if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
669     {
670       // consider degenerated triangle as point or segment
671       return aTrEdges[0].SquareModulus() > gp::Resolution()
672            ? OverlapsSegment (thePnt1, thePnt2, theClipRange, thePickResult)
673            : (aTrEdges[1].SquareModulus() > gp::Resolution()
674             ? OverlapsSegment (thePnt2, thePnt3, theClipRange, thePickResult)
675             : OverlapsPoint (thePnt1, theClipRange, thePickResult));
676     }
677
678     const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
679     const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myViewRayDir.XYZ());
680     if (Abs (anAlpha) < gp::Resolution())
681     {
682       // handle the case when triangle normal and selecting frustum direction are orthogonal
683       SelectBasics_PickResult aPickResult;
684       thePickResult.Invalidate();
685       for (Standard_Integer anEdgeIter = 0; anEdgeIter < 3; ++anEdgeIter)
686       {
687         const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
688         const gp_Pnt& anEndPnt  = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
689         segmentSegmentDistance (aStartPnt, anEndPnt, aPickResult);
690         thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
691       }
692       thePickResult.SetSurfaceNormal (aTriangleNormal);
693       return !theClipRange.IsClipped (thePickResult.Depth());
694     }
695
696     // check if intersection point belongs to triangle's interior part
697     const gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
698
699     const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
700     const gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
701     const Standard_Real anU = aVec.Dot (aTrEdges[2]);
702     const Standard_Real aV  = aVec.Dot (aTrEdges[0]);
703
704     const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
705     const gp_Pnt aPtOnPlane = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
706     if (isInterior)
707     {
708       thePickResult.SetDepth (myNearPickedPnt.Distance (aPtOnPlane) * myScale);
709       thePickResult.SetPickedPoint (aPtOnPlane);
710       thePickResult.SetSurfaceNormal (aTriangleNormal);
711       return !theClipRange.IsClipped (thePickResult.Depth());
712     }
713
714     Standard_Real aMinDist = RealLast();
715     Standard_Integer aNearestEdgeIdx1 = -1;
716     for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
717     {
718       gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
719       Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
720       Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
721       if (aDist < aMinDist)
722       {
723         aMinDist = aDist;
724         aNearestEdgeIdx1 = anEdgeIdx;
725       }
726     }
727     Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
728     const gp_Vec aVec12 (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]);
729     if (aVec12.SquareMagnitude() > gp::Resolution()
730      && myViewRayDir.IsParallel (aVec12, Precision::Angular()))
731     {
732       aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
733     }
734     segmentSegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult);
735     thePickResult.SetSurfaceNormal (aTriangleNormal);
736   }
737
738   return !theClipRange.IsClipped (thePickResult.Depth());
739 }
740
741 // =======================================================================
742 // function : GetMousePosition
743 // purpose  :
744 // =======================================================================
745 const gp_Pnt2d& SelectMgr_RectangularFrustum::GetMousePosition() const
746 {
747   if (mySelectionType == SelectMgr_SelectionType_Point)
748   {
749     return mySelRectangle.MousePos();
750   }
751   return base_type::GetMousePosition();
752 }
753
754 // =======================================================================
755 // function : DistToGeometryCenter
756 // purpose  : Measures distance between 3d projection of user-picked
757 //            screen point and given point theCOG
758 // =======================================================================
759 Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG) const
760 {
761   Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
762     "Error! SelectMgr_RectangularFrustum::DistToGeometryCenter() should be called after selection frustum initialization");
763
764   return theCOG.Distance (myNearPickedPnt) * myScale;
765 }
766
767 // =======================================================================
768 // function : DetectedPoint
769 // purpose  : Calculates the point on a view ray that was detected during
770 //            the run of selection algo by given depth
771 // =======================================================================
772 gp_Pnt SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
773 {
774   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point,
775     "SelectMgr_RectangularFrustum::DetectedPoint() should be called only for Point selection type");
776   return myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * theDepth / myScale;
777 }
778
779 // =======================================================================
780 // function : GetPlanes
781 // purpose  :
782 // =======================================================================
783 void SelectMgr_RectangularFrustum::GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const
784 {
785   thePlaneEquations.Clear();
786
787   SelectMgr_Vec4 anEquation;
788   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
789   {
790     const gp_Vec& aPlaneNorm = myIsOrthographic && aPlaneIdx % 2 == 1 ?
791       myPlanes[aPlaneIdx - 1].Reversed() : myPlanes[aPlaneIdx];
792     anEquation.x() = aPlaneNorm.X();
793     anEquation.y() = aPlaneNorm.Y();
794     anEquation.z() = aPlaneNorm.Z();
795     anEquation.w() = - (aPlaneNorm.XYZ().Dot (myVertices[aPlaneIdx % 2 == 0 ? aPlaneIdx : aPlaneIdx + 2].XYZ()));
796     thePlaneEquations.Append (anEquation);
797   }
798 }
799
800 //=======================================================================
801 //function : DumpJson
802 //purpose  : 
803 //=======================================================================
804 void SelectMgr_RectangularFrustum::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
805 {
806   OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_RectangularFrustum)
807   OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_Frustum)
808
809   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myNearPickedPnt)
810   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myFarPickedPnt)
811   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myViewRayDir)
812   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MinPnt())
813   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MaxPnt())
814
815   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myScale)
816 }