0029729: Visualization, Graphic3d_ClipPlane - add support of clipping plane chains
[occt.git] / src / SelectMgr / SelectMgr_RectangularFrustum.cxx
CommitLineData
f751596e 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
f751596e 21// =======================================================================
22// function : segmentSegmentDistance
23// purpose :
24// =======================================================================
25void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
26 const gp_Pnt& theSegPnt2,
27 Standard_Real& theDepth)
28{
3bf9a45f 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);
f751596e 38 Standard_Real aCoef = anA * aC - aB * aB;
a52c06d5 39 Standard_Real aSn = aCoef;
f751596e 40 Standard_Real aTc, aTn, aTd = aCoef;
41
fe76088c 42 if (aCoef < gp::Resolution())
f751596e 43 {
f751596e 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 {
f751596e 53 aTn = anE;
54 aTd = aC;
55 }
a52c06d5 56 else if (aSn > aCoef)
f751596e 57 {
f751596e 58 aTn = anE + aB;
59 aTd = aC;
60 }
61 }
62
63 if (aTn < 0.0)
64 {
65 aTn = 0.0;
f751596e 66 }
67 else if (aTn > aTd)
68 {
69 aTn = aTd;
f751596e 70 }
fe76088c 71 aTc = (Abs (aTd) < gp::Resolution() ? 0.0 : aTn / aTd);
f751596e 72
3bf9a45f 73 gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTc;
7479f643 74 theDepth = myNearPickedPnt.Distance (aClosestPnt) * myScale;
f751596e 75}
76
77// =======================================================================
78// function : segmentPlaneIntersection
79// purpose :
80// =======================================================================
3bf9a45f 81void SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePlane,
f751596e 82 const gp_Pnt& thePntOnPlane,
83 Standard_Real& theDepth)
84{
3bf9a45f 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);
f751596e 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
3bf9a45f 111 gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + anU * aParam;
7479f643 112 theDepth = myNearPickedPnt.Distance (aClosestPnt) * myScale;
f751596e 113}
114
2157d6ac 115namespace
116{
3bf9a45f 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
2157d6ac 172 // =======================================================================
173 // function : computeNormals
174 // purpose : Computes normals to frustum faces
175 // =======================================================================
3bf9a45f 176 void computeNormals (const gp_Vec* theEdges, gp_Vec* theNormals)
2157d6ac 177 {
178 // Top
3bf9a45f 179 theNormals[0] = theEdges[0].Crossed (theEdges[4]);
2157d6ac 180 // Bottom
099f3513 181 theNormals[1] = theEdges[2].Crossed (theEdges[0]);
2157d6ac 182 // Left
3bf9a45f 183 theNormals[2] = theEdges[4].Crossed (theEdges[1]);
2157d6ac 184 // Right
099f3513 185 theNormals[3] = theEdges[1].Crossed (theEdges[5]);
2157d6ac 186 // Near
3bf9a45f 187 theNormals[4] = theEdges[0].Crossed (theEdges[1]);
2157d6ac 188 // Far
3bf9a45f 189 theNormals[5] = -theNormals[4];
2157d6ac 190 }
191}
192
f751596e 193// =======================================================================
3bf9a45f 194// function : cacheVertexProjections
195// purpose : Caches projection of frustum's vertices onto its plane directions
196// and {i, j, k}
f751596e 197// =======================================================================
099f3513 198void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const
f751596e 199{
3bf9a45f 200 if (theFrustum->myIsOrthographic)
f751596e 201 {
3bf9a45f 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)
f751596e 210 {
3bf9a45f 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);
f751596e 215 }
f751596e 216 }
3bf9a45f 217 else
f751596e 218 {
3bf9a45f 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 }
f751596e 235
3bf9a45f 236 // project vertices onto {i, j, k}
f751596e 237 for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
238 {
239 Standard_Real aMax = -DBL_MAX;
3bf9a45f 240 Standard_Real aMin = DBL_MAX;
f751596e 241 for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
242 {
3bf9a45f 243 const gp_XYZ& aVert = theFrustum->myVertices[aVertIdx].XYZ();
244 aMax = Max (aVert.GetData()[aDim], aMax);
245 aMin = Min (aVert.GetData()[aDim], aMin);
f751596e 246 }
3bf9a45f 247 theFrustum->myMaxOrthoVertsProjections[aDim] = aMax;
248 theFrustum->myMinOrthoVertsProjections[aDim] = aMin;
f751596e 249 }
3bf9a45f 250}
251
252// =======================================================================
253// function : Build
254// purpose : Build volume according to the point and given pixel
255// tolerance
256// =======================================================================
257void 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;
f751596e 263
3bf9a45f 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);
7479f643 278
25c35042 279 myViewClipRange.SetVoid();
e9312c0f 280
7479f643 281 myScale = 1.0;
f751596e 282}
283
284// =======================================================================
285// function : Build
286// purpose : Build volume according to the selected rectangle
287// =======================================================================
288void 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);
3bf9a45f 297 myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ();
f751596e 298
3bf9a45f 299 // calculate base frustum characteristics: vertices and edge directions
300 computeFrustum (theMinPnt, theMaxPnt, myBuilder, myVertices, myEdgeDirs);
f751596e 301
3bf9a45f 302 // compute frustum normals
303 computeNormals (myEdgeDirs, myPlanes);
f751596e 304
3bf9a45f 305 // compute vertices projections onto frustum normals and
306 // {i, j, k} vectors and store them to corresponding class fields
307 cacheVertexProjections (this);
7479f643 308
25c35042 309 myViewClipRange.SetVoid();
e9312c0f 310
7479f643 311 myScale = 1.0;
f751596e 312}
313
314// =======================================================================
3bf9a45f 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.
f751596e 324// =======================================================================
099f3513 325Handle(SelectMgr_BaseFrustum) SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
326 const gp_GTrsf& theTrsf) const
f751596e 327{
3bf9a45f 328 Standard_ASSERT_RAISE (theScaleFactor > 0,
329 "Error! Pixel tolerance for selection should be greater than zero");
330
099f3513 331 Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
3bf9a45f 332 const Standard_Boolean isToScale = theScaleFactor != 1;
333 const Standard_Boolean isToTrsf = theTrsf.Form() != gp_Identity;
f751596e 334
3bf9a45f 335 if (!isToScale && !isToTrsf)
336 return aRes;
f751596e 337
338 aRes->myIsOrthographic = myIsOrthographic;
099f3513 339 const SelectMgr_RectangularFrustum* aRef = this;
f751596e 340
3bf9a45f 341 if (isToScale)
28ee613b 342 {
3bf9a45f 343 aRes->myNearPickedPnt = myNearPickedPnt;
344 aRes->myFarPickedPnt = myFarPickedPnt;
345 aRes->myViewRayDir = myViewRayDir;
28ee613b 346
3bf9a45f 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);
28ee613b 351
3bf9a45f 352 // recompute base frustum characteristics from scratch
353 computeFrustum (aMinPnt, aMaxPnt, myBuilder, aRes->myVertices, aRes->myEdgeDirs);
f751596e 354
099f3513 355 aRef = aRes.get();
f751596e 356 }
357
3bf9a45f 358 if (isToTrsf)
f751596e 359 {
91d96372 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
3bf9a45f 370 aRes->myViewRayDir = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
371
91d96372 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 }
3bf9a45f 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();
7479f643 391
91d96372 392 // Compute scale to transform depth from local coordinate system to world coordinate system
393 aRes->myScale = Sqrt (aRefScale / aRes->myFarPickedPnt.SquareDistance (aRes->myNearPickedPnt));
f751596e 394 }
395
3bf9a45f 396 // compute frustum normals
397 computeNormals (aRes->myEdgeDirs, aRes->myPlanes);
398
099f3513 399 cacheVertexProjections (aRes.get());
f751596e 400
e9312c0f 401 aRes->myViewClipRange = myViewClipRange;
3202bf1e 402 aRes->myIsViewClipEnabled = myIsViewClipEnabled;
099f3513 403 aRes->myMousePos = myMousePos;
e9312c0f 404
099f3513 405 return aRes;
f751596e 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// =======================================================================
2157d6ac 414Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& theBoxMin,
415 const SelectMgr_Vec3& theBoxMax,
416 Standard_Boolean* theInside)
f751596e 417{
2157d6ac 418 return hasOverlap (theBoxMin, theBoxMax, theInside);
f751596e 419}
420
421// =======================================================================
422// function : Overlaps
423// purpose : SAT intersection test between defined volume and
424// given axis-aligned box
425// =======================================================================
3bf9a45f 426Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& theBoxMin,
427 const SelectMgr_Vec3& theBoxMax,
7ab15952 428 Standard_Real& theDepth)
f751596e 429{
3bf9a45f 430 if (!hasOverlap (theBoxMin, theBoxMax))
f751596e 431 return Standard_False;
432
3bf9a45f 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()));
f751596e 437
3bf9a45f 438 theDepth = aNearestPnt.Distance (myNearPickedPnt);
f751596e 439
e9312c0f 440 return isViewClippingOk (theDepth);
f751596e 441}
442
443// =======================================================================
444// function : Overlaps
445// purpose : Intersection test between defined volume and given point
446// =======================================================================
7ab15952 447Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt,
448 Standard_Real& theDepth)
f751596e 449{
450 if (!hasOverlap (thePnt))
451 return Standard_False;
452
3bf9a45f 453 gp_XYZ aV = thePnt.XYZ() - myNearPickedPnt.XYZ();
454 gp_Pnt aDetectedPnt =
455 myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * (aV.Dot (myViewRayDir.XYZ()) / myViewRayDir.Dot (myViewRayDir));
f751596e 456
7479f643 457 theDepth = aDetectedPnt.Distance (myNearPickedPnt) * myScale;
f751596e 458
e9312c0f 459 return isViewClippingOk (theDepth);
f751596e 460}
461
3bf9a45f 462// =======================================================================
463// function : Overlaps
464// purpose : Intersection test between defined volume and given point
465// =======================================================================
466Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt)
467{
468 return hasOverlap (thePnt);
469}
470
f751596e 471// =======================================================================
472// function : Overlaps
473// purpose : Checks if line segment overlaps selecting frustum
474// =======================================================================
7ab15952 475Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
476 const gp_Pnt& thePnt2,
477 Standard_Real& theDepth)
f751596e 478{
479 theDepth = -DBL_MAX;
480 if (!hasOverlap (thePnt1, thePnt2))
481 return Standard_False;
482
483 segmentSegmentDistance (thePnt1, thePnt2, theDepth);
e9312c0f 484
485 return isViewClippingOk (theDepth);
f751596e 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// =======================================================================
114b7bf1 495Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const TColgp_Array1OfPnt& theArrayOfPnts,
7ab15952 496 Select3D_TypeOfSensitivity theSensType,
497 Standard_Real& theDepth)
f751596e 498{
499 if (theSensType == Select3D_TOS_BOUNDARY)
500 {
501 Standard_Integer aMatchingSegmentsNb = -1;
502 theDepth = DBL_MAX;
114b7bf1 503 const Standard_Integer aLower = theArrayOfPnts.Lower();
504 const Standard_Integer anUpper = theArrayOfPnts.Upper();
f751596e 505 for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
506 {
114b7bf1 507 const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
508 const gp_Pnt& aEndPnt = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
f751596e 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 {
3bf9a45f 523 gp_Vec aPolyNorm (gp_XYZ (RealLast(), RealLast(), RealLast()));
f751596e 524 if (!hasOverlap (theArrayOfPnts, aPolyNorm))
525 return Standard_False;
526
527 segmentPlaneIntersection (aPolyNorm,
114b7bf1 528 theArrayOfPnts.Value (theArrayOfPnts.Lower()),
f751596e 529 theDepth);
530 }
531
e9312c0f 532 return isViewClippingOk (theDepth);
f751596e 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// =======================================================================
7ab15952 542Standard_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)
f751596e 547{
548 if (theSensType == Select3D_TOS_BOUNDARY)
549 {
114b7bf1 550 const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
551 const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
f751596e 552 return Overlaps (aPntsArray, Select3D_TOS_BOUNDARY, theDepth);
553 }
554 else if (theSensType == Select3D_TOS_INTERIOR)
555 {
3bf9a45f 556 gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
f751596e 557 if (!hasOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
558 return Standard_False;
559
560 // check if intersection point belongs to triangle's interior part
3bf9a45f 561 gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
562 thePnt3.XYZ() - thePnt2.XYZ(),
563 thePnt1.XYZ() - thePnt3.XYZ() };
7e17e8f0 564
3bf9a45f 565 Standard_Real anAlpha = aTriangleNormal.Dot (myViewRayDir);
7e17e8f0 566 if (Abs (anAlpha) < gp::Resolution())
567 {
568 // handle degenerated triangles: in this case, there is no possible way to detect overlap correctly.
3bf9a45f 569 if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
7e17e8f0 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.
3bf9a45f 577 const gp_XYZ aDiff = myNearPickedPnt.XYZ() - thePnt1.XYZ();
7479f643 578 theDepth = aTriangleNormal.Dot (aDiff) * myScale;
e9312c0f 579
580 return isViewClippingOk (theDepth);
7e17e8f0 581 }
582
3bf9a45f 583 gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
f751596e 584
3bf9a45f 585 Standard_Real aTime = aTriangleNormal.Dot (anEdge);
f751596e 586
3bf9a45f 587 gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
f751596e 588
3bf9a45f 589 Standard_Real anU = aVec.Dot (aTrEdges[2]);
590 Standard_Real aV = aVec.Dot (aTrEdges[0]);
f751596e 591
592 Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
593
594 if (isInterior)
595 {
3bf9a45f 596 gp_Pnt aDetectedPnt = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
7479f643 597 theDepth = myNearPickedPnt.Distance (aDetectedPnt) * myScale;
e9312c0f 598
599 return isViewClippingOk (theDepth);
f751596e 600 }
601
602 gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
603 Standard_Real aMinDist = RealLast();
604 Standard_Integer aNearestEdgeIdx = -1;
3bf9a45f 605 gp_Pnt aPtOnPlane = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
f751596e 606 for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
607 {
3bf9a45f 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]);
f751596e 611 if (aMinDist > aDist)
612 {
613 aMinDist = aDist;
614 aNearestEdgeIdx = anEdgeIdx;
615 }
616 }
617 segmentSegmentDistance (aPnts[aNearestEdgeIdx], aPnts[(aNearestEdgeIdx + 1) % 3], theDepth);
618 }
619
e9312c0f 620 return isViewClippingOk (theDepth);
f751596e 621}
622
623// =======================================================================
624// function : DistToGeometryCenter
625// purpose : Measures distance between 3d projection of user-picked
626// screen point and given point theCOG
627// =======================================================================
7ab15952 628Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG)
f751596e 629{
7479f643 630 return theCOG.Distance (myNearPickedPnt) * myScale;
f751596e 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// =======================================================================
3bf9a45f 638gp_Pnt SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
f751596e 639{
be480fe7 640 return myNearPickedPnt.XYZ() + myViewRayDir.Normalized().XYZ() * theDepth / myScale;
f751596e 641}
642
643// =======================================================================
e9312c0f 644// function : computeClippingRange
645// purpose :
f751596e 646// =======================================================================
e9312c0f 647void SelectMgr_RectangularFrustum::computeClippingRange (const Graphic3d_SequenceOfHClipPlane& thePlanes,
25c35042 648 SelectMgr_ViewClipRange& theRange)
f751596e 649{
f751596e 650 Standard_Real aPlaneA, aPlaneB, aPlaneC, aPlaneD;
e9312c0f 651 for (Graphic3d_SequenceOfHClipPlane::Iterator aPlaneIt (thePlanes); aPlaneIt.More(); aPlaneIt.Next())
f751596e 652 {
653 const Handle(Graphic3d_ClipPlane)& aClipPlane = aPlaneIt.Value();
654 if (!aClipPlane->IsOn())
25c35042 655 {
f751596e 656 continue;
25c35042 657 }
f751596e 658
25c35042 659 Bnd_Range aSubRange (RealFirst(), RealLast());
660 for (const Graphic3d_ClipPlane* aSubPlaneIter = aClipPlane.get(); aSubPlaneIter != NULL; aSubPlaneIter = aSubPlaneIter->ChainNextPlane().get())
661 {
662 const gp_Pln aGeomPlane = aSubPlaneIter->ToPlane();
663 aGeomPlane.Coefficients (aPlaneA, aPlaneB, aPlaneC, aPlaneD);
f751596e 664
25c35042 665 const gp_XYZ& aPlaneDirXYZ = aGeomPlane.Axis().Direction().XYZ();
666 Standard_Real aDotProduct = myViewRayDir.XYZ().Dot (aPlaneDirXYZ);
667 Standard_Real aDistance = -myNearPickedPnt.XYZ().Dot (aPlaneDirXYZ) - aPlaneD;
f751596e 668
25c35042 669 // check whether the pick line is parallel to clip plane
670 if (Abs (aDotProduct) < Precision::Angular())
671 {
672 // line lies below the plane and is not clipped, skip
673 continue;
674 }
f751596e 675
25c35042 676 // compute distance to point of pick line intersection with the plane
677 const Standard_Real aParam = aDistance / aDotProduct;
f751596e 678
25c35042 679 const gp_Pnt anIntersectionPnt = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aParam;
680 Standard_Real aDistToPln = anIntersectionPnt.Distance (myNearPickedPnt);
681 if (aParam < 0.0)
682 {
683 // the plane is "behind" the ray
684 aDistToPln = -aDistToPln;
685 }
f751596e 686
25c35042 687 // change depth limits for case of opposite and directed planes
688 if (!aClipPlane->IsChain())
689 {
690 if (aDotProduct < 0.0)
691 {
692 theRange.ChangeMain().Add (Bnd_Range (aDistToPln, RealLast()));
693 }
694 else
695 {
696 theRange.ChangeMain().Add (Bnd_Range (RealFirst(), aDistToPln));
697 }
698 }
699 else
700 {
701 if (aDotProduct < 0.0)
702 {
703 aSubRange.TrimFrom (aDistToPln);
704 }
705 else
706 {
707 aSubRange.TrimTo (aDistToPln);
708 }
709 }
f751596e 710 }
711
25c35042 712 if (!aSubRange.IsVoid()
713 && aClipPlane->IsChain())
f751596e 714 {
25c35042 715 theRange.AddSubRange (aSubRange);
f751596e 716 }
717 }
e9312c0f 718}
719
720// =======================================================================
721// function : IsClipped
722// purpose : Checks if the point of sensitive in which selection was
723// detected belongs to the region defined by clipping planes
724// =======================================================================
725Standard_Boolean SelectMgr_RectangularFrustum::IsClipped (const Graphic3d_SequenceOfHClipPlane& thePlanes,
726 const Standard_Real theDepth)
727{
25c35042 728 SelectMgr_ViewClipRange aRange;
729 computeClippingRange (thePlanes, aRange);
730 return aRange.IsClipped (theDepth);
f751596e 731}
e9312c0f 732
733// =======================================================================
734// function : SetViewClipping
735// purpose :
736// =======================================================================
3202bf1e 737void SelectMgr_RectangularFrustum::SetViewClipping (const Handle(Graphic3d_SequenceOfHClipPlane)& thePlanes)
e9312c0f 738{
3202bf1e 739 if (thePlanes.IsNull()
740 || thePlanes->IsEmpty())
e9312c0f 741 {
25c35042 742 myViewClipRange.SetVoid();
e9312c0f 743 return;
744 }
745
25c35042 746 computeClippingRange (*thePlanes, myViewClipRange);
e9312c0f 747}
748
749// =======================================================================
750// function : isViewClippingOk
751// purpose :
752// =======================================================================
753Standard_Boolean SelectMgr_RectangularFrustum::isViewClippingOk (const Standard_Real theDepth) const
754{
25c35042 755 return !myIsViewClipEnabled
756 || !myViewClipRange.IsClipped (theDepth);
e9312c0f 757}
871dcdc2 758
759// =======================================================================
760// function : GetPlanes
761// purpose :
762// =======================================================================
763void SelectMgr_RectangularFrustum::GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const
764{
765 thePlaneEquations.Clear();
766
767 SelectMgr_Vec4 anEquation;
768 for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
769 {
770 const gp_Vec& aPlaneNorm = myIsOrthographic && aPlaneIdx % 2 == 1 ?
771 myPlanes[aPlaneIdx - 1].Reversed() : myPlanes[aPlaneIdx];
772 anEquation.x() = aPlaneNorm.X();
773 anEquation.y() = aPlaneNorm.Y();
774 anEquation.z() = aPlaneNorm.Z();
775 anEquation.w() = - (aPlaneNorm.XYZ().Dot (myVertices[aPlaneIdx % 2 == 0 ? aPlaneIdx : aPlaneIdx + 2].XYZ()));
776 thePlaneEquations.Append (anEquation);
777 }
778}