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 | // ======================================================================= |
25 | void 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 |
81 | void 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 |
115 | namespace |
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 |
198 | void 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 | // ======================================================================= |
257 | void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d &thePoint) |
258 | { |
259 | myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 0.0); |
260 | myFarPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 1.0); |
261 | myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); |
262 | myMousePos = thePoint; |
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 | |
e9312c0f |
279 | myViewClipRange.Clear(); |
280 | |
7479f643 |
281 | myScale = 1.0; |
f751596e |
282 | } |
283 | |
284 | // ======================================================================= |
285 | // function : Build |
286 | // purpose : Build volume according to the selected rectangle |
287 | // ======================================================================= |
288 | void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d& theMinPnt, |
289 | const gp_Pnt2d& theMaxPnt) |
290 | { |
291 | myNearPickedPnt = myBuilder->ProjectPntOnViewPlane ((theMinPnt.X() + theMaxPnt.X()) * 0.5, |
292 | (theMinPnt.Y() + theMaxPnt.Y()) * 0.5, |
293 | 0.0); |
294 | myFarPickedPnt = myBuilder->ProjectPntOnViewPlane ((theMinPnt.X() + theMaxPnt.X()) * 0.5, |
295 | (theMinPnt.Y() + theMaxPnt.Y()) * 0.5, |
296 | 1.0); |
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 | |
e9312c0f |
309 | myViewClipRange.Clear(); |
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 |
325 | Handle(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 |
414 | Standard_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 |
426 | Standard_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 |
447 | Standard_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 | // ======================================================================= |
466 | Standard_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 |
475 | Standard_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 |
495 | Standard_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 |
542 | Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1, |
543 | const gp_Pnt& thePnt2, |
544 | const gp_Pnt& thePnt3, |
545 | Select3D_TypeOfSensitivity theSensType, |
546 | Standard_Real& theDepth) |
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 |
628 | Standard_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 |
638 | gp_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 |
647 | void SelectMgr_RectangularFrustum::computeClippingRange (const Graphic3d_SequenceOfHClipPlane& thePlanes, |
648 | Standard_Real& theDepthMin, |
649 | Standard_Real& theDepthMax) |
f751596e |
650 | { |
e9312c0f |
651 | theDepthMax = DBL_MAX; |
652 | theDepthMin = -DBL_MAX; |
f751596e |
653 | Standard_Real aPlaneA, aPlaneB, aPlaneC, aPlaneD; |
e9312c0f |
654 | for (Graphic3d_SequenceOfHClipPlane::Iterator aPlaneIt (thePlanes); aPlaneIt.More(); aPlaneIt.Next()) |
f751596e |
655 | { |
656 | const Handle(Graphic3d_ClipPlane)& aClipPlane = aPlaneIt.Value(); |
657 | if (!aClipPlane->IsOn()) |
658 | continue; |
659 | |
660 | gp_Pln aGeomPlane = aClipPlane->ToPlane(); |
661 | |
662 | aGeomPlane.Coefficients (aPlaneA, aPlaneB, aPlaneC, aPlaneD); |
663 | |
664 | const gp_XYZ& aPlaneDirXYZ = aGeomPlane.Axis().Direction().XYZ(); |
665 | |
e9312c0f |
666 | Standard_Real aDotProduct = myViewRayDir.XYZ ().Dot (aPlaneDirXYZ); |
667 | Standard_Real aDistance = - myNearPickedPnt.XYZ ().Dot (aPlaneDirXYZ) |
668 | - aPlaneD; |
f751596e |
669 | |
670 | // check whether the pick line is parallel to clip plane |
671 | if (Abs (aDotProduct) < Precision::Angular()) |
672 | { |
673 | // line lies below the plane and is not clipped, skip |
674 | continue; |
675 | } |
676 | |
677 | // compute distance to point of pick line intersection with the plane |
678 | Standard_Real aParam = aDistance / aDotProduct; |
679 | |
680 | // check if ray intersects the plane, in case aIntDist < 0 |
681 | // the plane is "behind" the ray |
682 | if (aParam < 0.0) |
683 | { |
684 | continue; |
685 | } |
686 | |
3bf9a45f |
687 | const gp_Pnt anIntersectionPt = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aParam; |
688 | const Standard_Real aDistToPln = anIntersectionPt.Distance (myNearPickedPnt); |
f751596e |
689 | |
690 | // change depth limits for case of opposite and directed planes |
691 | if (aDotProduct < 0.0) |
692 | { |
e9312c0f |
693 | theDepthMax = Min (aDistToPln, theDepthMax); |
f751596e |
694 | } |
e9312c0f |
695 | else if (aDistToPln > theDepthMin) |
f751596e |
696 | { |
e9312c0f |
697 | theDepthMin = Max (aDistToPln, theDepthMin); |
f751596e |
698 | } |
699 | } |
e9312c0f |
700 | } |
701 | |
702 | // ======================================================================= |
703 | // function : IsClipped |
704 | // purpose : Checks if the point of sensitive in which selection was |
705 | // detected belongs to the region defined by clipping planes |
706 | // ======================================================================= |
707 | Standard_Boolean SelectMgr_RectangularFrustum::IsClipped (const Graphic3d_SequenceOfHClipPlane& thePlanes, |
708 | const Standard_Real theDepth) |
709 | { |
710 | Standard_Real aMaxDepth, aMinDepth; |
711 | computeClippingRange (thePlanes, aMinDepth, aMaxDepth); |
f751596e |
712 | |
713 | return (theDepth <= aMinDepth || theDepth >= aMaxDepth); |
714 | } |
e9312c0f |
715 | |
716 | // ======================================================================= |
717 | // function : SetViewClipping |
718 | // purpose : |
719 | // ======================================================================= |
3202bf1e |
720 | void SelectMgr_RectangularFrustum::SetViewClipping (const Handle(Graphic3d_SequenceOfHClipPlane)& thePlanes) |
e9312c0f |
721 | { |
3202bf1e |
722 | if (thePlanes.IsNull() |
723 | || thePlanes->IsEmpty()) |
e9312c0f |
724 | { |
725 | myViewClipRange.Clear(); |
726 | return; |
727 | } |
728 | |
729 | Standard_Real aMaxDepth, aMinDepth; |
3202bf1e |
730 | computeClippingRange (*thePlanes, aMinDepth, aMaxDepth); |
e9312c0f |
731 | myViewClipRange.Set (aMinDepth, aMaxDepth); |
732 | } |
733 | |
734 | // ======================================================================= |
735 | // function : isViewClippingOk |
736 | // purpose : |
737 | // ======================================================================= |
738 | Standard_Boolean SelectMgr_RectangularFrustum::isViewClippingOk (const Standard_Real theDepth) const |
739 | { |
3202bf1e |
740 | if (!myViewClipRange.IsValid() |
741 | || !myIsViewClipEnabled) |
742 | { |
e9312c0f |
743 | return Standard_True; |
3202bf1e |
744 | } |
e9312c0f |
745 | |
746 | return myViewClipRange.MaxDepth() > theDepth |
747 | && myViewClipRange.MinDepth() < theDepth; |
748 | } |
871dcdc2 |
749 | |
750 | // ======================================================================= |
751 | // function : GetPlanes |
752 | // purpose : |
753 | // ======================================================================= |
754 | void SelectMgr_RectangularFrustum::GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const |
755 | { |
756 | thePlaneEquations.Clear(); |
757 | |
758 | SelectMgr_Vec4 anEquation; |
759 | for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx) |
760 | { |
761 | const gp_Vec& aPlaneNorm = myIsOrthographic && aPlaneIdx % 2 == 1 ? |
762 | myPlanes[aPlaneIdx - 1].Reversed() : myPlanes[aPlaneIdx]; |
763 | anEquation.x() = aPlaneNorm.X(); |
764 | anEquation.y() = aPlaneNorm.Y(); |
765 | anEquation.z() = aPlaneNorm.Z(); |
766 | anEquation.w() = - (aPlaneNorm.XYZ().Dot (myVertices[aPlaneIdx % 2 == 0 ? aPlaneIdx : aPlaneIdx + 2].XYZ())); |
767 | thePlaneEquations.Append (anEquation); |
768 | } |
769 | } |