1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SoAntiSquish SoAntiSquish.h Inventor/nodes/SoAntiSquish.h
35   \brief The SoAntiSquish class is used to reset to uniform scaling.
36 
37   \ingroup nodes
38 
39   When traversed, this node replaces the scale vector of the matrix
40   with uniform values, based on one of the SoAntiSquish::Sizing
41   strategies.
42 
43   This node is for instance used by manipulators to retain the shape
44   of their dragger geometries when set up within the influence of an
45   SoSurroundScale node (which can yield nonuniform scale-vectors in
46   the current state transformation matrix).
47 
48   <b>FILE FORMAT/DEFAULTS:</b>
49   \code
50     AntiSquish {
51         recalcAlways TRUE
52         sizing AVERAGE_DIMENSION
53     }
54   \endcode
55 
56   \sa SoSurroundScale for a usage example.
57 */
58 
59 #include <Inventor/nodes/SoAntiSquish.h>
60 
61 #include <Inventor/actions/SoGetMatrixAction.h>
62 #include <Inventor/actions/SoGetBoundingBoxAction.h>
63 #include <Inventor/elements/SoModelMatrixElement.h>
64 
65 #include "nodes/SoSubNodeP.h"
66 
67 /*!
68   \enum SoAntiSquish::Sizing
69 
70   Different strategies for "unsquishing" a scale. Values are used by
71   the SoAntiSquish::sizing field.
72 */
73 
74 
75 /*!
76   \var SoSFEnum SoAntiSquish::sizing
77 
78   The current "unsquish" strategy. Default value is
79   SoAntiSquish::AVERAGE_DIMENSION.
80 */
81 /*!
82   \var SoSFBool SoAntiSquish::recalcAlways
83 
84   Whether to automatically have the unsquishing parameters
85   recalculated for every traversal. Default value is \c TRUE.
86 
87   You can set this to \c FALSE and manually invoke
88   SoAntiSquish::recalc() if you need closer control of the geometry
89   influenced by this node.
90 */
91 
92 
93 // *************************************************************************
94 
95 SO_NODE_SOURCE(SoAntiSquish);
96 
97 /*!
98   Constructor.
99 */
SoAntiSquish(void)100 SoAntiSquish::SoAntiSquish(void)
101 {
102   SO_NODE_INTERNAL_CONSTRUCTOR(SoAntiSquish);
103 
104   SO_NODE_ADD_FIELD(recalcAlways, (TRUE));
105   SO_NODE_ADD_FIELD(sizing, (SoAntiSquish::AVERAGE_DIMENSION));
106 
107   SO_NODE_DEFINE_ENUM_VALUE(Sizing, X);
108   SO_NODE_DEFINE_ENUM_VALUE(Sizing, Y);
109   SO_NODE_DEFINE_ENUM_VALUE(Sizing, Z);
110   SO_NODE_DEFINE_ENUM_VALUE(Sizing, AVERAGE_DIMENSION);
111   SO_NODE_DEFINE_ENUM_VALUE(Sizing, BIGGEST_DIMENSION);
112   SO_NODE_DEFINE_ENUM_VALUE(Sizing, SMALLEST_DIMENSION);
113   SO_NODE_DEFINE_ENUM_VALUE(Sizing, LONGEST_DIAGONAL);
114   SO_NODE_SET_SF_ENUM_TYPE(sizing, Sizing);
115 
116   this->matrixvalid = FALSE;
117   this->inversevalid = FALSE;
118 }
119 
120 /*!
121   Destructor.
122 */
~SoAntiSquish()123 SoAntiSquish::~SoAntiSquish()
124 {
125 }
126 
127 // Doc from superclass.
128 void
initClass(void)129 SoAntiSquish::initClass(void)
130 {
131   SO_NODE_INTERNAL_INIT_CLASS(SoAntiSquish, SO_FROM_INVENTOR_1);
132 }
133 
134 // Doc from superclass.
135 void
getBoundingBox(SoGetBoundingBoxAction * action)136 SoAntiSquish::getBoundingBox(SoGetBoundingBoxAction * action)
137 {
138   SbBool matrixwasvalid = this->matrixvalid;
139   SoAntiSquish::doAction(action);
140   if (this->recalcAlways.getValue() == FALSE) {
141     // Usually when recalcAlways is FALSE, SoAntiSquish::recalc() is
142     // called by manipulators at the same time as
143     // SoSurroundScale::invalidate() is called. This means that the
144     // first time we get here is often because SoSurrondScale applied
145     // an SoGetBoundingBoxAction to the scene graph it is going to
146     // calculate the surround scale for. This is _not_ the scene graph
147     // we want to anti-squish so we should recalculate it the next time
148     // we get here.
149     if (matrixwasvalid == FALSE) {
150       this->matrixvalid = FALSE;
151     }
152   }
153 }
154 
155 /*!
156   If SoAntiSquish::recalcAlways has been set to \c FALSE, you must
157   call this method whenever the transformations before this node in
158   the graph has changed.
159 
160   \sa SoAntiSquish::recalcAlways
161 */
162 void
recalc(void)163 SoAntiSquish::recalc(void)
164 {
165   this->matrixvalid = FALSE;
166 }
167 
168 /*!
169   Accumulates an "unsquishing" matrix on top of the current model
170   matrix.
171  */
172 void
doAction(SoAction * action)173 SoAntiSquish::doAction(SoAction * action)
174 {
175   SoState * state = action->getState();
176   if (!this->matrixvalid || this->recalcAlways.getValue()) {
177     this->matrixvalid = TRUE;
178     this->inversevalid = FALSE;
179     this->unsquishedmatrix =
180       this->getUnsquishingMatrix(SoModelMatrixElement::get(state),
181                                  FALSE, this->inversematrix);
182   }
183   SoModelMatrixElement::mult(action->getState(), this, this->unsquishedmatrix);
184 }
185 
186 // Doc from superclass.
187 void
callback(SoCallbackAction * action)188 SoAntiSquish::callback(SoCallbackAction * action)
189 {
190   SoAntiSquish::doAction((SoAction *)action);
191 }
192 
193 // Doc from superclass.
194 void
GLRender(SoGLRenderAction * action)195 SoAntiSquish::GLRender(SoGLRenderAction * action)
196 {
197   SoAntiSquish::doAction((SoAction *) action);
198 }
199 
200 // Doc from superclass.
201 void
getMatrix(SoGetMatrixAction * action)202 SoAntiSquish::getMatrix(SoGetMatrixAction * action)
203 {
204   if (!this->matrixvalid || !this->inversevalid ||
205       this->recalcAlways.getValue()) {
206     this->matrixvalid = TRUE;
207     this->inversevalid = TRUE;
208     this->unsquishedmatrix = this->getUnsquishingMatrix(action->getMatrix(),
209                                                         TRUE,
210                                                         this->inversematrix);
211   }
212 
213   SbMatrix & m = action->getMatrix();
214   SbMatrix & i = action->getInverse();
215   m.multLeft(this->unsquishedmatrix);
216   i.multRight(this->inversematrix);
217 }
218 
219 // Doc from superclass.
220 void
pick(SoPickAction * action)221 SoAntiSquish::pick(SoPickAction * action)
222 {
223   SoAntiSquish::doAction((SoAction *) action);
224 }
225 
226 /*!
227   Calculate and return the matrix needed to "unsquish" the \a
228   squishedmatrix.
229 
230   If \a calcinverse is \c TRUE, store the inverse of the
231   unsquishmatrix in \a getinverse.
232 */
233 SbMatrix
getUnsquishingMatrix(const SbMatrix & squishedmatrix,const SbBool calcinverse,SbMatrix & getinverse)234 SoAntiSquish::getUnsquishingMatrix(const SbMatrix & squishedmatrix,
235                                    const SbBool calcinverse,
236                                    SbMatrix & getinverse)
237 {
238   SbRotation r, so;
239   SbVec3f t, scale;
240 
241   float val = 1.0f;
242 
243   squishedmatrix.getTransform(t, r, scale, so);
244   switch (this->sizing.getValue()) {
245   case X:
246     val = scale[0];
247     break;
248   case Y:
249     val = scale[1];
250     break;
251   case Z:
252     val = scale[2];
253     break;
254   case AVERAGE_DIMENSION:
255     val = (scale[0] + scale[1] + scale[2]) / 3.0f;
256     break;
257   case BIGGEST_DIMENSION:
258     val = scale[0];
259     if (scale[1] > val) val = scale[1];
260     if (scale[2] > val) val = scale[2];
261     break;
262   case SMALLEST_DIMENSION:
263     val = scale[0];
264     if (scale[1] < val) val = scale[1];
265     if (scale[2] < val) val = scale[2];
266     break;
267   case LONGEST_DIAGONAL:
268     {
269       SbVec3f unitcube[8];
270       for (int i = 0; i < 8; i++) {
271         unitcube[i][0] = i & 1 ? 1.0f : -1.0f;
272         unitcube[i][1] = i & 2 ? 1.0f : -1.0f;
273         unitcube[i][2] = i & 4 ? 1.0f : -1.0f;
274         squishedmatrix.multVecMatrix(unitcube[i], unitcube[i]);
275       }
276 
277       val = (unitcube[1] - unitcube[6]).sqrLength();
278       float tmp = (unitcube[5] - unitcube[2]).sqrLength();
279       if (tmp > val) val = tmp;
280       tmp = (unitcube[3] - unitcube[4]).sqrLength();
281       if (tmp > val) val = tmp;
282       tmp = (unitcube[0] - unitcube[7]).sqrLength();
283       if (tmp > val) val = tmp;
284 
285       val = (float) sqrt(val);
286       val *= 0.5f;
287       break;
288     }
289   default:
290     assert(0 && "unknown sizing parameter");
291     val = (scale[0] + scale[1] + scale[2]) / 3.0f; // use avarage
292     break;
293   }
294   scale[0] = scale[1] = scale[2] = val;
295   SbMatrix matrix;
296   matrix.setTransform(t, r, scale, so);
297   matrix.multRight(squishedmatrix.inverse());
298   if (calcinverse) getinverse = matrix.inverse();
299   return matrix;
300 }
301