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