0032183: Visualization - implement AIS_LightSource::ProcessDragging() for rotating...
[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
e1eb39d2 16#include <SelectMgr_RectangularFrustum.hxx>
17
18#include <BVH_Tools.hxx>
503374ad 19#include <gp_Pln.hxx>
f751596e 20#include <NCollection_Vector.hxx>
21#include <Poly_Array1OfTriangle.hxx>
e1eb39d2 22#include <SelectMgr_FrustumBuilder.hxx>
23#include <SelectMgr_ViewClipRange.hxx>
f751596e 24
e1eb39d2 25// =======================================================================
26// function : Constructor
27// purpose :
28// =======================================================================
29SelectMgr_RectangularFrustum::SelectMgr_RectangularFrustum()
30 : myScale(1.0)
31{
32}
f751596e 33
f751596e 34// =======================================================================
35// function : segmentSegmentDistance
36// purpose :
37// =======================================================================
38void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
39 const gp_Pnt& theSegPnt2,
4a056d20 40 SelectBasics_PickResult& thePickResult) const
f751596e 41{
3bf9a45f 42 gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
2ff1d580 43 gp_XYZ aV = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane
3bf9a45f 44 gp_XYZ aW = theSegPnt1.XYZ() - myNearPickedPnt.XYZ();
45
46 Standard_Real anA = anU.Dot (anU);
47 Standard_Real aB = anU.Dot (aV);
48 Standard_Real aC = aV.Dot (aV);
49 Standard_Real aD = anU.Dot (aW);
50 Standard_Real anE = aV.Dot (aW);
f751596e 51 Standard_Real aCoef = anA * aC - aB * aB;
a52c06d5 52 Standard_Real aSn = aCoef;
f751596e 53 Standard_Real aTc, aTn, aTd = aCoef;
54
fe76088c 55 if (aCoef < gp::Resolution())
f751596e 56 {
f751596e 57 aTn = anE;
58 aTd = aC;
59 }
60 else
61 {
62 aSn = (aB * anE - aC * aD);
63 aTn = (anA * anE - aB * aD);
64 if (aSn < 0.0)
65 {
f751596e 66 aTn = anE;
67 aTd = aC;
68 }
a52c06d5 69 else if (aSn > aCoef)
f751596e 70 {
f751596e 71 aTn = anE + aB;
72 aTd = aC;
73 }
74 }
75
76 if (aTn < 0.0)
77 {
78 aTn = 0.0;
f751596e 79 }
80 else if (aTn > aTd)
81 {
82 aTn = aTd;
f751596e 83 }
fe76088c 84 aTc = (Abs (aTd) < gp::Resolution() ? 0.0 : aTn / aTd);
f751596e 85
2ff1d580 86 const gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + aV * aTc;
17017555 87 thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
88
89 const gp_Vec aPickedVec = aClosestPnt.XYZ() - theSegPnt1.XYZ();
90 const gp_Vec aFigureVec = theSegPnt2.XYZ() - theSegPnt1.XYZ();
91 const Standard_Real aPickedVecMod = aPickedVec.Magnitude();
92 const Standard_Real aFigureVecMod = aFigureVec.Magnitude();
93 if (aPickedVecMod <= gp::Resolution()
94 || aFigureVecMod <= gp::Resolution())
95 {
96 thePickResult.SetPickedPoint (aClosestPnt);
97 return;
98 }
99
100 const Standard_Real aCosOfAngle = aFigureVec.Dot (aPickedVec) / (aPickedVecMod * aFigureVecMod);
101 const Standard_Real aSegPntShift = Min(aFigureVecMod, Max(0.0, aCosOfAngle * aPickedVecMod));
102 thePickResult.SetPickedPoint (theSegPnt1.XYZ() + aFigureVec.XYZ() * (aSegPntShift / aFigureVecMod));
f751596e 103}
104
105// =======================================================================
106// function : segmentPlaneIntersection
107// purpose :
108// =======================================================================
17017555 109bool SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePlane,
f751596e 110 const gp_Pnt& thePntOnPlane,
4a056d20 111 SelectBasics_PickResult& thePickResult) const
f751596e 112{
2ff1d580 113 gp_XYZ anU = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane by > 1.0 check
3bf9a45f 114 gp_XYZ aW = myNearPickedPnt.XYZ() - thePntOnPlane.XYZ();
115 Standard_Real aD = thePlane.Dot (anU);
116 Standard_Real aN = -thePlane.Dot (aW);
f751596e 117
118 if (Abs (aD) < Precision::Confusion())
119 {
120 if (Abs (aN) < Precision::Angular())
121 {
17017555 122 thePickResult.Invalidate();
123 return false;
f751596e 124 }
125 else
126 {
17017555 127 thePickResult.Invalidate();
128 return false;
f751596e 129 }
130 }
131
132 Standard_Real aParam = aN / aD;
2ff1d580 133 if (aParam < 0.0 || aParam > 1.0) // > 1.0 check could be removed for an infinite ray and anU=myViewRayDir
f751596e 134 {
17017555 135 thePickResult.Invalidate();
136 return false;
f751596e 137 }
138
3bf9a45f 139 gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + anU * aParam;
17017555 140 thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
141 return true;
f751596e 142}
143
2157d6ac 144namespace
145{
3bf9a45f 146 // =======================================================================
147 // function : computeFrustum
148 // purpose : Computes base frustum data: its vertices and edge directions
149 // =======================================================================
150 void computeFrustum (const gp_Pnt2d theMinPnt, const gp_Pnt2d& theMaxPnt,
151 const Handle(SelectMgr_FrustumBuilder)& theBuilder,
152 gp_Pnt* theVertices, gp_Vec* theEdges)
153 {
154 // LeftTopNear
155 theVertices[0] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
156 theMaxPnt.Y(),
157 0.0);
158 // LeftTopFar
159 theVertices[1] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
160 theMaxPnt.Y(),
161 1.0);
162 // LeftBottomNear
163 theVertices[2] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
164 theMinPnt.Y(),
165 0.0);
166 // LeftBottomFar
167 theVertices[3] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
168 theMinPnt.Y(),
169 1.0);
170 // RightTopNear
171 theVertices[4] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
172 theMaxPnt.Y(),
173 0.0);
174 // RightTopFar
175 theVertices[5] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
176 theMaxPnt.Y(),
177 1.0);
178 // RightBottomNear
179 theVertices[6] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
180 theMinPnt.Y(),
181 0.0);
182 // RightBottomFar
183 theVertices[7] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
184 theMinPnt.Y(),
185 1.0);
186
187 // Horizontal
188 theEdges[0] = theVertices[4].XYZ() - theVertices[0].XYZ();
189 // Vertical
190 theEdges[1] = theVertices[2].XYZ() - theVertices[0].XYZ();
191 // LeftLower
192 theEdges[2] = theVertices[2].XYZ() - theVertices[3].XYZ();
193 // RightLower
194 theEdges[3] = theVertices[6].XYZ() - theVertices[7].XYZ();
195 // LeftUpper
196 theEdges[4] = theVertices[0].XYZ() - theVertices[1].XYZ();
197 // RightUpper
198 theEdges[5] = theVertices[4].XYZ() - theVertices[5].XYZ();
199 }
200
2157d6ac 201 // =======================================================================
202 // function : computeNormals
203 // purpose : Computes normals to frustum faces
204 // =======================================================================
3bf9a45f 205 void computeNormals (const gp_Vec* theEdges, gp_Vec* theNormals)
2157d6ac 206 {
207 // Top
3bf9a45f 208 theNormals[0] = theEdges[0].Crossed (theEdges[4]);
2157d6ac 209 // Bottom
099f3513 210 theNormals[1] = theEdges[2].Crossed (theEdges[0]);
2157d6ac 211 // Left
3bf9a45f 212 theNormals[2] = theEdges[4].Crossed (theEdges[1]);
2157d6ac 213 // Right
099f3513 214 theNormals[3] = theEdges[1].Crossed (theEdges[5]);
2157d6ac 215 // Near
3bf9a45f 216 theNormals[4] = theEdges[0].Crossed (theEdges[1]);
2157d6ac 217 // Far
3bf9a45f 218 theNormals[5] = -theNormals[4];
2157d6ac 219 }
220}
221
f751596e 222// =======================================================================
3bf9a45f 223// function : cacheVertexProjections
224// purpose : Caches projection of frustum's vertices onto its plane directions
225// and {i, j, k}
f751596e 226// =======================================================================
099f3513 227void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const
f751596e 228{
51d4a4f9 229 if (theFrustum->Camera()->IsOrthographic())
f751596e 230 {
3bf9a45f 231 // project vertices onto frustum normals
232 // Since orthographic view volume's faces are always a pairwise translation of
233 // one another, only 2 vertices that belong to opposite faces can be projected
234 // to simplify calculations.
235 Standard_Integer aVertIdxs[6] = { LeftTopNear, LeftBottomNear, // opposite planes in height direction
236 LeftBottomNear, RightBottomNear, // opposite planes in width direcion
237 LeftBottomFar, RightBottomNear }; // opposite planes in depth direction
238 for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 5; aPlaneIdx += 2)
f751596e 239 {
3bf9a45f 240 Standard_Real aProj1 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx]].XYZ());
241 Standard_Real aProj2 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx + 1]].XYZ());
242 theFrustum->myMinVertsProjections[aPlaneIdx] = Min (aProj1, aProj2);
243 theFrustum->myMaxVertsProjections[aPlaneIdx] = Max (aProj1, aProj2);
f751596e 244 }
f751596e 245 }
3bf9a45f 246 else
f751596e 247 {
3bf9a45f 248 // project all vertices onto frustum normals
249 for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
250 {
251 Standard_Real aMax = -DBL_MAX;
252 Standard_Real aMin = DBL_MAX;
253 const gp_XYZ& aPlane = theFrustum->myPlanes[aPlaneIdx].XYZ();
254 for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
255 {
256 Standard_Real aProjection = aPlane.Dot (theFrustum->myVertices[aVertIdx].XYZ());
257 aMin = Min (aMin, aProjection);
258 aMax = Max (aMax, aProjection);
259 }
260 theFrustum->myMinVertsProjections[aPlaneIdx] = aMin;
261 theFrustum->myMaxVertsProjections[aPlaneIdx] = aMax;
262 }
263 }
f751596e 264
3bf9a45f 265 // project vertices onto {i, j, k}
f751596e 266 for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
267 {
268 Standard_Real aMax = -DBL_MAX;
3bf9a45f 269 Standard_Real aMin = DBL_MAX;
f751596e 270 for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
271 {
3bf9a45f 272 const gp_XYZ& aVert = theFrustum->myVertices[aVertIdx].XYZ();
273 aMax = Max (aVert.GetData()[aDim], aMax);
274 aMin = Min (aVert.GetData()[aDim], aMin);
f751596e 275 }
3bf9a45f 276 theFrustum->myMaxOrthoVertsProjections[aDim] = aMax;
277 theFrustum->myMinOrthoVertsProjections[aDim] = aMin;
f751596e 278 }
3bf9a45f 279}
280
281// =======================================================================
e1eb39d2 282// function : Init
283// purpose :
3bf9a45f 284// =======================================================================
e1eb39d2 285void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d &thePoint)
3bf9a45f 286{
e1eb39d2 287 mySelectionType = SelectMgr_SelectionType_Point;
288 mySelRectangle.SetMousePos (thePoint);
289}
7479f643 290
e1eb39d2 291// =======================================================================
292// function : Init
293// purpose :
294// =======================================================================
295void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d& theMinPnt,
296 const gp_Pnt2d& theMaxPnt)
297{
298 mySelectionType = SelectMgr_SelectionType_Box;
299 mySelRectangle.SetMinPnt (theMinPnt);
300 mySelRectangle.SetMaxPnt (theMaxPnt);
f751596e 301}
302
303// =======================================================================
304// function : Build
e1eb39d2 305// purpose :
f751596e 306// =======================================================================
e1eb39d2 307void SelectMgr_RectangularFrustum::Build()
f751596e 308{
e1eb39d2 309 Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
310 "Error! SelectMgr_RectangularFrustum::Build() should be called after selection frustum initialization");
311 gp_Pnt2d aMinPnt, aMaxPnt;
312 if (mySelectionType == SelectMgr_SelectionType_Point)
313 {
314 const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
315 myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 0.0);
316 myFarPickedPnt = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 1.0);
317
318 aMinPnt.SetCoord (aMousePos.X() - myPixelTolerance * 0.5,
319 aMousePos.Y() - myPixelTolerance * 0.5);
320 aMaxPnt.SetCoord (aMousePos.X() + myPixelTolerance * 0.5,
321 aMousePos.Y() + myPixelTolerance * 0.5);
322 }
323 else
324 {
325 aMinPnt = mySelRectangle.MinPnt();
326 aMaxPnt = mySelRectangle.MaxPnt();
327 myNearPickedPnt = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
328 (aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
329 0.0);
330 myFarPickedPnt = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
331 (aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
332 1.0);
333 }
334
3bf9a45f 335 myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ();
f751596e 336
3bf9a45f 337 // calculate base frustum characteristics: vertices and edge directions
e1eb39d2 338 computeFrustum (aMinPnt, aMaxPnt, myBuilder, myVertices, myEdgeDirs);
f751596e 339
3bf9a45f 340 // compute frustum normals
341 computeNormals (myEdgeDirs, myPlanes);
f751596e 342
3bf9a45f 343 // compute vertices projections onto frustum normals and
344 // {i, j, k} vectors and store them to corresponding class fields
345 cacheVertexProjections (this);
7479f643 346
347 myScale = 1.0;
f751596e 348}
349
350// =======================================================================
3bf9a45f 351// function : ScaleAndTransform
352// purpose : IMPORTANT: Scaling makes sense only for frustum built on a single point!
353// Note that this method does not perform any checks on type of the frustum.
354// Returns a copy of the frustum resized according to the scale factor given
355// and transforms it using the matrix given.
356// There are no default parameters, but in case if:
357// - transformation only is needed: @theScaleFactor must be initialized
358// as any negative value;
359// - scale only is needed: @theTrsf must be set to gp_Identity.
f751596e 360// =======================================================================
e1eb39d2 361Handle(SelectMgr_BaseIntersector) SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
362 const gp_GTrsf& theTrsf,
363 const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
f751596e 364{
e1eb39d2 365 Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
366 "Error! SelectMgr_RectangularFrustum::ScaleAndTransform() should be called after selection frustum initialization");
367
0038de11 368 Standard_ASSERT_RAISE (theScaleFactor >= 0,
369 "Error! Pixel tolerance for selection should not be negative");
3bf9a45f 370
099f3513 371 Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
3bf9a45f 372 const Standard_Boolean isToScale = theScaleFactor != 1;
373 const Standard_Boolean isToTrsf = theTrsf.Form() != gp_Identity;
f751596e 374
3bf9a45f 375 if (!isToScale && !isToTrsf)
e1eb39d2 376 {
377 aRes->SetBuilder (theBuilder);
3bf9a45f 378 return aRes;
e1eb39d2 379 }
f751596e 380
51d4a4f9 381 aRes->SetCamera (myCamera);
099f3513 382 const SelectMgr_RectangularFrustum* aRef = this;
f751596e 383
3bf9a45f 384 if (isToScale)
28ee613b 385 {
3bf9a45f 386 aRes->myNearPickedPnt = myNearPickedPnt;
387 aRes->myFarPickedPnt = myFarPickedPnt;
388 aRes->myViewRayDir = myViewRayDir;
28ee613b 389
e1eb39d2 390 const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
391 const gp_Pnt2d aMinPnt (aMousePos.X() - theScaleFactor * 0.5,
392 aMousePos.Y() - theScaleFactor * 0.5);
393 const gp_Pnt2d aMaxPnt (aMousePos.X() + theScaleFactor * 0.5,
394 aMousePos.Y() + theScaleFactor * 0.5);
28ee613b 395
3bf9a45f 396 // recompute base frustum characteristics from scratch
397 computeFrustum (aMinPnt, aMaxPnt, myBuilder, aRes->myVertices, aRes->myEdgeDirs);
f751596e 398
099f3513 399 aRef = aRes.get();
f751596e 400 }
401
3bf9a45f 402 if (isToTrsf)
f751596e 403 {
91d96372 404 const Standard_Real aRefScale = aRef->myFarPickedPnt.SquareDistance (aRef->myNearPickedPnt);
405
406 gp_Pnt aPoint = aRef->myNearPickedPnt;
407 theTrsf.Transforms (aPoint.ChangeCoord());
408 aRes->myNearPickedPnt = aPoint;
409
410 aPoint.SetXYZ (aRef->myFarPickedPnt.XYZ());
411 theTrsf.Transforms (aPoint.ChangeCoord());
412 aRes->myFarPickedPnt = aPoint;
413
2ff1d580 414 aRes->myViewRayDir = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
3bf9a45f 415
91d96372 416 for (Standard_Integer anIt = 0; anIt < 8; anIt++)
417 {
418 aPoint = aRef->myVertices[anIt];
419 theTrsf.Transforms (aPoint.ChangeCoord());
420 aRes->myVertices[anIt] = aPoint;
421 }
3bf9a45f 422
423 // Horizontal
424 aRes->myEdgeDirs[0] = aRes->myVertices[4].XYZ() - aRes->myVertices[0].XYZ();
425 // Vertical
426 aRes->myEdgeDirs[1] = aRes->myVertices[2].XYZ() - aRes->myVertices[0].XYZ();
427 // LeftLower
428 aRes->myEdgeDirs[2] = aRes->myVertices[2].XYZ() - aRes->myVertices[3].XYZ();
429 // RightLower
430 aRes->myEdgeDirs[3] = aRes->myVertices[6].XYZ() - aRes->myVertices[7].XYZ();
431 // LeftUpper
432 aRes->myEdgeDirs[4] = aRes->myVertices[0].XYZ() - aRes->myVertices[1].XYZ();
433 // RightUpper
434 aRes->myEdgeDirs[5] = aRes->myVertices[4].XYZ() - aRes->myVertices[5].XYZ();
7479f643 435
91d96372 436 // Compute scale to transform depth from local coordinate system to world coordinate system
437 aRes->myScale = Sqrt (aRefScale / aRes->myFarPickedPnt.SquareDistance (aRes->myNearPickedPnt));
f751596e 438 }
439
51d4a4f9 440 aRes->SetBuilder (theBuilder);
441
3bf9a45f 442 // compute frustum normals
443 computeNormals (aRes->myEdgeDirs, aRes->myPlanes);
444
099f3513 445 cacheVertexProjections (aRes.get());
f751596e 446
e1eb39d2 447 aRes->mySelectionType = mySelectionType;
448 aRes->mySelRectangle = mySelRectangle;
099f3513 449 return aRes;
f751596e 450}
451
e1eb39d2 452// =======================================================================
453// function : IsScalable
454// purpose :
455// =======================================================================
456Standard_Boolean SelectMgr_RectangularFrustum::IsScalable() const
457{
458 return mySelectionType == SelectMgr_SelectionType_Point;
459}
460
f751596e 461// =======================================================================
03c9cc86 462// function : OverlapsBox
f751596e 463// purpose : Returns true if selecting volume is overlapped by
464// axis-aligned bounding box with minimum corner at point
465// theMinPnt and maximum at point theMaxPnt
466// =======================================================================
03c9cc86 467Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
468 const SelectMgr_Vec3& theBoxMax,
469 Standard_Boolean* theInside) const
f751596e 470{
e1eb39d2 471 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
472 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
473
03c9cc86 474 return hasBoxOverlap (theBoxMin, theBoxMax, theInside);
f751596e 475}
476
477// =======================================================================
03c9cc86 478// function : OverlapsBox
f751596e 479// purpose : SAT intersection test between defined volume and
480// given axis-aligned box
481// =======================================================================
03c9cc86 482Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
483 const SelectMgr_Vec3& theBoxMax,
484 const SelectMgr_ViewClipRange& theClipRange,
485 SelectBasics_PickResult& thePickResult) const
f751596e 486{
e1eb39d2 487 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
488 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
489
03c9cc86 490 if (!hasBoxOverlap (theBoxMin, theBoxMax))
f751596e 491 return Standard_False;
492
d31fb73a 493 Standard_Real aDepth = 0.0;
e1eb39d2 494 BVH_Ray<Standard_Real, 3> aRay(SelectMgr_Vec3(myNearPickedPnt.X(), myNearPickedPnt.Y(), myNearPickedPnt.Z()),
495 SelectMgr_Vec3(myViewRayDir.X(), myViewRayDir.Y(), myViewRayDir.Z()));
496 Standard_Real aTimeEnter, aTimeLeave;
497 if (!BVH_Tools<Standard_Real, 3>::RayBoxIntersection (aRay, theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
d31fb73a 498 {
499 gp_Pnt aNearestPnt (RealLast(), RealLast(), RealLast());
500 aNearestPnt.SetX (Max (Min (myNearPickedPnt.X(), theBoxMax.x()), theBoxMin.x()));
501 aNearestPnt.SetY (Max (Min (myNearPickedPnt.Y(), theBoxMax.y()), theBoxMin.y()));
502 aNearestPnt.SetZ (Max (Min (myNearPickedPnt.Z(), theBoxMax.z()), theBoxMin.z()));
503
504 aDepth = aNearestPnt.Distance (myNearPickedPnt);
505 thePickResult.SetDepth (aDepth);
d7fa57a7 506 return !theClipRange.IsClipped (thePickResult.Depth());
d31fb73a 507 }
508
e1eb39d2 509 Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
510 aRange.GetMin (aDepth);
511
d7fa57a7 512 if (!theClipRange.GetNearestDepth (aRange, aDepth))
d31fb73a 513 {
514 return Standard_False;
515 }
f751596e 516
d31fb73a 517 thePickResult.SetDepth (aDepth);
f751596e 518
d31fb73a 519 return Standard_True;
f751596e 520}
521
522// =======================================================================
03c9cc86 523// function : OverlapsPoint
f751596e 524// purpose : Intersection test between defined volume and given point
525// =======================================================================
03c9cc86 526Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt,
527 const SelectMgr_ViewClipRange& theClipRange,
528 SelectBasics_PickResult& thePickResult) const
f751596e 529{
e1eb39d2 530 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
531 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
532
03c9cc86 533 if (!hasPointOverlap (thePnt))
f751596e 534 return Standard_False;
535
3bf9a45f 536 gp_XYZ aV = thePnt.XYZ() - myNearPickedPnt.XYZ();
2ff1d580 537 const Standard_Real aDepth = aV.Dot (myViewRayDir.XYZ());
f751596e 538
2ff1d580 539 thePickResult.SetDepth (Abs (aDepth) * myScale);
17017555 540 thePickResult.SetPickedPoint (thePnt);
f751596e 541
d7fa57a7 542 return !theClipRange.IsClipped (thePickResult.Depth());
f751596e 543}
544
3bf9a45f 545// =======================================================================
03c9cc86 546// function : OverlapsPoint
3bf9a45f 547// purpose : Intersection test between defined volume and given point
548// =======================================================================
03c9cc86 549Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt) const
3bf9a45f 550{
e1eb39d2 551 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
552 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
553
03c9cc86 554 return hasPointOverlap (thePnt);
3bf9a45f 555}
556
f751596e 557// =======================================================================
03c9cc86 558// function : OverlapsSegment
f751596e 559// purpose : Checks if line segment overlaps selecting frustum
560// =======================================================================
03c9cc86 561Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSegment (const gp_Pnt& thePnt1,
562 const gp_Pnt& thePnt2,
563 const SelectMgr_ViewClipRange& theClipRange,
564 SelectBasics_PickResult& thePickResult) const
f751596e 565{
e1eb39d2 566 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
567 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
568
03c9cc86 569 if (!hasSegmentOverlap (thePnt1, thePnt2))
f751596e 570 return Standard_False;
571
17017555 572 segmentSegmentDistance (thePnt1, thePnt2, thePickResult);
e9312c0f 573
d7fa57a7 574 return !theClipRange.IsClipped (thePickResult.Depth());
f751596e 575}
576
577// =======================================================================
03c9cc86 578// function : OverlapsPolygon
f751596e 579// purpose : SAT intersection test between defined volume and given
580// ordered set of points, representing line segments. The test
581// may be considered of interior part or boundary line defined
582// by segments depending on given sensitivity type
583// =======================================================================
03c9cc86 584Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPolygon (const TColgp_Array1OfPnt& theArrayOfPnts,
585 Select3D_TypeOfSensitivity theSensType,
586 const SelectMgr_ViewClipRange& theClipRange,
587 SelectBasics_PickResult& thePickResult) const
f751596e 588{
e1eb39d2 589 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
590 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
591
f751596e 592 if (theSensType == Select3D_TOS_BOUNDARY)
593 {
594 Standard_Integer aMatchingSegmentsNb = -1;
17017555 595 SelectBasics_PickResult aPickResult;
596 thePickResult.Invalidate();
114b7bf1 597 const Standard_Integer aLower = theArrayOfPnts.Lower();
598 const Standard_Integer anUpper = theArrayOfPnts.Upper();
f751596e 599 for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
600 {
114b7bf1 601 const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
602 const gp_Pnt& aEndPnt = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
03c9cc86 603 if (hasSegmentOverlap (aStartPnt, aEndPnt))
f751596e 604 {
605 aMatchingSegmentsNb++;
17017555 606 segmentSegmentDistance (aStartPnt, aEndPnt, aPickResult);
607 thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
f751596e 608 }
609 }
610
611 if (aMatchingSegmentsNb == -1)
612 return Standard_False;
613 }
614 else if (theSensType == Select3D_TOS_INTERIOR)
615 {
3bf9a45f 616 gp_Vec aPolyNorm (gp_XYZ (RealLast(), RealLast(), RealLast()));
03c9cc86 617 if (!hasPolygonOverlap (theArrayOfPnts, aPolyNorm))
1ccc1371 618 {
619 return Standard_False;
620 }
621
622 if (aPolyNorm.Magnitude() <= Precision::Confusion())
623 {
624 // treat degenerated polygon as point
03c9cc86 625 return OverlapsPoint (theArrayOfPnts.First(), theClipRange, thePickResult);
1ccc1371 626 }
627 else if (!segmentPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
17017555 628 {
f751596e 629 return Standard_False;
17017555 630 }
f751596e 631 }
632
d7fa57a7 633 return !theClipRange.IsClipped (thePickResult.Depth());
f751596e 634}
635
636// =======================================================================
03c9cc86 637// function : OverlapsTriangle
f751596e 638// purpose : SAT intersection test between defined volume and given
639// triangle. The test may be considered of interior part or
640// boundary line defined by triangle vertices depending on
641// given sensitivity type
642// =======================================================================
03c9cc86 643Standard_Boolean SelectMgr_RectangularFrustum::OverlapsTriangle (const gp_Pnt& thePnt1,
644 const gp_Pnt& thePnt2,
645 const gp_Pnt& thePnt3,
646 Select3D_TypeOfSensitivity theSensType,
647 const SelectMgr_ViewClipRange& theClipRange,
648 SelectBasics_PickResult& thePickResult) const
f751596e 649{
e1eb39d2 650 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
651 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
652
f751596e 653 if (theSensType == Select3D_TOS_BOUNDARY)
654 {
114b7bf1 655 const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
656 const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
03c9cc86 657 return OverlapsPolygon (aPntsArray, Select3D_TOS_BOUNDARY, theClipRange, thePickResult);
f751596e 658 }
659 else if (theSensType == Select3D_TOS_INTERIOR)
660 {
3bf9a45f 661 gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
03c9cc86 662 if (!hasTriangleOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
2ff1d580 663 {
f751596e 664 return Standard_False;
2ff1d580 665 }
f751596e 666
2ff1d580 667 const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
668 thePnt3.XYZ() - thePnt2.XYZ(),
669 thePnt1.XYZ() - thePnt3.XYZ() };
670 if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
671 {
672 // consider degenerated triangle as point or segment
673 return aTrEdges[0].SquareModulus() > gp::Resolution()
03c9cc86 674 ? OverlapsSegment (thePnt1, thePnt2, theClipRange, thePickResult)
2ff1d580 675 : (aTrEdges[1].SquareModulus() > gp::Resolution()
03c9cc86 676 ? OverlapsSegment (thePnt2, thePnt3, theClipRange, thePickResult)
677 : OverlapsPoint (thePnt1, theClipRange, thePickResult));
2ff1d580 678 }
7e17e8f0 679
2ff1d580 680 const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
681 const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myViewRayDir.XYZ());
7e17e8f0 682 if (Abs (anAlpha) < gp::Resolution())
683 {
2ff1d580 684 // handle the case when triangle normal and selecting frustum direction are orthogonal
685 SelectBasics_PickResult aPickResult;
686 thePickResult.Invalidate();
687 for (Standard_Integer anEdgeIter = 0; anEdgeIter < 3; ++anEdgeIter)
7e17e8f0 688 {
2ff1d580 689 const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
690 const gp_Pnt& anEndPnt = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
691 segmentSegmentDistance (aStartPnt, anEndPnt, aPickResult);
692 thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
7e17e8f0 693 }
cece953c 694 thePickResult.SetSurfaceNormal (aTriangleNormal);
d7fa57a7 695 return !theClipRange.IsClipped (thePickResult.Depth());
7e17e8f0 696 }
697
2ff1d580 698 // check if intersection point belongs to triangle's interior part
699 const gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
f751596e 700
2ff1d580 701 const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
702 const gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
703 const Standard_Real anU = aVec.Dot (aTrEdges[2]);
704 const Standard_Real aV = aVec.Dot (aTrEdges[0]);
f751596e 705
17017555 706 const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
707 const gp_Pnt aPtOnPlane = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
f751596e 708 if (isInterior)
709 {
17017555 710 thePickResult.SetDepth (myNearPickedPnt.Distance (aPtOnPlane) * myScale);
711 thePickResult.SetPickedPoint (aPtOnPlane);
2615c2d7 712 thePickResult.SetSurfaceNormal (aTriangleNormal);
d7fa57a7 713 return !theClipRange.IsClipped (thePickResult.Depth());
f751596e 714 }
715
f751596e 716 Standard_Real aMinDist = RealLast();
2ff1d580 717 Standard_Integer aNearestEdgeIdx1 = -1;
f751596e 718 for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
719 {
3bf9a45f 720 gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
721 Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
722 Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
2ff1d580 723 if (aDist < aMinDist)
f751596e 724 {
725 aMinDist = aDist;
2ff1d580 726 aNearestEdgeIdx1 = anEdgeIdx;
f751596e 727 }
728 }
2ff1d580 729 Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
6eeb528c 730 const gp_Vec aVec12 (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]);
731 if (aVec12.SquareMagnitude() > gp::Resolution()
732 && myViewRayDir.IsParallel (aVec12, Precision::Angular()))
2ff1d580 733 {
734 aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
735 }
736 segmentSegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult);
cece953c 737 thePickResult.SetSurfaceNormal (aTriangleNormal);
f751596e 738 }
739
d7fa57a7 740 return !theClipRange.IsClipped (thePickResult.Depth());
f751596e 741}
742
e1eb39d2 743// =======================================================================
744// function : GetMousePosition
745// purpose :
746// =======================================================================
747const gp_Pnt2d& SelectMgr_RectangularFrustum::GetMousePosition() const
748{
749 if (mySelectionType == SelectMgr_SelectionType_Point)
750 {
751 return mySelRectangle.MousePos();
752 }
753 return base_type::GetMousePosition();
503374ad 754}
755
756// =======================================================================
757// function : OverlapsSphere
758// purpose :
759// =======================================================================
760Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& theCenter,
761 const Standard_Real theRadius,
762 const SelectMgr_ViewClipRange& theClipRange,
763 SelectBasics_PickResult& thePickResult) const
764{
765 Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
766 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
503374ad 767 Standard_Real aTimeEnter = 0.0, aTimeLeave = 0.0;
768 if (!RaySphereIntersection (theCenter, theRadius, myNearPickedPnt, myViewRayDir, aTimeEnter, aTimeLeave))
769 {
770 return Standard_False;
771 }
772
773 thePickResult.SetDepth (aTimeEnter * myScale);
774 if (theClipRange.IsClipped (thePickResult.Depth()))
775 {
776 thePickResult.SetDepth (aTimeLeave * myScale);
777 }
a3b2aaef 778 gp_Pnt aPntOnSphere (myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * thePickResult.Depth() / myScale);
503374ad 779 gp_Vec aNormal (aPntOnSphere.XYZ() - theCenter.XYZ());
780 thePickResult.SetPickedPoint (aPntOnSphere);
781 thePickResult.SetSurfaceNormal (aNormal);
782 return !theClipRange.IsClipped (thePickResult.Depth());
783}
784
785// =======================================================================
786// function : OverlapsSphere
787// purpose :
788// =======================================================================
789Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& theCenter,
790 const Standard_Real theRadius,
791 Standard_Boolean* theInside) const
792{
793 Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
794 "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
795 return hasSphereOverlap (theCenter, theRadius, theInside);
e1eb39d2 796}
797
f751596e 798// =======================================================================
799// function : DistToGeometryCenter
800// purpose : Measures distance between 3d projection of user-picked
801// screen point and given point theCOG
802// =======================================================================
4a056d20 803Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG) const
f751596e 804{
e1eb39d2 805 Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
806 "Error! SelectMgr_RectangularFrustum::DistToGeometryCenter() should be called after selection frustum initialization");
807
7479f643 808 return theCOG.Distance (myNearPickedPnt) * myScale;
f751596e 809}
810
811// =======================================================================
812// function : DetectedPoint
813// purpose : Calculates the point on a view ray that was detected during
814// the run of selection algo by given depth
815// =======================================================================
3bf9a45f 816gp_Pnt SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
f751596e 817{
e1eb39d2 818 Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point,
819 "SelectMgr_RectangularFrustum::DetectedPoint() should be called only for Point selection type");
2ff1d580 820 return myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * theDepth / myScale;
f751596e 821}
822
871dcdc2 823// =======================================================================
824// function : GetPlanes
825// purpose :
826// =======================================================================
827void SelectMgr_RectangularFrustum::GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const
828{
829 thePlaneEquations.Clear();
830
831 SelectMgr_Vec4 anEquation;
832 for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
833 {
51d4a4f9 834 const gp_Vec& aPlaneNorm = Camera()->IsOrthographic() && aPlaneIdx % 2 == 1 ?
871dcdc2 835 myPlanes[aPlaneIdx - 1].Reversed() : myPlanes[aPlaneIdx];
836 anEquation.x() = aPlaneNorm.X();
837 anEquation.y() = aPlaneNorm.Y();
838 anEquation.z() = aPlaneNorm.Z();
839 anEquation.w() = - (aPlaneNorm.XYZ().Dot (myVertices[aPlaneIdx % 2 == 0 ? aPlaneIdx : aPlaneIdx + 2].XYZ()));
840 thePlaneEquations.Append (anEquation);
841 }
842}
a5162275 843
844//=======================================================================
845//function : DumpJson
846//purpose :
847//=======================================================================
848void SelectMgr_RectangularFrustum::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
849{
850 OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_RectangularFrustum)
851 OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_Frustum)
852
853 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myNearPickedPnt)
854 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myFarPickedPnt)
855 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myViewRayDir)
e1eb39d2 856 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MinPnt())
857 OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MaxPnt())
a5162275 858
859 OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myScale)
860}