0024623: Visualization - improve selection mechanism
[occt.git] / src / SelectMgr / SelectMgr_FrustumBuilder.cxx
1 // Created on: 2014-11-24
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 <SelectMgr_FrustumBuilder.hxx>
17
18 #define DOT(A, B) (A.x() * B.x() + A.y() * B.y() + A.z() * B.z())
19 #define LENGTH(A) (std::sqrt (A.x() * A.x() + A.y() * A.y() + A.z() * A.z()))
20
21 //=======================================================================
22 // function : SelectMgr_FrustumBuilder
23 // purpose  : Creates new frustum builder with empty matrices
24 //=======================================================================
25 SelectMgr_FrustumBuilder::SelectMgr_FrustumBuilder()
26 : myOrientation(),
27   myProjection(),
28   myWidth (INT_MAX),
29   myHeight (INT_MAX),
30   myIsViewportSet (Standard_False) {}
31
32 //=======================================================================
33 // function : SetOrientation
34 // purpose  : Stores current orientation matrix
35 //=======================================================================
36 void SelectMgr_FrustumBuilder::SetOrientation (const Graphic3d_Mat4d& theOrientation)
37 {
38   myOrientation = theOrientation;
39 }
40
41 //=======================================================================
42 // function : SetProjection
43 // purpose  : Stores current projection matrix
44 //=======================================================================
45 void SelectMgr_FrustumBuilder::SetProjection (const Graphic3d_Mat4d& theProjection)
46 {
47   myProjection = theProjection;
48 }
49
50 //=======================================================================
51 // function : SetWindowSize
52 // purpose  : Stores current window width and height
53 //=======================================================================
54 void SelectMgr_FrustumBuilder::SetWindowSize (const Standard_Integer theWidth,
55                                               const Standard_Integer theHeight)
56 {
57   myWidth = theWidth;
58   myHeight = theHeight;
59 }
60
61 //=======================================================================
62 // function : SetViewport
63 // purpose  : Stores current viewport coordinates
64 //=======================================================================
65 void SelectMgr_FrustumBuilder::SetViewport (const Standard_Real theX,
66                                             const Standard_Real theY,
67                                             const Standard_Real theWidth,
68                                             const Standard_Real theHeight)
69 {
70   myViewport = NCollection_Vec4<Standard_Real> (theX, theY, theWidth, theHeight);
71   myIsViewportSet = Standard_True;
72 }
73
74 //=======================================================================
75 // function : InvalidateViewport
76 // purpose  :
77 //=======================================================================
78 void SelectMgr_FrustumBuilder::InvalidateViewport()
79 {
80   myIsViewportSet = Standard_False;
81 }
82
83 //=======================================================================
84 // function : SignedPlanePntDist
85 // purpose  : Calculates signed distance between plane with equation
86 //            theEq and point thePnt
87 //=======================================================================
88 Standard_Real SelectMgr_FrustumBuilder::SignedPlanePntDist (const SelectMgr_Vec3& theEq,
89                                                             const SelectMgr_Vec3& thePnt) const
90 {
91   const Standard_Real aNormLength = LENGTH (theEq);
92   const Standard_Real anInvNormLength = aNormLength < Precision::Confusion() ? 0.0 : 1.0 / aNormLength;
93   const Standard_Real anA = theEq.x() * anInvNormLength;
94   const Standard_Real aB  = theEq.y() * anInvNormLength;
95   const Standard_Real aC  = theEq.z() * anInvNormLength;
96   return anA * thePnt.x() + aB * thePnt.y() + aC * thePnt.z();
97 }
98
99 // =======================================================================
100 // function : PlaneEquation
101 // purpose  : Creates plane equation from 3 points: thePntA, thePntB and
102 //            thePntC containing point theInnerPnt
103 // =======================================================================
104 SelectMgr_Vec3 SelectMgr_FrustumBuilder::PlaneEquation (const SelectMgr_Vec3& thePntA,
105                                                         const SelectMgr_Vec3& thePntB,
106                                                         const SelectMgr_Vec3& thePntC,
107                                                         const SelectMgr_Vec3& theInnerPnt) const
108 {
109   NCollection_Vec4<Standard_Real> aPlaneEquation (0.0);
110
111   const SelectMgr_Vec3& aDirVecAB = thePntB - thePntA;
112   const SelectMgr_Vec3& aDirVecAC = thePntC - thePntA;
113   SelectMgr_Vec3 aPlaneNormal = SelectMgr_Vec3 (aDirVecAB.y() * aDirVecAC.z() - aDirVecAB.z() * aDirVecAC.y(),
114                                                 aDirVecAB.z() * aDirVecAC.x() - aDirVecAB.x() * aDirVecAC.z(),
115                                                 aDirVecAB.x() * aDirVecAC.y() - aDirVecAB.y() * aDirVecAC.x());
116
117   if (SignedPlanePntDist (aPlaneNormal, theInnerPnt) > 0)
118   {
119     aPlaneNormal *= -1.0;
120   }
121
122   return aPlaneNormal;
123 }
124
125 //=======================================================================
126 // function : PlaneEquation
127 // purpose  : Calculates plane equation from 3 points
128 //=======================================================================
129 SelectMgr_Vec3 SelectMgr_FrustumBuilder::PlaneEquation (const SelectMgr_Vec3& thePntA,
130                                                         const SelectMgr_Vec3& thePntB,
131                                                         const SelectMgr_Vec3& thePntC) const
132 {
133   const SelectMgr_Vec3& aVec1 = thePntB - thePntA;
134   const SelectMgr_Vec3& aVec2 = thePntC - thePntA;
135   SelectMgr_Vec3 aPlaneEquation = SelectMgr_Vec3 (aVec1.y() * aVec2.z() - aVec1.z() * aVec2.y(),
136                                                   aVec1.z() * aVec2.x() - aVec1.x() * aVec2.z(),
137                                                   aVec1.x() * aVec2.y() - aVec1.y() * aVec2.x());
138
139   return aPlaneEquation;
140 }
141
142 //=======================================================================
143 // function : safePointCast
144 // purpose  :
145 //=======================================================================
146 static NCollection_Vec4<Standard_Real> safePointCast (const gp_Pnt& thePnt)
147 {
148   Standard_Real aLim = 1e15f;
149
150   // have to deal with values greater then max float
151   gp_Pnt aSafePoint = thePnt;
152   const Standard_Real aBigFloat = aLim * 0.1f;
153   if (Abs (aSafePoint.X()) > aLim)
154     aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
155   if (Abs (aSafePoint.Y()) > aLim)
156     aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
157   if (Abs (aSafePoint.Z()) > aLim)
158     aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
159
160   // convert point
161   NCollection_Vec4<Standard_Real> aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
162
163   return aPnt;
164 }
165
166 //=======================================================================
167 // function : unProject
168 // purpose  : Unprojects point from NDC coords to 3d world space
169 //=======================================================================
170 SelectMgr_Vec3 SelectMgr_FrustumBuilder::unProject (const gp_Pnt& thePnt) const
171 {
172   Graphic3d_Mat4d aInvView;
173   Graphic3d_Mat4d aInvProj;
174
175   // this case should never happen
176   if (!myOrientation.Inverted (aInvView) || !myProjection.Inverted (aInvProj))
177   {
178     return SelectMgr_Vec3 (0.0, 0.0, 0.0);
179   }
180
181   // use compatible type of point
182   NCollection_Vec4<Standard_Real> aPnt = safePointCast (thePnt);
183
184   aPnt = aInvProj * aPnt; // convert to view coordinate space
185   aPnt = aInvView * aPnt; // convert to world coordinate space
186
187   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
188
189   return SelectMgr_Vec3 (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
190 }
191
192 // =======================================================================
193 // function : ProjectPntOnViewPlane
194 // purpose  : Projects 2d screen point onto view frustum plane:
195 //            theZ = 0 - near plane,
196 //            theZ = 1 - far plane
197 // =======================================================================
198 SelectMgr_Vec3 SelectMgr_FrustumBuilder::ProjectPntOnViewPlane (const Standard_Real& theX,
199                                                                 const Standard_Real& theY,
200                                                                 const Standard_Real& theZ) const
201 {
202   Standard_Real aX, anY, aZ;
203
204   // map coords to NDC
205   if (!myIsViewportSet)
206   {
207     aX = 2.0 * theX / myWidth - 1.0;
208     anY = (myHeight - 1 - theY) / myHeight * 2.0 - 1.0;
209     aZ = 2.0 * theZ - 1.0;
210   }
211   else
212   {
213     aX = 2.0 * (theX - myWidth * myViewport.x()) /
214       (myWidth * (myViewport.z() - myViewport.x())) - 1.0;
215     anY = 2.0 * (theY - myHeight * myViewport.y()) /
216       (myHeight * (myViewport.w() - myViewport.y())) - 1.0;
217     aZ = theZ;
218   }
219
220   return unProject (gp_Pnt (aX, anY, aZ));
221 }