0024623: Visualization - improve selection mechanism
[occt.git] / src / SelectMgr / SelectMgr_Frustum.lxx
1 // Created on: 2015-03-16
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 #define DOT(A, B) (A.x() * B.x() + A.y() * B.y() + A.z() * B.z())
20 #define DOTp(A, B) (A.x() * B.X() + A.y() * B.Y() + A.z() * B.Z())
21
22 // =======================================================================
23 // function : isSeparated
24 // purpose  : Checks if AABB and frustum are separated along the given axis.
25 // =======================================================================
26 template <int N>
27 Standard_Boolean SelectMgr_Frustum<N>::isSeparated (const SelectMgr_Vec3& theBoxMin,
28                                                     const SelectMgr_Vec3& theBoxMax,
29                                                     const SelectMgr_Vec3& theAxis) const
30 {
31   // frustum projection
32   Standard_Real aMinF =  DBL_MAX;
33   Standard_Real aMaxF = -DBL_MAX;
34
35   // box projection
36   Standard_Real aMinB =  DBL_MAX;
37   Standard_Real aMaxB = -DBL_MAX;
38
39   const Standard_Real aBoxProj1 =
40     theAxis.x() * (theAxis.x() < 0.0 ? theBoxMax.x() : theBoxMin.x()) +
41     theAxis.y() * (theAxis.y() < 0.0 ? theBoxMax.y() : theBoxMin.y()) +
42     theAxis.z() * (theAxis.z() < 0.0 ? theBoxMax.z() : theBoxMin.z());
43
44   const Standard_Real aBoxProj2 =
45     theAxis.x() * (theAxis.x() < 0.0 ? theBoxMin.x() : theBoxMax.x()) +
46     theAxis.y() * (theAxis.y() < 0.0 ? theBoxMin.y() : theBoxMax.y()) +
47     theAxis.z() * (theAxis.z() < 0.0 ? theBoxMin.z() : theBoxMax.z());
48
49   aMinB = Min (aBoxProj1, aBoxProj2);
50   aMaxB = Max (aBoxProj1, aBoxProj2);
51
52   for (Standard_Integer aVertIdx = 0; aVertIdx < N * 2; ++aVertIdx)
53   {
54     const Standard_Real aProj = DOT (myVertices[aVertIdx], theAxis);
55
56     aMinF = Min (aMinF, aProj);
57     aMaxF = Max (aMaxF, aProj);
58
59     if (aMinF <= aMaxB && aMaxF >= aMinB)
60     {
61       return Standard_False;
62     }
63   }
64
65   return aMinF > aMaxB || aMaxF < aMinB;
66 }
67
68 // =======================================================================
69 // function : isSeparated
70 // purpose  : Checks if triangle and frustum are separated along the
71 //            given axis
72 // =======================================================================
73 template <int N>
74 Standard_Boolean SelectMgr_Frustum<N>::isSeparated (const gp_Pnt& thePnt1,
75                                                     const gp_Pnt& thePnt2,
76                                                     const gp_Pnt& thePnt3,
77                                                     const SelectMgr_Vec3& theAxis) const
78 {
79   // frustum projection
80   Standard_Real aMinF = RealLast();
81   Standard_Real aMaxF = RealFirst();
82
83   // triangle projection
84   Standard_Real aMinTr = RealLast();
85   Standard_Real aMaxTr = RealFirst();
86
87   Standard_Real aTriangleProj;
88
89   aTriangleProj = DOTp (theAxis, thePnt1);
90   aMinTr = Min (aMinTr, aTriangleProj);
91   aMaxTr = Max (aMaxTr, aTriangleProj);
92
93   aTriangleProj = DOTp (theAxis, thePnt2);
94   aMinTr = Min (aMinTr, aTriangleProj);
95   aMaxTr = Max (aMaxTr, aTriangleProj);
96
97   aTriangleProj = DOTp (theAxis, thePnt3);
98   aMinTr = Min (aMinTr, aTriangleProj);
99   aMaxTr = Max (aMaxTr, aTriangleProj);
100
101   for (Standard_Integer aVertIter = 0; aVertIter < N * 2; ++aVertIter)
102   {
103     const Standard_Real aProj = DOT (myVertices[aVertIter], theAxis);
104
105     aMinF = Min (aMinF, aProj);
106     aMaxF = Max (aMaxF, aProj);
107
108     if (aMinF <= aMaxTr && aMaxF >= aMinTr)
109     {
110       return Standard_False;
111     }
112   }
113
114   return aMinF > aMaxTr || aMaxF < aMinTr;
115 }
116
117 // =======================================================================
118 // function : hasOverlap
119 // purpose  : Returns true if selecting volume is overlapped by
120 //            axis-aligned bounding box with minimum corner at point
121 //            theMinPnt and maximum at point theMaxPnt
122 // =======================================================================
123 template <int N>
124 const Standard_Boolean SelectMgr_Frustum<N>::hasOverlap (const SelectMgr_Vec3& theMinPnt,
125                                                          const SelectMgr_Vec3& theMaxPnt)
126 {
127   // E0 test
128   if (theMinPnt.x() > myMaxOrthoVertsProjections[0]
129   || theMaxPnt.x() < myMinOrthoVertsProjections[0])
130   {
131     return Standard_False;
132   }
133
134   // E1 test
135   if (theMinPnt.y() > myMaxOrthoVertsProjections[1]
136   || theMaxPnt.y() < myMinOrthoVertsProjections[1])
137   {
138     return Standard_False;
139   }
140
141   // E2 test
142   if (theMinPnt.z() > myMaxOrthoVertsProjections[2]
143   || theMaxPnt.z() < myMinOrthoVertsProjections[2])
144   {
145     return Standard_False;
146   }
147
148   const Standard_Integer anIncFactor = (myIsOrthographic && N == 4) ? 2 : 1;
149   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < N + 1; aPlaneIdx += anIncFactor)
150   {
151     Standard_Real aBoxProjMax = RealFirst();
152     Standard_Real aBoxProjMin = RealLast();
153     Standard_Real aFrustumProjMax = RealFirst();
154     Standard_Real aFrustumProjMin = RealLast();
155     SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
156
157     aFrustumProjMax = myMaxVertsProjections[aPlaneIdx];
158     aFrustumProjMin = myMinVertsProjections[aPlaneIdx];
159
160     const Standard_Real aBoxProj1 =
161       aPlane.x() * (aPlane.x() < 0.f ? theMaxPnt.x() : theMinPnt.x()) +
162       aPlane.y() * (aPlane.y() < 0.f ? theMaxPnt.y() : theMinPnt.y()) +
163       aPlane.z() * (aPlane.z() < 0.f ? theMaxPnt.z() : theMinPnt.z());
164
165     const Standard_Real aBoxProj2 =
166       aPlane.x() * (aPlane.x() < 0.f ? theMinPnt.x() : theMaxPnt.x()) +
167       aPlane.y() * (aPlane.y() < 0.f ? theMinPnt.y() : theMaxPnt.y()) +
168       aPlane.z() * (aPlane.z() < 0.f ? theMinPnt.z() : theMaxPnt.z());
169
170     aBoxProjMin = Min (aBoxProj1, aBoxProj2);
171     aBoxProjMax = Max (aBoxProj1, aBoxProj2);
172
173     if (aBoxProjMin > aFrustumProjMax
174       || aBoxProjMax < aFrustumProjMin)
175     {
176       return Standard_False;
177     }
178   }
179
180   SelectMgr_Vec3 aBndBoxDimensions[3] =
181   {
182     SelectMgr_Vec3 (1.0, 0.0, 0.0),
183     SelectMgr_Vec3 (0.0, 1.0, 0.0),
184     SelectMgr_Vec3 (0.0, 0.0, 1.0)
185   };
186
187   Standard_Integer aDirectionsNb = myIsOrthographic ? 4 : 6;
188   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
189   {
190     for (Standard_Integer aVolDir = 0; aVolDir < aDirectionsNb; ++aVolDir)
191     {
192       SelectMgr_Vec3 anEdge1 = aBndBoxDimensions[aDim];
193       SelectMgr_Vec3 anEdge2 = myEdgeDirs[aVolDir];
194       SelectMgr_Vec3 aTestDirection = SelectMgr_Vec3 (anEdge1.y() * anEdge2.z() - anEdge1.z() * anEdge2.y(),
195                                                       anEdge1.z() * anEdge2.x() - anEdge1.x() * anEdge2.z(),
196                                                       anEdge1.x() * anEdge2.y() - anEdge1.y() * anEdge2.x());
197
198       if (isSeparated (theMinPnt, theMaxPnt, aTestDirection))
199       {
200         return Standard_False;
201       }
202     }
203   }
204
205   return Standard_True;
206 }
207
208 // =======================================================================
209 // function : hasOverlap
210 // purpose  : SAT intersection test between defined volume and given point
211 // =======================================================================
212 template <int N>
213 const Standard_Boolean SelectMgr_Frustum<N>::hasOverlap (const gp_Pnt& thePnt)
214 {
215   SelectMgr_Vec3 aPnt (thePnt.X(), thePnt.Y(), thePnt.Z());
216
217   const Standard_Integer anIncFactor = (myIsOrthographic && N == 4) ? 2 : 1;
218   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < N + 1; aPlaneIdx += anIncFactor)
219   {
220     Standard_Real aPointProj      = RealLast();
221     Standard_Real aFrustumProjMax = RealFirst();
222     Standard_Real aFrustumProjMin = RealLast();
223     SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
224
225     aPointProj = DOT (aPlane, aPnt);
226
227     aFrustumProjMax = myMaxVertsProjections[aPlaneIdx];
228     aFrustumProjMin = myMinVertsProjections[aPlaneIdx];
229
230     if (aPointProj > aFrustumProjMax
231       || aPointProj < aFrustumProjMin)
232     {
233       return Standard_False;
234     }
235   }
236
237   return Standard_True;
238 }
239
240 // =======================================================================
241 // function : hasOverlap
242 // purpose  : SAT intersection test between defined volume and given segment
243 // =======================================================================
244 template <int N>
245 const Standard_Boolean SelectMgr_Frustum<N>::hasOverlap (const gp_Pnt& theStartPnt,
246                                                          const gp_Pnt& theEndPnt)
247 {
248   const SelectMgr_Vec3& aDir = SelectMgr_Vec3 (theEndPnt.X() - theStartPnt.X(),
249                                                theEndPnt.Y() - theStartPnt.Y(),
250                                                theEndPnt.Z() - theStartPnt.Z());
251   if (std::sqrt (aDir.x() * aDir.x() + aDir.y() * aDir.y() + aDir.z () * aDir.z()) < Precision::Confusion())
252     return Standard_True;
253
254   const Standard_Integer anIncFactor = (myIsOrthographic && N == 4) ? 2 : 1;
255   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < N + 1; aPlaneIdx += anIncFactor)
256   {
257     Standard_Real aMinSegm = RealLast(), aMaxSegm = RealFirst();
258     Standard_Real aMinF    = RealLast(), aMaxF    = RealFirst();
259     SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
260
261     Standard_Real aProj1 = DOTp (aPlane, theStartPnt);
262     Standard_Real aProj2 = DOTp (aPlane, theEndPnt);
263     aMinSegm = Min (aProj1, aProj2);
264     aMaxSegm = Max (aProj1, aProj2);
265
266     aMaxF = myMaxVertsProjections[aPlaneIdx];
267     aMinF = myMinVertsProjections[aPlaneIdx];
268
269     if (aMinSegm > aMaxF
270       || aMaxSegm < aMinF)
271     {
272       return Standard_False;
273     }
274   }
275
276   Standard_Real aMin1 = DBL_MAX, aMax1 = -DBL_MAX;
277   Standard_Real aMin2 = DBL_MAX, aMax2 = -DBL_MAX;
278   for (Standard_Integer aVertIdx = 0; aVertIdx < N * 2; ++aVertIdx)
279   {
280     Standard_Real aProjection = DOT (aDir, myVertices[aVertIdx]);
281     aMax2 = Max (aMax2, aProjection);
282     aMin2 = Min (aMin2, aProjection);
283   }
284   Standard_Real aProj1 = DOTp (aDir, theStartPnt);
285   Standard_Real aProj2 = DOTp (aDir, theEndPnt);
286   aMin1 = Min (aProj1, aProj2);
287   aMax1 = Max (aProj1, aProj2);
288   if (aMin1 > aMax2
289     || aMax1 < aMin2)
290   {
291     return Standard_False;
292   }
293
294   Standard_Integer aDirectionsNb = myIsOrthographic ? 4 : 6;
295   for (Standard_Integer aEdgeDirIdx = 0; aEdgeDirIdx < aDirectionsNb; ++aEdgeDirIdx)
296   {
297     Standard_Real aMinSegm = DBL_MAX, aMaxSegm = -DBL_MAX;
298     Standard_Real aMinF    = DBL_MAX, aMaxF    = -DBL_MAX;
299
300     SelectMgr_Vec3 aTestDir = SelectMgr_Vec3 (aDir.y() * myEdgeDirs[aEdgeDirIdx].z() - aDir.z() * myEdgeDirs[aEdgeDirIdx].y(),
301       aDir.z() * myEdgeDirs[aEdgeDirIdx].x() - aDir.x() * myEdgeDirs[aEdgeDirIdx].z(),
302       aDir.x() * myEdgeDirs[aEdgeDirIdx].y() - aDir.y() * myEdgeDirs[aEdgeDirIdx].x());
303
304     Standard_Real Proj1 = DOTp (aTestDir, theStartPnt);
305     Standard_Real Proj2 = DOTp (aTestDir, theEndPnt);
306     aMinSegm = Min (Proj1, Proj2);
307     aMaxSegm = Max (Proj1, Proj2);
308
309     for (Standard_Integer aVertIdx = 0; aVertIdx < N * 2; ++aVertIdx)
310     {
311       Standard_Real aProjection = DOT (aTestDir, myVertices[aVertIdx]);
312       aMaxF = Max (aMaxF, aProjection);
313       aMinF = Min (aMinF, aProjection);
314     }
315
316     if (aMinSegm > aMaxF
317       || aMaxSegm < aMinF)
318     {
319       return Standard_False;
320     }
321   }
322
323   return Standard_True;
324 }
325
326 // =======================================================================
327 // function : hasOverlap
328 // purpose  : SAT intersection test between frustum given and planar convex
329 //            polygon represented as ordered point set
330 // =======================================================================
331 template <int N>
332 const Standard_Boolean SelectMgr_Frustum<N>::hasOverlap (const Handle(TColgp_HArray1OfPnt)& theArrayOfPnts,
333                                                          SelectMgr_Vec3& theNormal)
334 {
335   Standard_Integer aStartIdx = theArrayOfPnts->Lower();
336   Standard_Integer anEndIdx = theArrayOfPnts->Upper();
337
338   const gp_Pnt& aPnt1 = theArrayOfPnts->Value (aStartIdx);
339   const gp_Pnt& aPnt2 = theArrayOfPnts->Value (aStartIdx + 1);
340   const gp_Pnt& aPnt3 = theArrayOfPnts->Value (aStartIdx + 2);
341   const gp_XYZ aVec1 = aPnt1.XYZ() - aPnt2.XYZ();
342   const gp_XYZ aVec2 = aPnt3.XYZ() - aPnt2.XYZ();
343   theNormal = SelectMgr_Vec3 (aVec2.Y() * aVec1.Z() - aVec2.Z() * aVec1.Y(),
344                               aVec2.Z() * aVec1.X() - aVec2.X() * aVec1.Z(),
345                               aVec2.X() * aVec1.Y() - aVec2.Y() * aVec1.X());
346   Standard_Real aPolygProjection = DOTp (theNormal, aPnt1);
347
348   Standard_Real aMax = RealFirst();
349   Standard_Real aMin = RealLast();
350   for (Standard_Integer aVertIdx = 0; aVertIdx < N * 2; ++aVertIdx)
351   {
352     Standard_Real aProjection = DOT (theNormal, myVertices[aVertIdx]);
353     aMax = Max (aMax, aProjection);
354     aMin = Min (aMin, aProjection);
355   }
356   if (aPolygProjection > aMax
357     || aPolygProjection < aMin)
358   {
359     return Standard_False;
360   }
361
362   Standard_Integer aPlanesNb = N == 4 ? N + 2 : N + 1;
363   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < aPlanesNb; ++aPlaneIdx)
364   {
365     Standard_Real aMaxF = RealFirst();
366     Standard_Real aMinF = RealLast();
367     Standard_Real aMaxPolyg = RealFirst();
368     Standard_Real aMinPolyg = RealLast();
369     SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
370     for (Standard_Integer aPntIter = aStartIdx; aPntIter <= anEndIdx; ++aPntIter)
371     {
372       Standard_Real aProjection = DOTp (aPlane, theArrayOfPnts->Value (aPntIter));
373       aMaxPolyg = Max (aMaxPolyg, aProjection);
374       aMinPolyg = Min (aMinPolyg, aProjection);
375     }
376     aMaxF = myMaxVertsProjections[aPlaneIdx];
377     aMinF = myMinVertsProjections[aPlaneIdx];
378     if (aMinPolyg > aMaxF
379       || aMaxPolyg < aMinF)
380     {
381       return Standard_False;
382     }
383   }
384
385   Standard_Integer aDirectionsNb = myIsOrthographic ? 4 : 6;
386   for (Standard_Integer aPntsIter = aStartIdx; aPntsIter <= anEndIdx; ++aPntsIter)
387   {
388     const gp_XYZ aSegmDir = aPntsIter == anEndIdx ? theArrayOfPnts->Value (aStartIdx).XYZ() - theArrayOfPnts->Value (anEndIdx).XYZ()
389                                              : theArrayOfPnts->Value (aPntsIter + 1).XYZ() - theArrayOfPnts->Value (aPntsIter).XYZ();
390     for (Standard_Integer aVolDir = 0; aVolDir < aDirectionsNb; ++aVolDir)
391     {
392       Standard_Real aMaxPolyg = RealFirst();
393       Standard_Real aMinPolyg = RealLast();
394       Standard_Real aMaxF = RealFirst();
395       Standard_Real aMinF = RealLast();
396       SelectMgr_Vec3 aTestDir = SelectMgr_Vec3 (aSegmDir.Y() * myEdgeDirs[aVolDir].z() - aSegmDir.Z() * myEdgeDirs[aVolDir].y(),
397                                                 aSegmDir.Z() * myEdgeDirs[aVolDir].x() - aSegmDir.X() * myEdgeDirs[aVolDir].z(),
398                                                 aSegmDir.X() * myEdgeDirs[aVolDir].y() - aSegmDir.Y() * myEdgeDirs[aVolDir].x());
399
400       for (Standard_Integer aPntIter = aStartIdx; aPntIter <= anEndIdx; ++aPntIter)
401       {
402         Standard_Real aProjection = DOTp (aTestDir, theArrayOfPnts->Value (aPntIter));
403         aMaxPolyg = Max (aMaxPolyg, aProjection);
404         aMinPolyg = Min (aMinPolyg, aProjection);
405       }
406
407       for (Standard_Integer aVertIdx = 0; aVertIdx < N * 2; ++aVertIdx)
408       {
409         Standard_Real aProjection = DOT (aTestDir, myVertices[aVertIdx]);
410         aMaxF = Max (aMaxF, aProjection);
411         aMinF = Min (aMinF, aProjection);
412       }
413
414       if (aMinPolyg > aMaxF
415         || aMaxPolyg < aMinF)
416       {
417         return Standard_False;
418       }
419     }
420   }
421
422   return Standard_True;
423 }
424
425 // =======================================================================
426 // function : hasOverlap
427 // purpose  : SAT intersection test between defined volume and given triangle
428 // =======================================================================
429 template <int N>
430 const Standard_Boolean SelectMgr_Frustum<N>::hasOverlap (const gp_Pnt& thePnt1,
431                                                          const gp_Pnt& thePnt2,
432                                                          const gp_Pnt& thePnt3,
433                                                          SelectMgr_Vec3& theNormal)
434 {
435
436   SelectMgr_Vec3 aPnt1 (thePnt1.X(), thePnt1.Y(), thePnt1.Z());
437   SelectMgr_Vec3 aPnt2 (thePnt2.X(), thePnt2.Y(), thePnt2.Z());
438   SelectMgr_Vec3 aPnt3 (thePnt3.X(), thePnt3.Y(), thePnt3.Z());
439   SelectMgr_Vec3 aTrEdges[3] = { aPnt2 - aPnt1,
440                                  aPnt3 - aPnt2,
441                                  aPnt1 - aPnt3 };
442
443   const Standard_Integer anIncFactor = (myIsOrthographic && N == 4) ? 2 : 1;
444   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < N + 1; aPlaneIdx += anIncFactor)
445   {
446     SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
447     Standard_Real aTriangleProj;
448
449     aTriangleProj = DOT (aPlane, aPnt1);
450     Standard_Real aTriangleProjMin = aTriangleProj;
451     Standard_Real aTriangleProjMax = aTriangleProj;
452
453     aTriangleProj = DOT (aPlane, aPnt2);
454     aTriangleProjMin = Min (aTriangleProjMin, aTriangleProj);
455     aTriangleProjMax = Max (aTriangleProjMax, aTriangleProj);
456
457     aTriangleProj = DOT (aPlane, aPnt3);
458     aTriangleProjMin = Min (aTriangleProjMin, aTriangleProj);
459     aTriangleProjMax = Max (aTriangleProjMax, aTriangleProj);
460
461     Standard_Real aFrustumProjMax = myMaxVertsProjections[aPlaneIdx];
462     Standard_Real aFrustumProjMin = myMinVertsProjections[aPlaneIdx];
463     if (aTriangleProjMin > aFrustumProjMax
464       || aTriangleProjMax < aFrustumProjMin)
465     {
466       return Standard_False;
467     }
468   }
469
470   theNormal = SelectMgr_Vec3 (aTrEdges[2].y() * aTrEdges[0].z() - aTrEdges[2].z() * aTrEdges[0].y(),
471                               aTrEdges[2].z() * aTrEdges[0].x() - aTrEdges[2].x() * aTrEdges[0].z(),
472                               aTrEdges[2].x() * aTrEdges[0].y() - aTrEdges[2].y() * aTrEdges[0].x());
473   if (isSeparated (thePnt1, thePnt2, thePnt3, theNormal))
474   {
475     return Standard_False;
476   }
477
478   Standard_Integer aDirectionsNb = myIsOrthographic ? 4 : 6;
479   for (Standard_Integer aTriangleEdgeIdx = 0; aTriangleEdgeIdx < 3; ++aTriangleEdgeIdx)
480   {
481     for (Standard_Integer aVolDir = 0; aVolDir < aDirectionsNb; ++aVolDir)
482     {
483       SelectMgr_Vec3 anEdge1 = myEdgeDirs[aVolDir];
484       SelectMgr_Vec3 anEdge2 = aTrEdges[aTriangleEdgeIdx];
485       SelectMgr_Vec3 aTestDirection = SelectMgr_Vec3 (
486         anEdge1.y() * anEdge2.z() - anEdge1.z() * anEdge2.y(),
487         anEdge1.z() * anEdge2.x() - anEdge1.x() * anEdge2.z(),
488         anEdge1.x() * anEdge2.y() - anEdge1.y() * anEdge2.x());
489
490       if (isSeparated (thePnt1, thePnt2, thePnt3, aTestDirection))
491       {
492         return Standard_False;
493       }
494     }
495   }
496
497   return Standard_True;
498 }
499
500 #undef DOT
501 #undef DOTp