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