bf658c96899dfd07fde3e5b748e6c5c0ae2ad793
[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 <NCollection_Vector.hxx>
17 #include <Poly_Array1OfTriangle.hxx>
18
19 #include <SelectMgr_RectangularFrustum.hxx>
20
21 #define DOT(A, B) (A.x() * B.x() + A.y() * B.y() + A.z() * B.z())
22 #define DOTp(A, B) (A.x() * B.X() + A.y() * B.Y() + A.z() * B.Z())
23 #define DISTANCE(A, B) (std::sqrt ((A.x() - B.x()) * (A.x() - B.x()) + (A.y() - B.y()) * (A.y() - B.y()) + (A.z() - B.z()) * (A.z() - B.z())))
24 #define DISTANCEp(A, B) (std::sqrt ((A.x() - B.X()) * (A.x() - B.X()) + (A.y() - B.Y()) * (A.y() - B.Y()) + (A.z() - B.Z()) * (A.z() - B.Z())))
25
26 // =======================================================================
27 // function : segmentSegmentDistance
28 // purpose  :
29 // =======================================================================
30 void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
31                                                            const gp_Pnt& theSegPnt2,
32                                                            Standard_Real& theDepth)
33 {
34   SelectMgr_Vec3 anU = SelectMgr_Vec3 (theSegPnt2.X() - theSegPnt1.X(),
35                                        theSegPnt2.Y() - theSegPnt1.Y(),
36                                        theSegPnt2.Z() - theSegPnt1.Z());
37   SelectMgr_Vec3 aV = myViewRayDir;
38   SelectMgr_Vec3 aW = SelectMgr_Vec3 (theSegPnt1.X() - myNearPickedPnt.x(),
39                                       theSegPnt1.Y() - myNearPickedPnt.y(),
40                                       theSegPnt1.Z() - myNearPickedPnt.z());
41   Standard_Real anA = DOT (anU, anU);
42   Standard_Real aB = DOT (anU, aV);
43   Standard_Real aC = DOT (aV, aV);
44   Standard_Real aD = DOT (anU, aW);
45   Standard_Real anE = DOT (aV, aW);
46   Standard_Real aCoef = anA * aC - aB * aB;
47   Standard_Real aSc, aSn, aSd = aCoef;
48   Standard_Real aTc, aTn, aTd = aCoef;
49
50   if (aCoef < Precision::Confusion())
51   {
52     aSn = 0.0;
53     aSd = 1.0;
54     aTn = anE;
55     aTd = aC;
56   }
57   else
58   {
59     aSn = (aB * anE - aC * aD);
60     aTn = (anA * anE - aB * aD);
61     if (aSn < 0.0)
62     {
63       aSn = 0.0;
64       aTn = anE;
65       aTd = aC;
66     }
67     else if (aSn > aSd)
68     {
69       aSn = aSd;
70       aTn = anE + aB;
71       aTd = aC;
72     }
73   }
74
75   if (aTn < 0.0)
76   {
77     aTn = 0.0;
78     if (-aD < 0.0)
79       aSn = 0.0;
80     else if (-aD > anA)
81       aSn = aSd;
82     else {
83       aSn = -aD;
84       aSd = anA;
85     }
86   }
87   else if (aTn > aTd)
88   {
89     aTn = aTd;
90     if ((-aD + aB) < 0.0)
91       aSn = 0;
92     else if ((-aD + aB) > anA)
93       aSn = aSd;
94     else {
95       aSn = (-aD +  aB);
96       aSd = anA;
97     }
98   }
99   aSc = (Abs (aSn) < Precision::Confusion() ? 0.0 : aSn / aSd);
100   aTc = (Abs (aTn) < Precision::Confusion() ? 0.0 : aTn / aTd);
101
102   SelectMgr_Vec3 aClosestPnt = myNearPickedPnt + myViewRayDir * aTc;
103   theDepth = DISTANCE (myNearPickedPnt, aClosestPnt);
104 }
105
106 // =======================================================================
107 // function : segmentPlaneIntersection
108 // purpose  :
109 // =======================================================================
110 void SelectMgr_RectangularFrustum::segmentPlaneIntersection (const SelectMgr_Vec3& thePlane,
111                                                              const gp_Pnt& thePntOnPlane,
112                                                              Standard_Real& theDepth)
113 {
114   SelectMgr_Vec3 anU = myViewRayDir;
115   SelectMgr_Vec3 aW = SelectMgr_Vec3 (myNearPickedPnt.x() - thePntOnPlane.X(),
116                                       myNearPickedPnt.y() - thePntOnPlane.Y(),
117                                       myNearPickedPnt.z() - thePntOnPlane.Z());
118   Standard_Real aD = DOT (thePlane, anU);
119   Standard_Real aN = -DOT (thePlane, aW);
120
121   if (Abs (aD) < Precision::Confusion())
122   {
123     if (Abs (aN) < Precision::Angular())
124     {
125       theDepth = DBL_MAX;
126       return;
127     }
128     else
129     {
130       theDepth = DBL_MAX;
131       return;
132     }
133   }
134
135   Standard_Real aParam = aN / aD;
136   if (aParam < 0.0 || aParam > 1.0)
137   {
138     theDepth = DBL_MAX;
139     return;
140   }
141
142   SelectMgr_Vec3 aClosestPnt = myNearPickedPnt + anU * aParam;
143   theDepth = DISTANCE (myNearPickedPnt, aClosestPnt);
144 }
145
146 namespace
147 {
148   // =======================================================================
149   // function : computeNormals
150   // purpose  : Computes normals to frustum faces
151   // =======================================================================
152   void computeNormals (const SelectMgr_Vec3* theVertices, SelectMgr_Vec3* theNormals)
153   {
154     // Top
155     theNormals[0] = SelectMgr_Vec3::Cross (theVertices[1] - theVertices[0],
156                                            theVertices[4] - theVertices[0]);
157     // Bottom
158     theNormals[1] = SelectMgr_Vec3::Cross (theVertices[3] - theVertices[2],
159                                            theVertices[6] - theVertices[2]);
160     // Left
161     theNormals[2] = SelectMgr_Vec3::Cross (theVertices[1] - theVertices[0],
162                                            theVertices[2] - theVertices[0]);
163     // Right
164     theNormals[3] = SelectMgr_Vec3::Cross (theVertices[5] - theVertices[4],
165                                            theVertices[6] - theVertices[4]);
166     // Near
167     theNormals[4] = SelectMgr_Vec3::Cross (theVertices[6] - theVertices[4],
168                                            theVertices[0] - theVertices[4]);
169     // Far
170     theNormals[5] = SelectMgr_Vec3::Cross (theVertices[7] - theVertices[5],
171                                            theVertices[1] - theVertices[5]);
172   }
173 }
174
175 // =======================================================================
176 // function : Build
177 // purpose  : Build volume according to the point and given pixel
178 //            tolerance
179 // =======================================================================
180 void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d &thePoint)
181 {
182   myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 0.0);
183   myFarPickedPnt  = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 1.0);
184   myViewRayDir = myFarPickedPnt - myNearPickedPnt;
185   myMousePos = thePoint;
186
187   // LeftTopNear
188   myVertices[0] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
189                                                     thePoint.Y() + myPixelTolerance / 2.0,
190                                                     0.0);
191   // LeftTopFar
192   myVertices[1] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
193                                                     thePoint.Y() + myPixelTolerance / 2.0,
194                                                     1.0);
195   // LeftBottomNear
196   myVertices[2] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
197                                                     thePoint.Y() - myPixelTolerance / 2.0,
198                                                     0.0);
199   // LeftBottomFar
200   myVertices[3] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
201                                                     thePoint.Y() - myPixelTolerance / 2.0,
202                                                     1.0);
203   // RightTopNear
204   myVertices[4] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
205                                                     thePoint.Y() + myPixelTolerance / 2.0,
206                                                     0.0);
207   // RightTopFar
208   myVertices[5] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
209                                                     thePoint.Y() + myPixelTolerance / 2.0,
210                                                     1.0);
211   // RightBottomNear
212   myVertices[6] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
213                                                     thePoint.Y() - myPixelTolerance / 2.0,
214                                                     0.0);
215   // RightBottomFar
216   myVertices[7] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
217                                                     thePoint.Y() - myPixelTolerance / 2.0,
218                                                     1.0);
219
220   // compute frustum normals
221   computeNormals (myVertices, myPlanes);
222
223   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
224   {
225     Standard_Real aMax = -DBL_MAX;
226     Standard_Real aMin =  DBL_MAX;
227     const SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
228     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
229     {
230       Standard_Real aProjection = DOT (aPlane, myVertices[aVertIdx]);
231       aMax = Max (aMax, aProjection);
232       aMin = Min (aMin, aProjection);
233     }
234     myMaxVertsProjections[aPlaneIdx] = aMax;
235     myMinVertsProjections[aPlaneIdx] = aMin;
236   }
237
238   SelectMgr_Vec3 aDimensions[3] =
239   {
240     SelectMgr_Vec3 (1.0, 0.0, 0.0),
241     SelectMgr_Vec3 (0.0, 1.0, 0.0),
242     SelectMgr_Vec3 (0.0, 0.0, 1.0)
243   };
244
245   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
246   {
247     Standard_Real aMax = -DBL_MAX;
248     Standard_Real aMin =  DBL_MAX;
249     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
250     {
251       Standard_Real aProjection = DOT (aDimensions[aDim], myVertices[aVertIdx]);
252       aMax = Max (aProjection, aMax);
253       aMin = Min (aProjection, aMin);
254     }
255     myMaxOrthoVertsProjections[aDim] = aMax;
256     myMinOrthoVertsProjections[aDim] = aMin;
257   }
258
259   // Horizontal
260   myEdgeDirs[0] = myVertices[4] - myVertices[0];
261   // Vertical
262   myEdgeDirs[1] = myVertices[2] - myVertices[0];
263   // LeftLower
264   myEdgeDirs[2] = myVertices[2] - myVertices[3];
265   // RightLower
266   myEdgeDirs[3] = myVertices[6] - myVertices[7];
267   // LeftUpper
268   myEdgeDirs[4] = myVertices[0] - myVertices[1];
269   // RightUpper
270   myEdgeDirs[5] = myVertices[4] - myVertices[5];
271 }
272
273 // =======================================================================
274 // function : Build
275 // purpose  : Build volume according to the selected rectangle
276 // =======================================================================
277 void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d& theMinPnt,
278                                           const gp_Pnt2d& theMaxPnt)
279 {
280   myNearPickedPnt = myBuilder->ProjectPntOnViewPlane ((theMinPnt.X() + theMaxPnt.X()) * 0.5,
281                                                       (theMinPnt.Y() + theMaxPnt.Y()) * 0.5,
282                                                       0.0);
283   myFarPickedPnt = myBuilder->ProjectPntOnViewPlane ((theMinPnt.X() + theMaxPnt.X()) * 0.5,
284                                                      (theMinPnt.Y() + theMaxPnt.Y()) * 0.5,
285                                                      1.0);
286   myViewRayDir = myFarPickedPnt - myNearPickedPnt;
287
288   // LeftTopNear
289   myVertices[0] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
290                                                     theMaxPnt.Y(),
291                                                     0.0);
292   // LeftTopFar
293   myVertices[1] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
294                                                     theMaxPnt.Y(),
295                                                     1.0);
296   // LeftBottomNear
297   myVertices[2] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
298                                                     theMinPnt.Y(),
299                                                     0.0);
300   // LeftBottomFar
301   myVertices[3] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
302                                                     theMinPnt.Y(),
303                                                     1.0);
304   // RightTopNear
305   myVertices[4] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
306                                                     theMaxPnt.Y(),
307                                                     0.0);
308   // RightTopFar
309   myVertices[5] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
310                                                     theMaxPnt.Y(),
311                                                     1.0);
312   // RightBottomNear
313   myVertices[6] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
314                                                     theMinPnt.Y(),
315                                                     0.0);
316   // RightBottomFar
317   myVertices[7] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
318                                                     theMinPnt.Y(),
319                                                     1.0);
320
321   // compute frustum normals
322   computeNormals (myVertices, myPlanes);
323
324   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
325   {
326     Standard_Real aMax = -DBL_MAX;
327     Standard_Real aMin =  DBL_MAX;
328     const SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
329     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
330     {
331       Standard_Real aProjection = DOT (aPlane, myVertices[aVertIdx]);
332       aMax = Max (aMax, aProjection);
333       aMin = Min (aMin, aProjection);
334     }
335     myMaxVertsProjections[aPlaneIdx] = aMax;
336     myMinVertsProjections[aPlaneIdx] = aMin;
337   }
338
339   SelectMgr_Vec3 aDimensions[3] =
340   {
341     SelectMgr_Vec3 (1.0, 0.0, 0.0),
342     SelectMgr_Vec3 (0.0, 1.0, 0.0),
343     SelectMgr_Vec3 (0.0, 0.0, 1.0)
344   };
345
346   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
347   {
348     Standard_Real aMax = -DBL_MAX;
349     Standard_Real aMin =  DBL_MAX;
350     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
351     {
352       Standard_Real aProjection = DOT (aDimensions[aDim], myVertices[aVertIdx]);
353       aMax = Max (aMax, aProjection);
354       aMin = Min (aMin, aProjection);
355     }
356     myMaxOrthoVertsProjections[aDim] = aMax;
357     myMinOrthoVertsProjections[aDim] = aMin;
358   }
359
360   // Horizontal
361   myEdgeDirs[0] = myVertices[4] - myVertices[0];
362   // Vertical
363   myEdgeDirs[1] = myVertices[2] - myVertices[0];
364   // LeftLower
365   myEdgeDirs[2] = myVertices[2] - myVertices[3];
366   // RightLower
367   myEdgeDirs[3] = myVertices[6] - myVertices[7];
368   // LeftUpper
369   myEdgeDirs[4] = myVertices[0] - myVertices[1];
370   // RightUpper
371   myEdgeDirs[5] = myVertices[4] - myVertices[5];
372 }
373
374 // =======================================================================
375 // function : Transform
376 // purpose  : Returns a copy of the frustum transformed according to the matrix given
377 // =======================================================================
378 NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::Transform (const gp_Trsf& theTrsf)
379 {
380   SelectMgr_RectangularFrustum* aRes = new SelectMgr_RectangularFrustum();
381
382   aRes->myNearPickedPnt = SelectMgr_MatOp::Transform (theTrsf, myNearPickedPnt);
383   aRes->myFarPickedPnt  = SelectMgr_MatOp::Transform (theTrsf, myFarPickedPnt);
384   aRes->myViewRayDir    = aRes->myFarPickedPnt - aRes->myNearPickedPnt;
385
386   aRes->myIsOrthographic = myIsOrthographic;
387
388   // LeftTopNear
389   aRes->myVertices[0] = SelectMgr_MatOp::Transform (theTrsf, myVertices[0]);
390   // LeftTopFar
391   aRes->myVertices[1] = SelectMgr_MatOp::Transform (theTrsf, myVertices[1]);
392   // LeftBottomNear
393   aRes->myVertices[2] = SelectMgr_MatOp::Transform (theTrsf, myVertices[2]);
394   // LeftBottomFar
395   aRes->myVertices[3] = SelectMgr_MatOp::Transform (theTrsf, myVertices[3]);
396   // RightTopNear
397   aRes->myVertices[4] = SelectMgr_MatOp::Transform (theTrsf, myVertices[4]);
398   // RightTopFar
399   aRes->myVertices[5] = SelectMgr_MatOp::Transform (theTrsf, myVertices[5]);
400   // RightBottomNear
401   aRes->myVertices[6] = SelectMgr_MatOp::Transform (theTrsf, myVertices[6]);
402   // RightBottomFar
403   aRes->myVertices[7] = SelectMgr_MatOp::Transform (theTrsf, myVertices[7]);
404
405   // compute frustum normals
406   computeNormals (aRes->myVertices, aRes->myPlanes);
407
408   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
409   {
410     Standard_Real aMax = -DBL_MAX;
411     Standard_Real aMin =  DBL_MAX;
412     const SelectMgr_Vec3 aPlane = aRes->myPlanes[aPlaneIdx];
413     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
414     {
415       Standard_Real aProjection = DOT (aPlane, aRes->myVertices[aVertIdx]);
416       aMax = Max (aMax, aProjection);
417       aMin = Min (aMin, aProjection);
418     }
419     aRes->myMaxVertsProjections[aPlaneIdx] = aMax;
420     aRes->myMinVertsProjections[aPlaneIdx] = aMin;
421   }
422
423   SelectMgr_Vec3 aDimensions[3] =
424   {
425     SelectMgr_Vec3 (1.0, 0.0, 0.0),
426     SelectMgr_Vec3 (0.0, 1.0, 0.0),
427     SelectMgr_Vec3 (0.0, 0.0, 1.0)
428   };
429
430   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
431   {
432     Standard_Real aMax = -DBL_MAX;
433     Standard_Real aMin =  DBL_MAX;
434     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
435     {
436       Standard_Real aProjection = DOT (aDimensions[aDim], aRes->myVertices[aVertIdx]);
437       aMax = Max (aMax, aProjection);
438       aMin = Min (aMin, aProjection);
439     }
440     aRes->myMaxOrthoVertsProjections[aDim] = aMax;
441     aRes->myMinOrthoVertsProjections[aDim] = aMin;
442   }
443
444   // Horizontal
445   aRes->myEdgeDirs[0] = aRes->myVertices[4] - aRes->myVertices[0];
446   // Vertical
447   aRes->myEdgeDirs[1] = aRes->myVertices[2] - aRes->myVertices[0];
448   // LeftLower
449   aRes->myEdgeDirs[2] = aRes->myVertices[2] - aRes->myVertices[3];
450   // RightLower
451   aRes->myEdgeDirs[3] = aRes->myVertices[6] - aRes->myVertices[7];
452   // LeftUpper
453   aRes->myEdgeDirs[4] = aRes->myVertices[0] - aRes->myVertices[1];
454   // RightUpper
455   aRes->myEdgeDirs[5] = aRes->myVertices[4] - aRes->myVertices[5];
456
457   return NCollection_Handle<SelectMgr_BaseFrustum> (aRes);
458 }
459
460 // =======================================================================
461 // function : Scale
462 // purpose  : IMPORTANT: Makes sense only for frustum built on a single point!
463 //            Returns a copy of the frustum resized according to the scale factor given
464 // =======================================================================
465 NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::Scale (const Standard_Real theScaleFactor)
466 {
467   SelectMgr_RectangularFrustum* aRes = new SelectMgr_RectangularFrustum();
468
469   aRes->myNearPickedPnt = myNearPickedPnt;
470   aRes->myFarPickedPnt  = myFarPickedPnt;
471   aRes->myViewRayDir    = myViewRayDir;
472
473   aRes->myIsOrthographic = myIsOrthographic;
474
475     // LeftTopNear
476   aRes->myVertices[0] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() - theScaleFactor / 2.0,
477                                                           myMousePos.Y() + theScaleFactor / 2.0,
478                                                           0.0);
479   // LeftTopFar
480   aRes->myVertices[1] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() - theScaleFactor / 2.0,
481                                                           myMousePos.Y() + theScaleFactor / 2.0,
482                                                           1.0);
483   // LeftBottomNear
484   aRes->myVertices[2] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() - theScaleFactor / 2.0,
485                                                           myMousePos.Y() - theScaleFactor / 2.0,
486                                                           0.0);
487   // LeftBottomFar
488   aRes->myVertices[3] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() - theScaleFactor / 2.0,
489                                                           myMousePos.Y() - theScaleFactor / 2.0,
490                                                           1.0);
491   // RightTopNear
492   aRes->myVertices[4] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() + theScaleFactor / 2.0,
493                                                           myMousePos.Y() + theScaleFactor / 2.0,
494                                                           0.0);
495   // RightTopFar
496   aRes->myVertices[5] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() + theScaleFactor / 2.0,
497                                                           myMousePos.Y() + theScaleFactor / 2.0,
498                                                           1.0);
499   // RightBottomNear
500   aRes->myVertices[6] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() + theScaleFactor / 2.0,
501                                                           myMousePos.Y() - theScaleFactor / 2.0,
502                                                           0.0);
503   // RightBottomFar
504   aRes->myVertices[7] = myBuilder->ProjectPntOnViewPlane (myMousePos.X() + theScaleFactor / 2.0,
505                                                           myMousePos.Y() - theScaleFactor / 2.0,
506                                                           1.0);
507   // compute frustum normals
508   computeNormals (aRes->myVertices, aRes->myPlanes);
509
510   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
511   {
512     Standard_Real aMax = -DBL_MAX;
513     Standard_Real aMin =  DBL_MAX;
514     const SelectMgr_Vec3 aPlane = aRes->myPlanes[aPlaneIdx];
515     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
516     {
517       Standard_Real aProjection = DOT (aPlane, aRes->myVertices[aVertIdx]);
518       aMax = Max (aMax, aProjection);
519       aMin = Min (aMin, aProjection);
520     }
521     aRes->myMaxVertsProjections[aPlaneIdx] = aMax;
522     aRes->myMinVertsProjections[aPlaneIdx] = aMin;
523   }
524
525   SelectMgr_Vec3 aDimensions[3] =
526   {
527     SelectMgr_Vec3 (1.0, 0.0, 0.0),
528     SelectMgr_Vec3 (0.0, 1.0, 0.0),
529     SelectMgr_Vec3 (0.0, 0.0, 1.0)
530   };
531
532   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
533   {
534     Standard_Real aMax = -DBL_MAX;
535     Standard_Real aMin =  DBL_MAX;
536     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
537     {
538       Standard_Real aProjection = DOT (aDimensions[aDim], aRes->myVertices[aVertIdx]);
539       aMax = Max (aMax, aProjection);
540       aMin = Min (aMin, aProjection);
541     }
542     aRes->myMaxOrthoVertsProjections[aDim] = aMax;
543     aRes->myMinOrthoVertsProjections[aDim] = aMin;
544   }
545
546   // Horizontal
547   aRes->myEdgeDirs[0] = aRes->myVertices[4] - aRes->myVertices[0];
548   // Vertical
549   aRes->myEdgeDirs[1] = aRes->myVertices[2] - aRes->myVertices[0];
550   // LeftLower
551   aRes->myEdgeDirs[2] = aRes->myVertices[2] - aRes->myVertices[3];
552   // RightLower
553   aRes->myEdgeDirs[3] = aRes->myVertices[6] - aRes->myVertices[7];
554   // LeftUpper
555   aRes->myEdgeDirs[4] = aRes->myVertices[0] - aRes->myVertices[1];
556   // RightUpper
557   aRes->myEdgeDirs[5] = aRes->myVertices[4] - aRes->myVertices[5];
558
559   return NCollection_Handle<SelectMgr_BaseFrustum> (aRes);
560 }
561
562 // =======================================================================
563 // function : Overlaps
564 // purpose  : Returns true if selecting volume is overlapped by
565 //            axis-aligned bounding box with minimum corner at point
566 //            theMinPnt and maximum at point theMaxPnt
567 // =======================================================================
568 Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& theBoxMin,
569                                                          const SelectMgr_Vec3& theBoxMax,
570                                                          Standard_Boolean*     theInside)
571 {
572   return hasOverlap (theBoxMin, theBoxMax, theInside);
573 }
574
575 // =======================================================================
576 // function : Overlaps
577 // purpose  : SAT intersection test between defined volume and
578 //            given axis-aligned box
579 // =======================================================================
580 Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const BVH_Box<Standard_Real, 3>& theBox,
581                                                          Standard_Real& theDepth)
582 {
583   const SelectMgr_Vec3& aMinPnt = theBox.CornerMin();
584   const SelectMgr_Vec3& aMaxPnt = theBox.CornerMax();
585   if (!hasOverlap (aMinPnt, aMaxPnt))
586     return Standard_False;
587
588   SelectMgr_Vec3 aNearestPnt = SelectMgr_Vec3 (RealLast(), RealLast(), RealLast());
589   aNearestPnt.x() = Max (Min (myNearPickedPnt.x(), aMaxPnt.x()), aMinPnt.x());
590   aNearestPnt.y() = Max (Min (myNearPickedPnt.y(), aMaxPnt.y()), aMinPnt.y());
591   aNearestPnt.z() = Max (Min (myNearPickedPnt.z(), aMaxPnt.z()), aMinPnt.z());
592
593   theDepth = DISTANCE (aNearestPnt, myNearPickedPnt);
594
595   return Standard_True;
596 }
597
598 // =======================================================================
599 // function : Overlaps
600 // purpose  : Intersection test between defined volume and given point
601 // =======================================================================
602 Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt,
603                                                          Standard_Real& theDepth)
604 {
605   if (!hasOverlap (thePnt))
606     return Standard_False;
607
608   SelectMgr_Vec3 aPnt (thePnt.X(), thePnt.Y(), thePnt.Z());
609   SelectMgr_Vec3 aV = aPnt - myNearPickedPnt;
610   SelectMgr_Vec3 aDetectedPnt = myNearPickedPnt + myViewRayDir * (DOT (aV, myViewRayDir) / DOT (myViewRayDir, myViewRayDir));
611
612   theDepth = DISTANCE (aDetectedPnt, myNearPickedPnt);
613
614   return Standard_True;
615 }
616
617 // =======================================================================
618 // function : Overlaps
619 // purpose  : Checks if line segment overlaps selecting frustum
620 // =======================================================================
621 Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
622                                                          const gp_Pnt& thePnt2,
623                                                          Standard_Real& theDepth)
624 {
625   theDepth = -DBL_MAX;
626   if (!hasOverlap (thePnt1, thePnt2))
627     return Standard_False;
628
629   segmentSegmentDistance (thePnt1, thePnt2, theDepth);
630   return Standard_True;
631 }
632
633 // =======================================================================
634 // function : Overlaps
635 // purpose  : SAT intersection test between defined volume and given
636 //            ordered set of points, representing line segments. The test
637 //            may be considered of interior part or boundary line defined
638 //            by segments depending on given sensitivity type
639 // =======================================================================
640 Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const Handle(TColgp_HArray1OfPnt)& theArrayOfPnts,
641                                                          Select3D_TypeOfSensitivity theSensType,
642                                                          Standard_Real& theDepth)
643 {
644   if (theSensType == Select3D_TOS_BOUNDARY)
645   {
646     Standard_Integer aMatchingSegmentsNb = -1;
647     theDepth = DBL_MAX;
648     Standard_Integer aLower = theArrayOfPnts->Lower();
649     Standard_Integer anUpper = theArrayOfPnts->Upper();
650
651     for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
652     {
653       const gp_Pnt& aStartPnt = theArrayOfPnts->Value (aPntIter);
654       const gp_Pnt& aEndPnt = aPntIter == anUpper ? theArrayOfPnts->Value (aLower)
655         : theArrayOfPnts->Value (aPntIter + 1);
656
657       if (hasOverlap (aStartPnt, aEndPnt))
658       {
659         aMatchingSegmentsNb++;
660         Standard_Real aSegmentDepth = RealLast();
661         segmentSegmentDistance (aStartPnt, aEndPnt, aSegmentDepth);
662         theDepth = Min (theDepth, aSegmentDepth);
663       }
664     }
665
666     if (aMatchingSegmentsNb == -1)
667       return Standard_False;
668   }
669   else if (theSensType == Select3D_TOS_INTERIOR)
670   {
671     SelectMgr_Vec3 aPolyNorm (RealLast());
672     if (!hasOverlap (theArrayOfPnts, aPolyNorm))
673       return Standard_False;
674
675     segmentPlaneIntersection (aPolyNorm,
676                               theArrayOfPnts->Value (theArrayOfPnts->Lower()),
677                               theDepth);
678   }
679
680   return Standard_True;
681 }
682
683 // =======================================================================
684 // function : Overlaps
685 // purpose  : SAT intersection test between defined volume and given
686 //            triangle. The test may be considered of interior part or
687 //            boundary line defined by triangle vertices depending on
688 //            given sensitivity type
689 // =======================================================================
690 Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
691                                                          const gp_Pnt& thePnt2,
692                                                          const gp_Pnt& thePnt3,
693                                                          Select3D_TypeOfSensitivity theSensType,
694                                                          Standard_Real& theDepth)
695 {
696   if (theSensType == Select3D_TOS_BOUNDARY)
697   {
698     Handle(TColgp_HArray1OfPnt) aPntsArray = new TColgp_HArray1OfPnt(1, 4);
699     aPntsArray->SetValue (1, thePnt1);
700     aPntsArray->SetValue (2, thePnt2);
701     aPntsArray->SetValue (3, thePnt3);
702     aPntsArray->SetValue (4, thePnt1);
703     return Overlaps (aPntsArray, Select3D_TOS_BOUNDARY, theDepth);
704   }
705   else if (theSensType == Select3D_TOS_INTERIOR)
706   {
707     SelectMgr_Vec3 aTriangleNormal (RealLast());
708     if (!hasOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
709       return Standard_False;
710
711     // check if intersection point belongs to triangle's interior part
712     SelectMgr_Vec3 aPnt1 (thePnt1.X(), thePnt1.Y(), thePnt1.Z());
713     SelectMgr_Vec3 aTrEdges[3] = { SelectMgr_Vec3 (thePnt2.X() - thePnt1.X(), thePnt2.Y() - thePnt1.Y(), thePnt2.Z() - thePnt1.Z()),
714                                    SelectMgr_Vec3 (thePnt3.X() - thePnt2.X(), thePnt3.Y() - thePnt2.Y(), thePnt3.Z() - thePnt2.Z()),
715                                    SelectMgr_Vec3 (thePnt1.X() - thePnt3.X(), thePnt1.Y() - thePnt3.Y(), thePnt1.Z() - thePnt3.Z()) };
716     SelectMgr_Vec3 anEdge = (aPnt1 - myNearPickedPnt) * (1.0 / DOT (aTriangleNormal, myViewRayDir));
717
718     Standard_Real aTime = DOT (aTriangleNormal, anEdge);
719
720     SelectMgr_Vec3 aVec = SelectMgr_Vec3 (myViewRayDir.y() * anEdge.z() - myViewRayDir.z() * anEdge.y(),
721                                           myViewRayDir.z() * anEdge.x() - myViewRayDir.x() * anEdge.z(),
722                                           myViewRayDir.x() * anEdge.y() - myViewRayDir.y() * anEdge.x());
723
724     Standard_Real anU = DOT (aVec, aTrEdges[2]);
725     Standard_Real aV  = DOT (aVec, aTrEdges[0]);
726
727     Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
728
729     if (isInterior)
730     {
731       SelectMgr_Vec3 aDetectedPnt = myNearPickedPnt + myViewRayDir * aTime;
732       theDepth = DISTANCE (myNearPickedPnt, aDetectedPnt);
733       return Standard_True;
734     }
735
736     gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
737     Standard_Real aMinDist = RealLast();
738     Standard_Integer aNearestEdgeIdx = -1;
739     SelectMgr_Vec3 aPtOnPlane = myNearPickedPnt + myViewRayDir * aTime;
740     for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
741     {
742       SelectMgr_Vec3 aW = SelectMgr_Vec3 (aPtOnPlane.x() - aPnts[anEdgeIdx].X(),
743                                           aPtOnPlane.y() - aPnts[anEdgeIdx].Y(),
744                                           aPtOnPlane.z() - aPnts[anEdgeIdx].Z());
745       Standard_Real aCoef = DOT (aTrEdges[anEdgeIdx], aW) / DOT (aTrEdges[anEdgeIdx], aTrEdges[anEdgeIdx]);
746       Standard_Real aDist = DISTANCE (aPtOnPlane, SelectMgr_Vec3 (aPnts[anEdgeIdx].X() + aCoef * aTrEdges[anEdgeIdx].x(),
747                                                                   aPnts[anEdgeIdx].Y() + aCoef * aTrEdges[anEdgeIdx].y(),
748                                                                   aPnts[anEdgeIdx].Z() + aCoef * aTrEdges[anEdgeIdx].z()));
749       if (aMinDist > aDist)
750       {
751         aMinDist = aDist;
752         aNearestEdgeIdx = anEdgeIdx;
753       }
754     }
755     segmentSegmentDistance (aPnts[aNearestEdgeIdx], aPnts[(aNearestEdgeIdx + 1) % 3], theDepth);
756   }
757
758   return Standard_True;
759 }
760
761 // =======================================================================
762 // function : DistToGeometryCenter
763 // purpose  : Measures distance between 3d projection of user-picked
764 //            screen point and given point theCOG
765 // =======================================================================
766 Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG)
767 {
768   const SelectMgr_Vec3& aCOG = SelectMgr_Vec3 (theCOG.X(), theCOG.Y(), theCOG.Z());
769   return DISTANCE (aCOG, myNearPickedPnt);
770 }
771
772 // =======================================================================
773 // function : DetectedPoint
774 // purpose  : Calculates the point on a view ray that was detected during
775 //            the run of selection algo by given depth
776 // =======================================================================
777 SelectMgr_Vec3 SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
778 {
779   return myNearPickedPnt + myViewRayDir * theDepth;
780 }
781
782 // =======================================================================
783 // function : IsClipped
784 // purpose  : Checks if the point of sensitive in which selection was
785 //            detected belongs to the region defined by clipping planes
786 // =======================================================================
787 Standard_Boolean SelectMgr_RectangularFrustum::IsClipped (const Graphic3d_SequenceOfHClipPlane& thePlanes,
788                                                           const Standard_Real theDepth)
789 {
790   Graphic3d_SequenceOfHClipPlane::Iterator aPlaneIt (thePlanes);
791   Standard_Real aMaxDepth = DBL_MAX;
792   Standard_Real aMinDepth = -DBL_MAX;
793   Standard_Real aPlaneA, aPlaneB, aPlaneC, aPlaneD;
794   for ( ; aPlaneIt.More(); aPlaneIt.Next())
795   {
796     const Handle(Graphic3d_ClipPlane)& aClipPlane = aPlaneIt.Value();
797     if (!aClipPlane->IsOn())
798       continue;
799
800     gp_Pln aGeomPlane = aClipPlane->ToPlane();
801
802     aGeomPlane.Coefficients (aPlaneA, aPlaneB, aPlaneC, aPlaneD);
803
804     const gp_XYZ& aPlaneDirXYZ = aGeomPlane.Axis().Direction().XYZ();
805
806     Standard_Real aDotProduct = DOTp (myViewRayDir, aPlaneDirXYZ);
807     Standard_Real aDistance = - (DOTp (myNearPickedPnt, aPlaneDirXYZ) + aPlaneD);
808
809     // check whether the pick line is parallel to clip plane
810     if (Abs (aDotProduct) < Precision::Angular())
811     {
812       // line lies below the plane and is not clipped, skip
813       continue;
814     }
815
816     // compute distance to point of pick line intersection with the plane
817     Standard_Real aParam = aDistance / aDotProduct;
818
819     // check if ray intersects the plane, in case aIntDist < 0
820     // the plane is "behind" the ray
821     if (aParam < 0.0)
822     {
823       continue;
824     }
825
826     const SelectMgr_Vec3 anIntersectionPt = myNearPickedPnt + myViewRayDir * aParam;
827     const Standard_Real aDistToPln = DISTANCE (anIntersectionPt, myNearPickedPnt);
828
829     // change depth limits for case of opposite and directed planes
830     if (aDotProduct < 0.0)
831     {
832       aMaxDepth = Min (aDistToPln, aMaxDepth);
833     }
834     else if (aDistToPln > aMinDepth)
835     {
836       aMinDepth = Max (aDistToPln, aMinDepth);
837     }
838   }
839
840   return (theDepth <= aMinDepth || theDepth >= aMaxDepth);
841 }