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 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36 
37 #ifdef HAVE_DRAGGERS
38 
39 /*!
40   \class SoTransformerDragger SoTransformerDragger.h Inventor/draggers/SoTransformerDragger.h
41   \brief The SoTransformerDragger provides geometry for translation, scaling and rotations.
42 
43   \ingroup draggers
44 
45   \DRAGGER_DEFAULT_SCREENSHOT
46 
47   <center>
48   \image html transformer.png "Screen Shot of Default Dragger"
49   </center>
50 
51   Translate the dragger by clicking and dragging any of the
52   (invisible) sides. Translation will default be done in the plane of
53   the side the end-user selected. The user can hold down a \c SHIFT
54   key to lock translation to a single of the axes in the plane. By
55   holding down a \c CTRL key instead, translation can be done along
56   the plane's normal vector.
57 
58   Scaling is done by dragging the corner cubes. By default, uniform
59   scaling will be done. Hold down \c SHIFT before selecting any of the
60   corners to do non-uniform scaling. Uniform scaling towards a
61   corner-point can be accomplished by holding down \c CTRL before
62   clicking and dragging one of the cubes.
63 
64   Rotation is done by dragging any of the 6 end-markers of the axis
65   cross. The initial drag direction decides which orientation the
66   rotation will be done in. Hold down \c SHIFT to do free-form
67   rotation around the sphere instead.
68 
69 
70   This is a big and complex dragger which needs a fair amount of
71   proper documentation when provided in end-user applications. If what
72   you are trying to accomplish in your application does not really
73   demand most of the features of this dragger, you are advised to
74   investigate whether or not any of the less complex draggers can
75   fulfill your requirements -- so you can provide an as simple as
76   possible user interface to your end-users.
77 
78 
79   For the application programmer's convenience, the Coin library also
80   provides a manipulator class called SoTransformerManip, which wraps
81   the SoTransformerDragger into the necessary mechanisms for making
82   direct insertion of this dragger into a scenegraph possible with
83   very little effort.
84 
85   \sa SoTransformerManip
86 */
87 
88 #include <Inventor/draggers/SoTransformerDragger.h>
89 
90 #include <cstring>
91 
92 #include <Inventor/nodes/SoAntiSquish.h>
93 #include <Inventor/nodes/SoLocateHighlight.h>
94 #include <Inventor/nodes/SoRotation.h>
95 #include <Inventor/nodes/SoSurroundScale.h>
96 #include <Inventor/nodes/SoSwitch.h>
97 #include <Inventor/nodes/SoTransform.h>
98 #include <Inventor/nodes/SoTranslation.h>
99 #include <Inventor/sensors/SoFieldSensor.h>
100 #include <Inventor/SbVec3f.h>
101 #include <Inventor/SbMatrix.h>
102 #include <Inventor/projectors/SbPlaneProjector.h>
103 #include <Inventor/projectors/SbLineProjector.h>
104 #include <Inventor/projectors/SbSphereSectionProjector.h>
105 #include <Inventor/projectors/SbCylinderPlaneProjector.h>
106 #include <Inventor/events/SoKeyboardEvent.h>
107 #include <Inventor/actions/SoSearchAction.h>
108 #include <Inventor/actions/SoGetMatrixAction.h>
109 #include <Inventor/lists/SoPathList.h>
110 
111 #include <data/draggerDefaults/transformerDragger.h>
112 
113 #include "coindefs.h" // COIN_STUB() & COIN_OBSOLETED()
114 #include "nodekits/SoSubKitP.h"
115 #include "SbBasicP.h"
116 
117 // FIXME, bugs or missing features (pederb, 20000224):
118 // o some feedback is missing (mostly crosshair)
119 // o detect if disc or cylinder rotator should be used (disc-only right now)
120 //
121 // Also the translation feedback is a bit different from OIV. Coin
122 // always places the feedback axes at the center of the face being
123 // translated. OIV places them at the picked point. I think our
124 // strategy is better, since when switching between constrained
125 // translations and unconstrained translation, the OIV feedback axes
126 // can easily be positioned outside the face being dragged.
127 //
128 // MATRICES AND SPACES:
129 // There are many matrices and spaces that can take some time to get
130 // understand. The matrices calculated are:
131 //
132 // localToWorld = motionMatrix * draggerToWorld
133 // worldToLocal = worldToDragger * motionMatrix^-1
134 // draggerToWorld = worldToDragger^-1
135 //
136 // localToWorking = surroundScaleMatrix^-1
137 // workingToLocal = surroundScaleMatrix
138 //
139 // workingToWorld = surroundScaleMatrix * localToWorld
140 // worldToWorking = worldToLocal * surroundScaleMatrix
141 //
142 // boxPointInWorldSpace = p * surroundScaleMatrix * localToWorld
143 // worldPointInBoxSpace = p * worldToLocal * surroundScaleMatrix
144 
145 /*!
146   \enum SoTransformerDragger::State
147 
148   The various possible states the dragger might be in at any given
149   time. That is: either SoTransformerDragger::INACTIVE if there's no
150   interaction, or any of the other values to indicate what operation
151   the end-user is currently executing.
152 */
153 
154 /*!
155   \var SoSFRotation SoTransformerDragger::rotation
156 
157   This field is continuously updated to contain the orientation of the
158   dragger.
159 */
160 /*!
161   \var SoSFVec3f SoTransformerDragger::translation
162 
163   The dragger's offset position from the local origo.
164 */
165 /*!
166   \var SoSFVec3f SoTransformerDragger::scaleFactor
167 
168   Continuously updated to contain the current vector of scaling along
169   the X, Y and Z axes.
170 */
171 
172 // FIXME: can't see what this is for -- investigate. 20011208 mortene.
173 //  /*!
174 //    \var SoSFFloat SoTransformerDragger::minDiscRotDot
175 //  */
176 
177 /*!
178   \var SoFieldSensor * SoTransformerDragger::translFieldSensor
179   \COININTERNAL
180 */
181 /*!
182   \var SoFieldSensor * SoTransformerDragger::scaleFieldSensor
183   \COININTERNAL
184 */
185 /*!
186   \var SoFieldSensor * SoTransformerDragger::rotateFieldSensor
187   \COININTERNAL
188 */
189 /*!
190   \var SoNodeList SoTransformerDragger::antiSquishList
191   \COININTERNAL
192 */
193 
194 #define WHATKIND_NONE      0
195 #define WHATKIND_SCALE     1
196 #define WHATKIND_TRANSLATE 2
197 #define WHATKIND_ROTATE    3
198 
199 #define CONSTRAINT_OFF  0
200 #define CONSTRAINT_WAIT 1
201 #define CONSTRAINT_X    2
202 #define CONSTRAINT_Y    3
203 #define CONSTRAINT_Z    4
204 
205 #define KNOB_DISTANCE 1.25f   // distance from center to rotate-knobs
206 
207 
208 #ifndef DOXYGEN_SKIP_THIS
209 
210 class SoTransformerDraggerP {
211 public:
212   SbMatrix prevMotionMatrix;
213   SbVec3f prevWorldHitPt;
214   SbVec3f ctrlOffset;
215   SbBool ctrlDown;
216   SbBool shiftDown;
217   SbVec2f normalizedStartLocaterPosition;
218 
219   SbBool locateHighlighting;
220   static int colinearThreshold;
221   int constraintState;
222 
223   int whatkind;
224   int whatnum;
225   int dimension;
226 };
227 
228 int SoTransformerDraggerP::colinearThreshold = 3; // FIXME: find default value from somewhere
229 
230 #endif // DOXYGEN_SKIP_THIS
231 
232 SO_KIT_SOURCE(SoTransformerDragger);
233 
234 // doc in superclass
235 void
initClass(void)236 SoTransformerDragger::initClass(void)
237 {
238   SO_KIT_INTERNAL_INIT_CLASS(SoTransformerDragger, SO_FROM_INVENTOR_1);
239   SoTransformerDraggerP::colinearThreshold = 3;
240 }
241 
242 void
build_catalog1(void)243 SoTransformerDragger::build_catalog1(void)
244 {
245   SO_KIT_ADD_CATALOG_ENTRY(surroundScale, SoSurroundScale, TRUE, topSeparator, overallStyle, TRUE);
246   SO_KIT_ADD_CATALOG_ENTRY(overallStyle, SoGroup, TRUE, topSeparator, geomSeparator, FALSE);
247   SO_KIT_ADD_CATALOG_ENTRY(translatorSep, SoSeparator, TRUE, topSeparator, rotatorSep, FALSE);
248 }
249 
250 void
build_catalog2(void)251 SoTransformerDragger::build_catalog2(void)
252 {
253   SO_KIT_ADD_CATALOG_ENTRY(translator1Switch, SoSwitch, TRUE, translatorSep, translator2Switch, FALSE);
254   SO_KIT_ADD_CATALOG_ENTRY(translator1LocateGroup, SoLocateHighlight, TRUE, translator1Switch, translator1Active, FALSE);
255   SO_KIT_ADD_CATALOG_ENTRY(translator1, SoSeparator, TRUE, translator1LocateGroup, "", TRUE);
256   SO_KIT_ADD_CATALOG_ENTRY(translator1Active, SoSeparator, TRUE, translator1Switch, "", TRUE);
257   SO_KIT_ADD_CATALOG_ENTRY(translator2Switch, SoSwitch, TRUE, translatorSep, translator3Switch, FALSE);
258   SO_KIT_ADD_CATALOG_ENTRY(translator2LocateGroup, SoLocateHighlight, TRUE, translator2Switch, translator2Active, FALSE);
259   SO_KIT_ADD_CATALOG_ENTRY(translator2, SoSeparator, TRUE, translator2LocateGroup, "", TRUE);
260   SO_KIT_ADD_CATALOG_ENTRY(translator2Active, SoSeparator, TRUE, translator2Switch, "", TRUE);
261   SO_KIT_ADD_CATALOG_ENTRY(translator3Switch, SoSwitch, TRUE, translatorSep, translator4Switch, FALSE);
262   SO_KIT_ADD_CATALOG_ENTRY(translator3LocateGroup, SoLocateHighlight, TRUE, translator3Switch, translator3Active, FALSE);
263   SO_KIT_ADD_CATALOG_ENTRY(translator3, SoSeparator, TRUE, translator3LocateGroup, "", TRUE);
264   SO_KIT_ADD_CATALOG_ENTRY(translator3Active, SoSeparator, TRUE, translator3Switch, "", TRUE);
265   SO_KIT_ADD_CATALOG_ENTRY(translator4Switch, SoSwitch, TRUE, translatorSep, translator5Switch, FALSE);
266   SO_KIT_ADD_CATALOG_ENTRY(translator4LocateGroup, SoLocateHighlight, TRUE, translator4Switch, translator4Active, FALSE);
267   SO_KIT_ADD_CATALOG_ENTRY(translator4, SoSeparator, TRUE, translator4LocateGroup, "", TRUE);
268   SO_KIT_ADD_CATALOG_ENTRY(translator4Active, SoSeparator, TRUE, translator4Switch, "", TRUE);
269   SO_KIT_ADD_CATALOG_ENTRY(translator5Switch, SoSwitch, TRUE, translatorSep, translator6Switch, FALSE);
270   SO_KIT_ADD_CATALOG_ENTRY(translator5LocateGroup, SoLocateHighlight, TRUE, translator5Switch, translator5Active, FALSE);
271   SO_KIT_ADD_CATALOG_ENTRY(translator5, SoSeparator, TRUE, translator5LocateGroup, "", TRUE);
272   SO_KIT_ADD_CATALOG_ENTRY(translator5Active, SoSeparator, TRUE, translator5Switch, "", TRUE);
273   SO_KIT_ADD_CATALOG_ENTRY(translator6Switch, SoSwitch, TRUE, translatorSep, "", FALSE);
274   SO_KIT_ADD_CATALOG_ENTRY(translator6LocateGroup, SoLocateHighlight, TRUE, translator6Switch, translator6Active, FALSE);
275   SO_KIT_ADD_CATALOG_ENTRY(translator6, SoSeparator, TRUE, translator6LocateGroup, "", TRUE);
276   SO_KIT_ADD_CATALOG_ENTRY(translator6Active, SoSeparator, TRUE, translator6Switch, "", TRUE);
277 }
278 
279 void
build_catalog3(void)280 SoTransformerDragger::build_catalog3(void)
281 {
282   SO_KIT_ADD_CATALOG_ENTRY(rotatorSep, SoSeparator, TRUE, topSeparator, scaleSep, FALSE);
283   SO_KIT_ADD_CATALOG_ENTRY(rotator1Switch, SoSwitch, TRUE, rotatorSep, rotator2Switch, FALSE);
284   SO_KIT_ADD_CATALOG_ENTRY(rotator1LocateGroup, SoLocateHighlight, TRUE, rotator1Switch, rotator1Active, FALSE);
285   SO_KIT_ADD_CATALOG_ENTRY(rotator1, SoSeparator, TRUE, rotator1LocateGroup, "", TRUE);
286   SO_KIT_ADD_CATALOG_ENTRY(rotator1Active, SoSeparator, TRUE, rotator1Switch, "", TRUE);
287   SO_KIT_ADD_CATALOG_ENTRY(rotator2Switch, SoSwitch, TRUE, rotatorSep, rotator3Switch, FALSE);
288   SO_KIT_ADD_CATALOG_ENTRY(rotator2LocateGroup, SoLocateHighlight, TRUE, rotator2Switch, rotator2Active, FALSE);
289   SO_KIT_ADD_CATALOG_ENTRY(rotator2, SoSeparator, TRUE, rotator2LocateGroup, "", TRUE);
290   SO_KIT_ADD_CATALOG_ENTRY(rotator2Active, SoSeparator, TRUE, rotator2Switch, "", TRUE);
291   SO_KIT_ADD_CATALOG_ENTRY(rotator3Switch, SoSwitch, TRUE, rotatorSep, rotator4Switch, FALSE);
292   SO_KIT_ADD_CATALOG_ENTRY(rotator3LocateGroup, SoLocateHighlight, TRUE, rotator3Switch, rotator3Active, FALSE);
293   SO_KIT_ADD_CATALOG_ENTRY(rotator3, SoSeparator, TRUE, rotator3LocateGroup, "", TRUE);
294   SO_KIT_ADD_CATALOG_ENTRY(rotator3Active, SoSeparator, TRUE, rotator3Switch, "", TRUE);
295   SO_KIT_ADD_CATALOG_ENTRY(rotator4Switch, SoSwitch, TRUE, rotatorSep, rotator5Switch, FALSE);
296   SO_KIT_ADD_CATALOG_ENTRY(rotator4LocateGroup, SoLocateHighlight, TRUE, rotator4Switch, rotator4Active, FALSE);
297   SO_KIT_ADD_CATALOG_ENTRY(rotator4, SoSeparator, TRUE, rotator4LocateGroup, "", TRUE);
298   SO_KIT_ADD_CATALOG_ENTRY(rotator4Active, SoSeparator, TRUE, rotator4Switch, "", TRUE);
299   SO_KIT_ADD_CATALOG_ENTRY(rotator5Switch, SoSwitch, TRUE, rotatorSep, rotator6Switch, FALSE);
300   SO_KIT_ADD_CATALOG_ENTRY(rotator5LocateGroup, SoLocateHighlight, TRUE, rotator5Switch, rotator5Active, FALSE);
301   SO_KIT_ADD_CATALOG_ENTRY(rotator5, SoSeparator, TRUE, rotator5LocateGroup, "", TRUE);
302   SO_KIT_ADD_CATALOG_ENTRY(rotator5Active, SoSeparator, TRUE, rotator5Switch, "", TRUE);
303   SO_KIT_ADD_CATALOG_ENTRY(rotator6Switch, SoSwitch, TRUE, rotatorSep, "", FALSE);
304   SO_KIT_ADD_CATALOG_ENTRY(rotator6LocateGroup, SoLocateHighlight, TRUE, rotator6Switch, rotator6Active, FALSE);
305   SO_KIT_ADD_CATALOG_ENTRY(rotator6, SoSeparator, TRUE, rotator6LocateGroup, "", TRUE);
306   SO_KIT_ADD_CATALOG_ENTRY(rotator6Active, SoSeparator, TRUE, rotator6Switch, "", TRUE);
307 }
308 
309 void
build_catalog4(void)310 SoTransformerDragger::build_catalog4(void)
311 {
312   SO_KIT_ADD_CATALOG_ENTRY(scaleSep, SoSeparator, TRUE, topSeparator, circleFeedbackSep, FALSE);
313   SO_KIT_ADD_CATALOG_ENTRY(scale1Switch, SoSwitch, TRUE, scaleSep, scale2Switch, FALSE);
314   SO_KIT_ADD_CATALOG_ENTRY(scale1LocateGroup, SoLocateHighlight, TRUE, scale1Switch, scale1Active, FALSE);
315   SO_KIT_ADD_CATALOG_ENTRY(scale1, SoSeparator, TRUE, scale1LocateGroup, "", TRUE);
316   SO_KIT_ADD_CATALOG_ENTRY(scale1Active, SoSeparator, TRUE, scale1Switch, "", TRUE);
317   SO_KIT_ADD_CATALOG_ENTRY(scale2Switch, SoSwitch, TRUE, scaleSep, scale3Switch, FALSE);
318   SO_KIT_ADD_CATALOG_ENTRY(scale2LocateGroup, SoLocateHighlight, TRUE, scale2Switch, scale2Active, FALSE);
319   SO_KIT_ADD_CATALOG_ENTRY(scale2, SoSeparator, TRUE, scale2LocateGroup, "", TRUE);
320   SO_KIT_ADD_CATALOG_ENTRY(scale2Active, SoSeparator, TRUE, scale2Switch, "", TRUE);
321   SO_KIT_ADD_CATALOG_ENTRY(scale3Switch, SoSwitch, TRUE, scaleSep, scale4Switch, FALSE);
322   SO_KIT_ADD_CATALOG_ENTRY(scale3LocateGroup, SoLocateHighlight, TRUE, scale3Switch, scale3Active, FALSE);
323   SO_KIT_ADD_CATALOG_ENTRY(scale3, SoSeparator, TRUE, scale3LocateGroup, "", TRUE);
324   SO_KIT_ADD_CATALOG_ENTRY(scale3Active, SoSeparator, TRUE, scale3Switch, "", TRUE);
325   SO_KIT_ADD_CATALOG_ENTRY(scale4Switch, SoSwitch, TRUE, scaleSep, scale5Switch, FALSE);
326   SO_KIT_ADD_CATALOG_ENTRY(scale4LocateGroup, SoLocateHighlight, TRUE, scale4Switch, scale4Active, FALSE);
327   SO_KIT_ADD_CATALOG_ENTRY(scale4, SoSeparator, TRUE, scale4LocateGroup, "", TRUE);
328   SO_KIT_ADD_CATALOG_ENTRY(scale4Active, SoSeparator, TRUE, scale4Switch, "", TRUE);
329   SO_KIT_ADD_CATALOG_ENTRY(scale5Switch, SoSwitch, TRUE, scaleSep, scale6Switch, FALSE);
330   SO_KIT_ADD_CATALOG_ENTRY(scale5LocateGroup, SoLocateHighlight, TRUE, scale5Switch, scale5Active, FALSE);
331   SO_KIT_ADD_CATALOG_ENTRY(scale5, SoSeparator, TRUE, scale5LocateGroup, "", TRUE);
332   SO_KIT_ADD_CATALOG_ENTRY(scale5Active, SoSeparator, TRUE, scale5Switch, "", TRUE);
333   SO_KIT_ADD_CATALOG_ENTRY(scale6Switch, SoSwitch, TRUE, scaleSep, scale7Switch, FALSE);
334   SO_KIT_ADD_CATALOG_ENTRY(scale6LocateGroup, SoLocateHighlight, TRUE, scale6Switch, scale6Active, FALSE);
335   SO_KIT_ADD_CATALOG_ENTRY(scale6, SoSeparator, TRUE, scale6LocateGroup, "", TRUE);
336   SO_KIT_ADD_CATALOG_ENTRY(scale6Active, SoSeparator, TRUE, scale6Switch, "", TRUE);
337   SO_KIT_ADD_CATALOG_ENTRY(scale7Switch, SoSwitch, TRUE, scaleSep, scale8Switch, FALSE);
338   SO_KIT_ADD_CATALOG_ENTRY(scale7LocateGroup, SoLocateHighlight, TRUE, scale7Switch, scale7Active, FALSE);
339   SO_KIT_ADD_CATALOG_ENTRY(scale7, SoSeparator, TRUE, scale7LocateGroup, "", TRUE);
340   SO_KIT_ADD_CATALOG_ENTRY(scale7Active, SoSeparator, TRUE, scale7Switch, "", TRUE);
341   SO_KIT_ADD_CATALOG_ENTRY(scale8Switch, SoSwitch, TRUE, scaleSep, "", FALSE);
342   SO_KIT_ADD_CATALOG_ENTRY(scale8LocateGroup, SoLocateHighlight, TRUE, scale8Switch, scale8Active, FALSE);
343   SO_KIT_ADD_CATALOG_ENTRY(scale8, SoSeparator, TRUE, scale8LocateGroup, "", TRUE);
344   SO_KIT_ADD_CATALOG_ENTRY(scale8Active, SoSeparator, TRUE, scale8Switch, "", TRUE);
345 }
346 
347 void
build_catalog5(void)348 SoTransformerDragger::build_catalog5(void)
349 {
350   SO_KIT_ADD_CATALOG_ENTRY(axisFeedbackSep, SoSeparator, TRUE, geomSeparator, translateBoxFeedbackSep, FALSE);
351   SO_KIT_ADD_CATALOG_ENTRY(axisFeedbackLocation, SoTranslation, TRUE, axisFeedbackSep, xAxisFeedbackSwitch, FALSE);
352   SO_KIT_ADD_CATALOG_ENTRY(xAxisFeedbackSwitch, SoSwitch, TRUE, axisFeedbackSep, yAxisFeedbackSwitch, FALSE);
353   SO_KIT_ADD_CATALOG_ENTRY(xAxisFeedbackActive, SoSeparator, TRUE, xAxisFeedbackSwitch, xAxisFeedbackSelect, TRUE);
354   SO_KIT_ADD_CATALOG_ENTRY(xAxisFeedbackSelect, SoSeparator, TRUE, xAxisFeedbackSwitch, xCrosshairFeedback, TRUE);
355   SO_KIT_ADD_CATALOG_ENTRY(xCrosshairFeedback, SoSeparator, TRUE, xAxisFeedbackSwitch, "", TRUE);
356   SO_KIT_ADD_CATALOG_ENTRY(yAxisFeedbackSwitch, SoSwitch, TRUE, axisFeedbackSep, zAxisFeedbackSwitch, FALSE);
357   SO_KIT_ADD_CATALOG_ENTRY(yAxisFeedbackActive, SoSeparator, TRUE, yAxisFeedbackSwitch, yAxisFeedbackSelect, TRUE);
358   SO_KIT_ADD_CATALOG_ENTRY(yAxisFeedbackSelect, SoSeparator, TRUE, yAxisFeedbackSwitch, yCrosshairFeedback, TRUE);
359   SO_KIT_ADD_CATALOG_ENTRY(yCrosshairFeedback, SoSeparator, TRUE, yAxisFeedbackSwitch, "", TRUE);
360   SO_KIT_ADD_CATALOG_ENTRY(zAxisFeedbackSwitch, SoSwitch, TRUE, axisFeedbackSep, "", FALSE);
361   SO_KIT_ADD_CATALOG_ENTRY(zAxisFeedbackActive, SoSeparator, TRUE, zAxisFeedbackSwitch, zAxisFeedbackSelect, TRUE);
362   SO_KIT_ADD_CATALOG_ENTRY(zAxisFeedbackSelect, SoSeparator, TRUE, zAxisFeedbackSwitch, zCrosshairFeedback, TRUE);
363   SO_KIT_ADD_CATALOG_ENTRY(zCrosshairFeedback, SoSeparator, TRUE, zAxisFeedbackSwitch, "", TRUE);
364   SO_KIT_ADD_CATALOG_ENTRY(translateBoxFeedbackSep, SoSeparator, TRUE, geomSeparator, scaleBoxFeedbackSwitch, FALSE);
365   SO_KIT_ADD_CATALOG_ENTRY(translateBoxFeedbackSwitch, SoSwitch, TRUE, translateBoxFeedbackSep, "", FALSE);
366   SO_KIT_ADD_CATALOG_ENTRY(translateBoxFeedbackRotation, SoRotation, TRUE, translateBoxFeedbackSwitch, translateBoxFeedback, FALSE);
367   SO_KIT_ADD_CATALOG_ENTRY(translateBoxFeedback, SoSeparator, TRUE, translateBoxFeedbackSwitch, "", TRUE);
368   SO_KIT_ADD_CATALOG_ENTRY(scaleBoxFeedbackSwitch, SoSwitch, TRUE, geomSeparator, posXWallFeedbackSwitch, FALSE);
369   SO_KIT_ADD_CATALOG_ENTRY(scaleBoxFeedback, SoSeparator, TRUE, scaleBoxFeedbackSwitch, "", TRUE);
370   SO_KIT_ADD_CATALOG_ENTRY(posXWallFeedbackSwitch, SoSwitch, TRUE, geomSeparator, posYWallFeedbackSwitch, FALSE);
371   SO_KIT_ADD_CATALOG_ENTRY(posXWallFeedback, SoSeparator, TRUE, posXWallFeedbackSwitch, posXRoundWallFeedback, TRUE);
372   SO_KIT_ADD_CATALOG_ENTRY(posXRoundWallFeedback, SoSeparator, TRUE, posXWallFeedbackSwitch, "", TRUE);
373   SO_KIT_ADD_CATALOG_ENTRY(posYWallFeedbackSwitch, SoSwitch, TRUE, geomSeparator, posZWallFeedbackSwitch, FALSE);
374   SO_KIT_ADD_CATALOG_ENTRY(posYWallFeedback, SoSeparator, TRUE, posYWallFeedbackSwitch, posYRoundWallFeedback, TRUE);
375   SO_KIT_ADD_CATALOG_ENTRY(posYRoundWallFeedback, SoSeparator, TRUE, posYWallFeedbackSwitch, "", TRUE);
376   SO_KIT_ADD_CATALOG_ENTRY(posZWallFeedbackSwitch, SoSwitch, TRUE, geomSeparator, negXWallFeedbackSwitch, FALSE);
377   SO_KIT_ADD_CATALOG_ENTRY(posZWallFeedback, SoSeparator, TRUE, posZWallFeedbackSwitch, posZRoundWallFeedback, TRUE);
378   SO_KIT_ADD_CATALOG_ENTRY(posZRoundWallFeedback, SoSeparator, TRUE, posZWallFeedbackSwitch, "", TRUE);
379 }
380 
381 void
build_catalog6(void)382 SoTransformerDragger::build_catalog6(void)
383 {
384   SO_KIT_ADD_CATALOG_ENTRY(negXWallFeedbackSwitch, SoSwitch, TRUE, geomSeparator, negYWallFeedbackSwitch, FALSE);
385   SO_KIT_ADD_CATALOG_ENTRY(negXWallFeedback, SoSeparator, TRUE, negXWallFeedbackSwitch, negXRoundWallFeedback, TRUE);
386   SO_KIT_ADD_CATALOG_ENTRY(negXRoundWallFeedback, SoSeparator, TRUE, negXWallFeedbackSwitch, "", TRUE);
387   SO_KIT_ADD_CATALOG_ENTRY(negYWallFeedbackSwitch, SoSwitch, TRUE, geomSeparator, negZWallFeedbackSwitch, FALSE);
388   SO_KIT_ADD_CATALOG_ENTRY(negYWallFeedback, SoSeparator, TRUE, negYWallFeedbackSwitch, negYRoundWallFeedback, TRUE);
389   SO_KIT_ADD_CATALOG_ENTRY(negYRoundWallFeedback, SoSeparator, TRUE, negYWallFeedbackSwitch, "", TRUE);
390   SO_KIT_ADD_CATALOG_ENTRY(negZWallFeedbackSwitch, SoSwitch, TRUE, geomSeparator, radialFeedbackSwitch, FALSE);
391   SO_KIT_ADD_CATALOG_ENTRY(negZWallFeedback, SoSeparator, TRUE, negZWallFeedbackSwitch, negZRoundWallFeedback, TRUE);
392   SO_KIT_ADD_CATALOG_ENTRY(negZRoundWallFeedback, SoSeparator, TRUE, negZWallFeedbackSwitch, "", TRUE);
393   SO_KIT_ADD_CATALOG_ENTRY(radialFeedbackSwitch, SoSwitch, TRUE, geomSeparator, "", FALSE);
394   SO_KIT_ADD_CATALOG_ENTRY(radialFeedback, SoSeparator, TRUE, radialFeedbackSwitch, "", TRUE);
395   SO_KIT_ADD_CATALOG_ENTRY(circleFeedbackSep, SoSeparator, TRUE, topSeparator, "", FALSE);
396   SO_KIT_ADD_CATALOG_ENTRY(circleFeedbackTransformSwitch, SoSwitch, TRUE, circleFeedbackSep, xCircleFeedbackSwitch, FALSE);
397   SO_KIT_ADD_CATALOG_ENTRY(circleFeedbackAntiSquish, SoAntiSquish, TRUE, circleFeedbackTransformSwitch, circleFeedbackTransform, FALSE);
398   SO_KIT_ADD_CATALOG_ENTRY(circleFeedbackTransform, SoTransform, TRUE, circleFeedbackTransformSwitch, "", FALSE);
399   SO_KIT_ADD_CATALOG_ENTRY(xCircleFeedbackSwitch, SoSwitch, TRUE, circleFeedbackSep, yCircleFeedbackSwitch, FALSE);
400   SO_KIT_ADD_CATALOG_ENTRY(xCircleFeedback, SoSeparator, TRUE, xCircleFeedbackSwitch, "", TRUE);
401   SO_KIT_ADD_CATALOG_ENTRY(yCircleFeedbackSwitch, SoSwitch, TRUE, circleFeedbackSep, zCircleFeedbackSwitch, FALSE);
402   SO_KIT_ADD_CATALOG_ENTRY(yCircleFeedback, SoSeparator, TRUE, yCircleFeedbackSwitch, "", TRUE);
403   SO_KIT_ADD_CATALOG_ENTRY(zCircleFeedbackSwitch, SoSwitch, TRUE, circleFeedbackSep, "", FALSE);
404   SO_KIT_ADD_CATALOG_ENTRY(zCircleFeedback, SoSeparator, TRUE, zCircleFeedbackSwitch, "", TRUE);
405 }
406 
407 #define PRIVATE(obj) ((obj)->pimpl)
408 #define THISP(d) static_cast<SoTransformerDragger *>(d)
409 
410 // FIXME: document which parts need to be present in the geometry
411 // scenegraph, and what role they play in the dragger. 20010913 mortene.
412 /*!
413   \DRAGGER_CONSTRUCTOR
414 
415   \NODEKIT_PRE_DIAGRAM
416 
417   \verbatim
418   CLASS SoTransformerDragger
419   -->"this"
420         "callbackList"
421         "topSeparator"
422            "motionMatrix"
423   -->      "surroundScale"
424   -->      "overallStyle"
425            "geomSeparator"
426   -->         "axisFeedbackSep"
427   -->            "axisFeedbackLocation"
428   -->            "xAxisFeedbackSwitch"
429   -->               "xAxisFeedbackActive"
430   -->               "xAxisFeedbackSelect"
431   -->               "xCrosshairFeedback"
432   -->            "yAxisFeedbackSwitch"
433   -->               "yAxisFeedbackActive"
434   -->               "yAxisFeedbackSelect"
435   -->               "yCrosshairFeedback"
436   -->            "zAxisFeedbackSwitch"
437   -->               "zAxisFeedbackActive"
438   -->               "zAxisFeedbackSelect"
439   -->               "zCrosshairFeedback"
440   -->         "translateBoxFeedbackSep"
441   -->            "translateBoxFeedbackSwitch"
442   -->               "translateBoxFeedbackRotation"
443   -->               "translateBoxFeedback"
444   -->         "scaleBoxFeedbackSwitch"
445   -->            "scaleBoxFeedback"
446   -->         "posXWallFeedbackSwitch"
447   -->            "posXWallFeedback"
448   -->            "posXRoundWallFeedback"
449   -->         "posYWallFeedbackSwitch"
450   -->            "posYWallFeedback"
451   -->            "posYRoundWallFeedback"
452   -->         "posZWallFeedbackSwitch"
453   -->            "posZWallFeedback"
454   -->            "posZRoundWallFeedback"
455   -->         "negXWallFeedbackSwitch"
456   -->            "negXWallFeedback"
457   -->            "negXRoundWallFeedback"
458   -->         "negYWallFeedbackSwitch"
459   -->            "negYWallFeedback"
460   -->            "negYRoundWallFeedback"
461   -->         "negZWallFeedbackSwitch"
462   -->            "negZWallFeedback"
463   -->            "negZRoundWallFeedback"
464   -->         "radialFeedbackSwitch"
465   -->            "radialFeedback"
466   -->      "translatorSep"
467   -->         "translator1Switch"
468   -->            "translator1LocateGroup"
469   -->               "translator1"
470   -->            "translator1Active"
471   -->         "translator2Switch"
472   -->            "translator2LocateGroup"
473   -->               "translator2"
474   -->            "translator2Active"
475   -->         "translator3Switch"
476   -->            "translator3LocateGroup"
477   -->               "translator3"
478   -->            "translator3Active"
479   -->         "translator4Switch"
480   -->            "translator4LocateGroup"
481   -->               "translator4"
482   -->            "translator4Active"
483   -->         "translator5Switch"
484   -->            "translator5LocateGroup"
485   -->               "translator5"
486   -->            "translator5Active"
487   -->         "translator6Switch"
488   -->            "translator6LocateGroup"
489   -->               "translator6"
490   -->            "translator6Active"
491   -->      "rotatorSep"
492   -->         "rotator1Switch"
493   -->            "rotator1LocateGroup"
494   -->               "rotator1"
495   -->            "rotator1Active"
496   -->         "rotator2Switch"
497   -->            "rotator2LocateGroup"
498   -->               "rotator2"
499   -->            "rotator2Active"
500   -->         "rotator3Switch"
501   -->            "rotator3LocateGroup"
502   -->               "rotator3"
503   -->            "rotator3Active"
504   -->         "rotator4Switch"
505   -->            "rotator4LocateGroup"
506   -->               "rotator4"
507   -->            "rotator4Active"
508   -->         "rotator5Switch"
509   -->            "rotator5LocateGroup"
510   -->               "rotator5"
511   -->            "rotator5Active"
512   -->         "rotator6Switch"
513   -->            "rotator6LocateGroup"
514   -->               "rotator6"
515   -->            "rotator6Active"
516   -->      "scaleSep"
517   -->         "scale1Switch"
518   -->            "scale1LocateGroup"
519   -->               "scale1"
520   -->            "scale1Active"
521   -->         "scale2Switch"
522   -->            "scale2LocateGroup"
523   -->               "scale2"
524   -->            "scale2Active"
525   -->         "scale3Switch"
526   -->            "scale3LocateGroup"
527   -->               "scale3"
528   -->            "scale3Active"
529   -->         "scale4Switch"
530   -->            "scale4LocateGroup"
531   -->               "scale4"
532   -->            "scale4Active"
533   -->         "scale5Switch"
534   -->            "scale5LocateGroup"
535   -->               "scale5"
536   -->            "scale5Active"
537   -->         "scale6Switch"
538   -->            "scale6LocateGroup"
539   -->               "scale6"
540   -->            "scale6Active"
541   -->         "scale7Switch"
542   -->            "scale7LocateGroup"
543   -->               "scale7"
544   -->            "scale7Active"
545   -->         "scale8Switch"
546   -->            "scale8LocateGroup"
547   -->               "scale8"
548   -->            "scale8Active"
549   -->      "circleFeedbackSep"
550   -->         "circleFeedbackTransformSwitch"
551   -->            "circleFeedbackAntiSquish"
552   -->            "circleFeedbackTransform"
553   -->         "xCircleFeedbackSwitch"
554   -->            "xCircleFeedback"
555   -->         "yCircleFeedbackSwitch"
556   -->            "yCircleFeedback"
557   -->         "zCircleFeedbackSwitch"
558   -->            "zCircleFeedback"
559   \endverbatim
560 
561   \NODEKIT_POST_DIAGRAM
562 
563 
564   \NODEKIT_PRE_TABLE
565 
566   \verbatim
567   CLASS SoTransformerDragger
568   PVT   "this",  SoTransformerDragger  ---
569         "callbackList",  SoNodeKitListPart [ SoCallback, SoEventCallback ]
570   PVT   "topSeparator",  SoSeparator  ---
571   PVT   "motionMatrix",  SoMatrixTransform  ---
572         "surroundScale",  SoSurroundScale  ---
573   PVT   "overallStyle",  SoGroup  ---
574   PVT   "geomSeparator",  SoSeparator  ---
575   PVT   "translatorSep",  SoSeparator  ---
576   PVT   "translator1Switch",  SoSwitch  ---
577   PVT   "translator1LocateGroup",  SoLocateHighlight  ---
578         "translator1",  SoSeparator  ---
579         "translator1Active",  SoSeparator  ---
580   PVT   "translator2Switch",  SoSwitch  ---
581   PVT   "translator2LocateGroup",  SoLocateHighlight  ---
582         "translator2",  SoSeparator  ---
583         "translator2Active",  SoSeparator  ---
584   PVT   "translator3Switch",  SoSwitch  ---
585   PVT   "translator3LocateGroup",  SoLocateHighlight  ---
586         "translator3",  SoSeparator  ---
587         "translator3Active",  SoSeparator  ---
588   PVT   "translator4Switch",  SoSwitch  ---
589   PVT   "translator4LocateGroup",  SoLocateHighlight  ---
590         "translator4",  SoSeparator  ---
591         "translator4Active",  SoSeparator  ---
592   PVT   "translator5Switch",  SoSwitch  ---
593   PVT   "translator5LocateGroup",  SoLocateHighlight  ---
594         "translator5",  SoSeparator  ---
595         "translator5Active",  SoSeparator  ---
596   PVT   "translator6Switch",  SoSwitch  ---
597   PVT   "translator6LocateGroup",  SoLocateHighlight  ---
598         "translator6",  SoSeparator  ---
599         "translator6Active",  SoSeparator  ---
600   PVT   "rotatorSep",  SoSeparator  ---
601   PVT   "rotator1Switch",  SoSwitch  ---
602   PVT   "rotator1LocateGroup",  SoLocateHighlight  ---
603         "rotator1",  SoSeparator  ---
604         "rotator1Active",  SoSeparator  ---
605   PVT   "rotator2Switch",  SoSwitch  ---
606   PVT   "rotator2LocateGroup",  SoLocateHighlight  ---
607         "rotator2",  SoSeparator  ---
608         "rotator2Active",  SoSeparator  ---
609   PVT   "rotator3Switch",  SoSwitch  ---
610   PVT   "rotator3LocateGroup",  SoLocateHighlight  ---
611         "rotator3",  SoSeparator  ---
612         "rotator3Active",  SoSeparator  ---
613   PVT   "rotator4Switch",  SoSwitch  ---
614   PVT   "rotator4LocateGroup",  SoLocateHighlight  ---
615         "rotator4",  SoSeparator  ---
616         "rotator4Active",  SoSeparator  ---
617   PVT   "rotator5Switch",  SoSwitch  ---
618   PVT   "rotator5LocateGroup",  SoLocateHighlight  ---
619         "rotator5",  SoSeparator  ---
620         "rotator5Active",  SoSeparator  ---
621   PVT   "rotator6Switch",  SoSwitch  ---
622   PVT   "rotator6LocateGroup",  SoLocateHighlight  ---
623         "rotator6",  SoSeparator  ---
624         "rotator6Active",  SoSeparator  ---
625   PVT   "scaleSep",  SoSeparator  ---
626   PVT   "scale1Switch",  SoSwitch  ---
627   PVT   "scale1LocateGroup",  SoLocateHighlight  ---
628         "scale1",  SoSeparator  ---
629         "scale1Active",  SoSeparator  ---
630   PVT   "scale2Switch",  SoSwitch  ---
631   PVT   "scale2LocateGroup",  SoLocateHighlight  ---
632         "scale2",  SoSeparator  ---
633         "scale2Active",  SoSeparator  ---
634   PVT   "scale3Switch",  SoSwitch  ---
635   PVT   "scale3LocateGroup",  SoLocateHighlight  ---
636         "scale3",  SoSeparator  ---
637         "scale3Active",  SoSeparator  ---
638   PVT   "scale4Switch",  SoSwitch  ---
639   PVT   "scale4LocateGroup",  SoLocateHighlight  ---
640         "scale4",  SoSeparator  ---
641         "scale4Active",  SoSeparator  ---
642   PVT   "scale5Switch",  SoSwitch  ---
643   PVT   "scale5LocateGroup",  SoLocateHighlight  ---
644         "scale5",  SoSeparator  ---
645         "scale5Active",  SoSeparator  ---
646   PVT   "scale6Switch",  SoSwitch  ---
647   PVT   "scale6LocateGroup",  SoLocateHighlight  ---
648         "scale6",  SoSeparator  ---
649         "scale6Active",  SoSeparator  ---
650   PVT   "scale7Switch",  SoSwitch  ---
651   PVT   "scale7LocateGroup",  SoLocateHighlight  ---
652         "scale7",  SoSeparator  ---
653         "scale7Active",  SoSeparator  ---
654   PVT   "scale8Switch",  SoSwitch  ---
655   PVT   "scale8LocateGroup",  SoLocateHighlight  ---
656         "scale8",  SoSeparator  ---
657         "scale8Active",  SoSeparator  ---
658   PVT   "circleFeedbackSep",  SoSeparator  ---
659   PVT   "circleFeedbackTransformSwitch",  SoSwitch  ---
660   PVT   "circleFeedbackAntiSquish",  SoAntiSquish  ---
661   PVT   "circleFeedbackTransform",  SoTransform  ---
662   PVT   "xCircleFeedbackSwitch",  SoSwitch  ---
663         "xCircleFeedback",  SoSeparator  ---
664   PVT   "yCircleFeedbackSwitch",  SoSwitch  ---
665         "yCircleFeedback",  SoSeparator  ---
666   PVT   "zCircleFeedbackSwitch",  SoSwitch  ---
667         "zCircleFeedback",  SoSeparator  ---
668   PVT   "axisFeedbackSep",  SoSeparator  ---
669   PVT   "axisFeedbackLocation",  SoTranslation  ---
670   PVT   "xAxisFeedbackSwitch",  SoSwitch  ---
671         "xAxisFeedbackActive",  SoSeparator  ---
672         "xAxisFeedbackSelect",  SoSeparator  ---
673         "xCrosshairFeedback",  SoSeparator  ---
674   PVT   "yAxisFeedbackSwitch",  SoSwitch  ---
675         "yAxisFeedbackActive",  SoSeparator  ---
676         "yAxisFeedbackSelect",  SoSeparator  ---
677         "yCrosshairFeedback",  SoSeparator  ---
678   PVT   "zAxisFeedbackSwitch",  SoSwitch  ---
679         "zAxisFeedbackActive",  SoSeparator  ---
680         "zAxisFeedbackSelect",  SoSeparator  ---
681         "zCrosshairFeedback",  SoSeparator  ---
682   PVT   "translateBoxFeedbackSep",  SoSeparator  ---
683   PVT   "translateBoxFeedbackSwitch",  SoSwitch  ---
684   PVT   "translateBoxFeedbackRotation",  SoRotation  ---
685         "translateBoxFeedback",  SoSeparator  ---
686   PVT   "scaleBoxFeedbackSwitch",  SoSwitch  ---
687         "scaleBoxFeedback",  SoSeparator  ---
688   PVT   "posXWallFeedbackSwitch",  SoSwitch  ---
689         "posXWallFeedback",  SoSeparator  ---
690         "posXRoundWallFeedback",  SoSeparator  ---
691   PVT   "posYWallFeedbackSwitch",  SoSwitch  ---
692         "posYWallFeedback",  SoSeparator  ---
693         "posYRoundWallFeedback",  SoSeparator  ---
694   PVT   "posZWallFeedbackSwitch",  SoSwitch  ---
695         "posZWallFeedback",  SoSeparator  ---
696         "posZRoundWallFeedback",  SoSeparator  ---
697   PVT   "negXWallFeedbackSwitch",  SoSwitch  ---
698         "negXWallFeedback",  SoSeparator  ---
699         "negXRoundWallFeedback",  SoSeparator  ---
700   PVT   "negYWallFeedbackSwitch",  SoSwitch  ---
701         "negYWallFeedback",  SoSeparator  ---
702         "negYRoundWallFeedback",  SoSeparator  ---
703   PVT   "negZWallFeedbackSwitch",  SoSwitch  ---
704         "negZWallFeedback",  SoSeparator  ---
705         "negZRoundWallFeedback",  SoSeparator  ---
706   PVT   "radialFeedbackSwitch",  SoSwitch  ---
707         "radialFeedback",  SoSeparator  ---
708   \endverbatim
709 
710   \NODEKIT_POST_TABLE
711 */
SoTransformerDragger(void)712 SoTransformerDragger::SoTransformerDragger(void)
713 {
714   SO_KIT_INTERNAL_CONSTRUCTOR(SoTransformerDragger);
715 
716   // split-up to avoid one huge method
717   this->build_catalog1();
718   this->build_catalog2();
719   this->build_catalog3();
720   this->build_catalog4();
721   this->build_catalog5();
722   this->build_catalog6();
723 
724   if (SO_KIT_IS_FIRST_INSTANCE()) {
725     SoInteractionKit::readDefaultParts("transformerDragger.iv",
726                                        TRANSFORMERDRAGGER_draggergeometry,
727                                        static_cast<int>(strlen(TRANSFORMERDRAGGER_draggergeometry)));
728   }
729 
730   SO_KIT_ADD_FIELD(rotation, (SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f)));
731   SO_KIT_ADD_FIELD(translation, (0.0f, 0.0f, 0.0f));
732   SO_KIT_ADD_FIELD(scaleFactor, (1.0f, 1.0f, 1.0f));
733   // FIXME: it doesn't look like this field is actually used or set
734   // anywhere else but here. Investigate. 20011208 mortene.
735   SO_KIT_ADD_FIELD(minDiscRotDot, (0.025f));
736 
737   SO_KIT_INIT_INSTANCE();
738 
739   this->setPartAsDefault("overallStyle", "transformerOverallStyle");
740   this->setPartAsDefault("translator1", "transformerTranslator1");
741   this->setPartAsDefault("translator2", "transformerTranslator2");
742   this->setPartAsDefault("translator3", "transformerTranslator3");
743   this->setPartAsDefault("translator4", "transformerTranslator4");
744   this->setPartAsDefault("translator5", "transformerTranslator5");
745   this->setPartAsDefault("translator6", "transformerTranslator6");
746   this->setPartAsDefault("translator1Active", "transformerTranslator1Active");
747   this->setPartAsDefault("translator2Active", "transformerTranslator2Active");
748   this->setPartAsDefault("translator3Active", "transformerTranslator3Active");
749   this->setPartAsDefault("translator4Active", "transformerTranslator4Active");
750   this->setPartAsDefault("translator5Active", "transformerTranslator5Active");
751   this->setPartAsDefault("translator6Active", "transformerTranslator6Active");
752   this->setPartAsDefault("rotator1", "transformerRotator1");
753   this->setPartAsDefault("rotator2", "transformerRotator2");
754   this->setPartAsDefault("rotator3", "transformerRotator3");
755   this->setPartAsDefault("rotator4", "transformerRotator4");
756   this->setPartAsDefault("rotator5", "transformerRotator5");
757   this->setPartAsDefault("rotator6", "transformerRotator6");
758   this->setPartAsDefault("rotator1Active", "transformerRotator1Active");
759   this->setPartAsDefault("rotator2Active", "transformerRotator2Active");
760   this->setPartAsDefault("rotator3Active", "transformerRotator3Active");
761   this->setPartAsDefault("rotator4Active", "transformerRotator4Active");
762   this->setPartAsDefault("rotator5Active", "transformerRotator5Active");
763   this->setPartAsDefault("rotator6Active", "transformerRotator6Active");
764   this->setPartAsDefault("scale1", "transformerScale1");
765   this->setPartAsDefault("scale2", "transformerScale2");
766   this->setPartAsDefault("scale3", "transformerScale3");
767   this->setPartAsDefault("scale4", "transformerScale4");
768   this->setPartAsDefault("scale5", "transformerScale5");
769   this->setPartAsDefault("scale6", "transformerScale6");
770   this->setPartAsDefault("scale7", "transformerScale7");
771   this->setPartAsDefault("scale8", "transformerScale8");
772   this->setPartAsDefault("scale1Active", "transformerScale1Active");
773   this->setPartAsDefault("scale2Active", "transformerScale2Active");
774   this->setPartAsDefault("scale3Active", "transformerScale3Active");
775   this->setPartAsDefault("scale4Active", "transformerScale4Active");
776   this->setPartAsDefault("scale5Active", "transformerScale5Active");
777   this->setPartAsDefault("scale6Active", "transformerScale6Active");
778   this->setPartAsDefault("scale7Active", "transformerScale7Active");
779   this->setPartAsDefault("scale8Active", "transformerScale8Active");
780   this->setPartAsDefault("xAxisFeedbackActive", "transformerXAxisFeedbackActive");
781   this->setPartAsDefault("xAxisFeedbackSelect", "transformerXAxisFeedbackSelect");
782   this->setPartAsDefault("yAxisFeedbackActive", "transformerYAxisFeedbackActive");
783   this->setPartAsDefault("yAxisFeedbackSelect", "transformerYAxisFeedbackSelect");
784   this->setPartAsDefault("zAxisFeedbackActive", "transformerZAxisFeedbackActive");
785   this->setPartAsDefault("zAxisFeedbackSelect", "transformerZAxisFeedbackSelect");
786   this->setPartAsDefault("xCrosshairFeedback", "transformerXCrosshairFeedback");
787   this->setPartAsDefault("yCrosshairFeedback", "transformerYCrosshairFeedback");
788   this->setPartAsDefault("zCrosshairFeedback", "transformerZCrosshairFeedback");
789   this->setPartAsDefault("xCircleFeedback", "transformerXCircleFeedback");
790   this->setPartAsDefault("yCircleFeedback", "transformerYCircleFeedback");
791   this->setPartAsDefault("zCircleFeedback", "transformerZCircleFeedback");
792   this->setPartAsDefault("radialFeedback", "transformerRadialFeedback");
793   this->setPartAsDefault("translateBoxFeedback", "transformerTranslateBoxFeedback");
794 
795   this->setPartAsDefault("scaleBoxFeedback", "transformerScaleBoxFeedback");
796   this->setPartAsDefault("posXWallFeedback", "transformerPosXWallFeedback");
797   this->setPartAsDefault("posYWallFeedback", "transformerPosYWallFeedback");
798   this->setPartAsDefault("posZWallFeedback", "transformerPosZWallFeedback");
799   this->setPartAsDefault("negXWallFeedback", "transformerNegXWallFeedback");
800   this->setPartAsDefault("negYWallFeedback", "transformerNegYWallFeedback");
801   this->setPartAsDefault("negZWallFeedback", "transformerNegZWallFeedback");
802   this->setPartAsDefault("posXRoundWallFeedback", "transformerPosXRoundWallFeedback");
803   this->setPartAsDefault("posYRoundWallFeedback", "transformerPosYRoundWallFeedback");
804   this->setPartAsDefault("posZRoundWallFeedback", "transformerPosZRoundWallFeedback");
805   this->setPartAsDefault("negXRoundWallFeedback", "transformerNegXRoundWallFeedback");
806   this->setPartAsDefault("negYRoundWallFeedback", "transformerNegYRoundWallFeedback");
807   this->setPartAsDefault("negZRoundWallFeedback", "transformerNegZRoundWallFeedback");
808 
809   this->state = INACTIVE;
810   PRIVATE(this)->constraintState = CONSTRAINT_OFF;
811   // FIXME: according to SGI classdoc, this flag is supposed to be
812   // default TRUE?  Investigate. 20011208 mortene.
813   PRIVATE(this)->locateHighlighting = FALSE;
814   PRIVATE(this)->whatkind = WHATKIND_NONE;
815   PRIVATE(this)->whatnum = -1;
816 
817   this->setAllPartSwitches(0, 0, 0);
818 
819   this->addStartCallback(SoTransformerDragger::startCB);
820   this->addMotionCallback(SoTransformerDragger::motionCB);
821   this->addFinishCallback(SoTransformerDragger::finishCB);
822   this->addValueChangedCallback(SoTransformerDragger::valueChangedCB);
823   this->addOtherEventCallback(SoTransformerDragger::metaKeyChangeCB);
824 
825   this->planeProj = new SbPlaneProjector;
826   this->lineProj = new SbLineProjector;
827   this->sphereProj = new SbSphereSectionProjector;
828   this->cylProj = new SbCylinderPlaneProjector;
829 
830   this->translFieldSensor = new SoFieldSensor(SoTransformerDragger::fieldSensorCB, this);
831   this->translFieldSensor->setPriority(0);
832   this->scaleFieldSensor = new SoFieldSensor(SoTransformerDragger::fieldSensorCB, this);
833   this->scaleFieldSensor->setPriority(0);
834   this->rotateFieldSensor = new SoFieldSensor(SoTransformerDragger::fieldSensorCB, this);
835   this->rotateFieldSensor->setPriority(0);
836 
837   this->setUpConnections(TRUE, TRUE);
838 
839   // make sure these are not written if they have the default value.
840   // FIXME: investigate why this is needed. There must be a
841   // notification that is sent somewhere that causes the fields to
842   // become non-default. pederb, 2003-04-01
843   this->translatorSep.setDefault(TRUE);
844   this->rotatorSep.setDefault(TRUE);
845   this->scaleSep.setDefault(TRUE);
846   this->translateBoxFeedbackSep.setDefault(TRUE);
847   this->axisFeedbackSep.setDefault(TRUE);
848   this->scale8LocateGroup.setDefault(TRUE);
849   this->scale7LocateGroup.setDefault(TRUE);
850   this->circleFeedbackSep.setDefault(TRUE);
851 }
852 
853 /*!
854   Protected destructor.
855 
856   (Dragger classes are derived from SoBase, so they are reference
857   counted and automatically destroyed when their reference count goes
858   to 0.)
859  */
~SoTransformerDragger()860 SoTransformerDragger::~SoTransformerDragger()
861 {
862   delete this->planeProj;
863   delete this->lineProj;
864   delete this->sphereProj;
865   delete this->cylProj;
866 
867   delete this->translFieldSensor;
868   delete this->scaleFieldSensor;
869   delete this->rotateFieldSensor;
870 }
871 
872 // Doc in super.
873 SbBool
setUpConnections(SbBool onoff,SbBool doitalways)874 SoTransformerDragger::setUpConnections(SbBool onoff, SbBool doitalways)
875 {
876   if (!doitalways && this->connectionsSetUp == onoff) return onoff;
877 
878   if (onoff) {
879     inherited::setUpConnections(onoff, doitalways);
880 
881     SoTransformerDragger::fieldSensorCB(this, NULL);
882 
883     if (this->translFieldSensor->getAttachedField() != &this->translation) {
884       this->translFieldSensor->attach(&this->translation);
885     }
886     if (this->scaleFieldSensor->getAttachedField() != &this->scaleFactor) {
887       this->scaleFieldSensor->attach(&this->scaleFactor);
888     }
889     if (this->rotateFieldSensor->getAttachedField() != &this->rotation) {
890       this->rotateFieldSensor->attach(&this->rotation);
891     }
892   }
893   else {
894     if (this->translFieldSensor->getAttachedField() != NULL) {
895       this->translFieldSensor->detach();
896     }
897     if (this->scaleFieldSensor->getAttachedField() != NULL) {
898       this->scaleFieldSensor->detach();
899     }
900     if (this->rotateFieldSensor->getAttachedField() != NULL) {
901       this->rotateFieldSensor->detach();
902     }
903     inherited::setUpConnections(onoff, doitalways);
904   }
905   return !(this->connectionsSetUp = onoff);
906 }
907 
908 // Convenience method used to call setDefault on similar fields.
909 //
910 // Note: keep the function name prefix to avoid name clashes with
911 // other dragger .cpp files for "--enable-compact" builds.
912 //
913 // FIXME: should collect these methods in a common method visible to
914 // all draggers implementing the exact same functionality. 20010826 mortene.
915 static void
SoTransformerDragger_set_default(SoDragger * dragger,const char * fmt,int minval,int maxval)916 SoTransformerDragger_set_default(SoDragger * dragger, const char * fmt,
917                                  int minval, int maxval)
918 {
919   SbString str;
920   for (int i = minval; i <= maxval; i++) {
921     str.sprintf(fmt, i);
922     SoField * f = dragger->getField(str.getString());
923     assert(f);
924     f->setDefault(TRUE);
925   }
926 }
927 
928 // Doc in superclass.
929 void
setDefaultOnNonWritingFields(void)930 SoTransformerDragger::setDefaultOnNonWritingFields(void)
931 {
932   this->surroundScale.setDefault(TRUE);
933   this->circleFeedbackAntiSquish.setDefault(TRUE);
934   this->circleFeedbackTransform.setDefault(TRUE);
935   this->axisFeedbackLocation.setDefault(TRUE);
936   this->translateBoxFeedbackRotation.setDefault(TRUE);
937 
938   SoTransformerDragger_set_default(this, "translator%dLocateGroup", 1, 6);
939   SoTransformerDragger_set_default(this, "rotator%dLocateGroup", 1, 6);
940   SoTransformerDragger_set_default(this, "scale%dLocateGroup", 1, 6);
941 
942   inherited::setDefaultOnNonWritingFields();
943 }
944 
945 /*! \COININTERNAL */
946 void
fieldSensorCB(void * d,SoSensor *)947 SoTransformerDragger::fieldSensorCB(void *d, SoSensor *)
948 {
949   SoTransformerDragger * thisp = THISP(d);
950   SbMatrix matrix = thisp->getMotionMatrix();
951   thisp->workFieldsIntoTransform(matrix);
952   thisp->setMotionMatrix(matrix);
953 }
954 
955 /*! \COININTERNAL */
956 void
valueChangedCB(void *,SoDragger * d)957 SoTransformerDragger::valueChangedCB(void *, SoDragger * d)
958 {
959   SoTransformerDragger * thisp = THISP(d);
960   SbMatrix matrix = thisp->getMotionMatrix();
961   SbVec3f trans, scale;
962   SbRotation rot, scaleOrient;
963   matrix.getTransform(trans, rot, scale, scaleOrient);
964 
965   thisp->translFieldSensor->detach();
966   if (thisp->translation.getValue() != trans)
967     thisp->translation = trans;
968   thisp->translFieldSensor->attach(&thisp->translation);
969 
970   thisp->scaleFieldSensor->detach();
971   if (thisp->scaleFactor.getValue() != scale)
972     thisp->scaleFactor = scale;
973   thisp->scaleFieldSensor->attach(&thisp->scaleFactor);
974 
975   thisp->rotateFieldSensor->detach();
976   if (thisp->rotation.getValue() != rot) {
977     thisp->rotation = rot;
978   }
979   thisp->rotateFieldSensor->attach(&thisp->rotation);
980 }
981 
982 /*!
983   Returns an indicator for the current operation executed on the
984   dragger by the end-user -- or SoTransformerDragger::INACTIVE if
985   none.
986 */
987 SoTransformerDragger::State
getCurrentState(void)988 SoTransformerDragger::getCurrentState(void)
989 {
990   return this->state;
991 }
992 
993 void
unsquishKnobs(void)994 SoTransformerDragger::unsquishKnobs(void)
995 {
996   this->updateAntiSquishList();
997 }
998 
999 SbBool
isLocateHighlighting(void)1000 SoTransformerDragger::isLocateHighlighting(void)
1001 {
1002   return PRIVATE(this)->locateHighlighting;
1003 }
1004 
1005 void
setLocateHighlighting(SbBool onoff)1006 SoTransformerDragger::setLocateHighlighting(SbBool onoff)
1007 {
1008   // FIXME: I can't see that this flag is actually used anywhere..?
1009   // 20011208 mortene.
1010   PRIVATE(this)->locateHighlighting = onoff;
1011 }
1012 
1013 void
setColinearThreshold(int newval)1014 SoTransformerDragger::setColinearThreshold(int newval)
1015 {
1016   SoTransformerDraggerP::colinearThreshold = newval;
1017 }
1018 
1019 int
getColinearThreshold(void)1020 SoTransformerDragger::getColinearThreshold(void)
1021 {
1022   return SoTransformerDraggerP::colinearThreshold;
1023 }
1024 
1025 SbVec3f
getBoxPointInWorldSpace(const SbVec3f & pointonunitbox)1026 SoTransformerDragger::getBoxPointInWorldSpace(const SbVec3f & pointonunitbox)
1027 {
1028   SbMatrix mat, inv;
1029   this->getSurroundScaleMatrices(mat, inv);
1030   mat.multRight(this->getLocalToWorldMatrix());
1031   SbVec3f ret;
1032   mat.multVecMatrix(pointonunitbox, ret);
1033   return ret;
1034 }
1035 
1036 SbVec3f
getBoxDirInWorldSpace(const SbVec3f & dironunitbox)1037 SoTransformerDragger::getBoxDirInWorldSpace(const SbVec3f & dironunitbox)
1038 {
1039   SbMatrix mat, inv;
1040   this->getSurroundScaleMatrices(mat, inv);
1041   mat.multRight(this->getLocalToWorldMatrix());
1042   SbVec3f ret;
1043   mat.multDirMatrix(dironunitbox, ret);
1044   return ret;
1045 }
1046 
1047 SbVec3f
getWorldPointInBoxSpace(const SbVec3f & pointinworldspace)1048 SoTransformerDragger::getWorldPointInBoxSpace(const SbVec3f & pointinworldspace)
1049 {
1050   SbMatrix mat, inv;
1051   this->getSurroundScaleMatrices(mat, inv);
1052   mat.multLeft(this->getWorldToLocalMatrix());
1053   SbVec3f ret;
1054   mat.multVecMatrix(pointinworldspace, ret);
1055   return ret;
1056 }
1057 
1058 SbVec2f
getWorldPointInPixelSpace(const SbVec3f & thepoint)1059 SoTransformerDragger::getWorldPointInPixelSpace(const SbVec3f & thepoint)
1060 {
1061   SbVec3f screenpt;
1062   this->getViewVolume().projectToScreen(thepoint, screenpt);
1063   return SbVec2f(screenpt[0], screenpt[1]);
1064 }
1065 
1066 SbVec3f
getInteractiveCenterInBoxSpace(void)1067 SoTransformerDragger::getInteractiveCenterInBoxSpace(void)
1068 {
1069   if (PRIVATE(this)->ctrlDown) return PRIVATE(this)->ctrlOffset;
1070   else return SbVec3f(0.0f, 0.0f, 0.0f);
1071 }
1072 
1073 /*! \COININTERNAL */
1074 void
startCB(void *,SoDragger * d)1075 SoTransformerDragger::startCB(void *, SoDragger * d)
1076 {
1077   SoTransformerDragger * thisp = THISP(d);
1078   thisp->dragStart();
1079 }
1080 
1081 /*! \COININTERNAL */
1082 void
motionCB(void *,SoDragger * d)1083 SoTransformerDragger::motionCB(void *, SoDragger * d)
1084 {
1085   SoTransformerDragger * thisp = THISP(d);
1086   thisp->drag();
1087 }
1088 
1089 /*! \COININTERNAL */
1090 void
finishCB(void *,SoDragger * d)1091 SoTransformerDragger::finishCB(void *, SoDragger * d)
1092 {
1093   SoTransformerDragger * thisp = THISP(d);
1094   thisp->dragFinish();
1095 }
1096 
1097 /*! \COININTERNAL */
1098 void
metaKeyChangeCB(void *,SoDragger * d)1099 SoTransformerDragger::metaKeyChangeCB(void *, SoDragger *d)
1100 {
1101   SoTransformerDragger * thisp = THISP(d);
1102   if (!thisp->isActive.getValue()) return;
1103 
1104   const SoEvent *event = thisp->getEvent();
1105   if (PRIVATE(thisp)->shiftDown != event->wasShiftDown()) {
1106     thisp->drag();
1107   }
1108   if (PRIVATE(thisp)->ctrlDown != event->wasCtrlDown()) {
1109     thisp->drag();
1110   }
1111 }
1112 
1113 // invalidate surround scale node, if it exists
1114 static void
invalidate_surroundscale(SoBaseKit * kit)1115 invalidate_surroundscale(SoBaseKit * kit)
1116 {
1117   SoSurroundScale * ss = coin_safe_cast<SoSurroundScale *>(
1118     kit->getPart("surroundScale", FALSE)
1119     );
1120   if (ss) ss->invalidate();
1121 }
1122 
1123 /*! \COININTERNAL
1124   Called when dragger is selected (picked) by the user.
1125 */
1126 void
dragStart(void)1127 SoTransformerDragger::dragStart(void)
1128 {
1129   invalidate_surroundscale(this);
1130 
1131   int i;
1132   const SoPath *pickpath = this->getPickPath();
1133   const SoEvent *event = this->getEvent();
1134 
1135   SbBool found = FALSE;
1136   this->state = INACTIVE;
1137 
1138   SbVec3f startpt = this->getLocalStartingPoint();
1139   startpt = this->localToWorking(startpt);
1140 
1141   SbString str;
1142   if (!found) {
1143     for (i = 1; i <= 6; i++) {
1144       str.sprintf("translator%d", i);
1145       if (pickpath->findNode(this->getNodeFieldNode(str.getString())) >= 0 ||
1146           this->getSurrogatePartPickedName() == str.getString()) break;
1147     }
1148     if (i <= 6) {
1149       found = TRUE;
1150       this->state = static_cast<State>((int(RIT_TRANSLATE) + (i-1)));
1151       PRIVATE(this)->whatkind = WHATKIND_TRANSLATE;
1152       PRIVATE(this)->whatnum = i;
1153       if (i <= 2) PRIVATE(this)->dimension = 1;
1154       else if (i <= 4) PRIVATE(this)->dimension = 0;
1155       else PRIVATE(this)->dimension = 2;
1156     }
1157   }
1158 
1159   if (!found) {
1160     for (i = 1; i <= 6; i++) {
1161       str.sprintf("rotator%d", i);
1162       if (pickpath->findNode(this->getNodeFieldNode(str.getString()))>= 0 ||
1163           this->getSurrogatePartPickedName() == str.getString()) break;
1164     }
1165     if (i <= 6) {
1166       found = TRUE;
1167       this->state = static_cast<State>((int(RIT_X_ROTATE) + (i-1)));
1168       PRIVATE(this)->whatkind = WHATKIND_ROTATE;
1169       PRIVATE(this)->whatnum = i;
1170       if (i <= 2) PRIVATE(this)->dimension = 1;
1171       else if (i <= 4) PRIVATE(this)->dimension = 0;
1172       else PRIVATE(this)->dimension = 2;
1173     }
1174   }
1175   if (!found) {
1176     for (i = 1; i <= 8; i++) {
1177       str.sprintf("scale%d", i);
1178       if (pickpath->findNode(this->getNodeFieldNode(str.getString()))>= 0 ||
1179           this->getSurrogatePartPickedName() == str.getString()) break;
1180     }
1181     if (i <= 8) {
1182       found = TRUE;
1183       this->state = static_cast<State>((int(PX_PY_PZ_3D_SCALE) + (i-1)));
1184       PRIVATE(this)->whatkind = WHATKIND_SCALE;
1185       PRIVATE(this)->whatnum = i;
1186     }
1187   }
1188   assert(found);
1189 
1190   PRIVATE(this)->ctrlDown = event->wasCtrlDown();
1191   PRIVATE(this)->shiftDown = event->wasShiftDown();
1192   PRIVATE(this)->ctrlOffset = this->calcCtrlOffset(startpt);
1193 
1194   switch(PRIVATE(this)->whatkind) {
1195   case WHATKIND_TRANSLATE:
1196     {
1197       SbVec3f n(0.0f, 0.0f, 0.0f);
1198       n[PRIVATE(this)->dimension] = 1.0f;
1199       this->planeProj->setPlane(SbPlane(n, startpt));
1200       this->lineProj->setLine(SbLine(startpt, startpt + n));
1201       PRIVATE(this)->constraintState = CONSTRAINT_OFF;
1202       if (PRIVATE(this)->shiftDown) {
1203         PRIVATE(this)->constraintState = CONSTRAINT_WAIT;
1204       }
1205       SbLine myline(SbVec3f(0.0f, 0.0f, 0.0f), n);
1206       SoTranslation *t = SO_GET_ANY_PART(this, "axisFeedbackLocation", SoTranslation);
1207       t->translation = myline.getClosestPoint(startpt);
1208 
1209       this->setAllPartSwitches(SO_SWITCH_NONE, SO_SWITCH_NONE, SO_SWITCH_NONE);
1210       str.sprintf("translator%dSwitch", PRIVATE(this)->whatnum);
1211       this->setSwitchValue(str.getString(), 1);
1212       this->setSwitchValue("translateBoxFeedbackSwitch", SO_SWITCH_ALL);
1213       SoRotation * feedbackrot = coin_assert_cast<SoRotation *>(this->getAnyPart("translateBoxFeedbackRotation", TRUE));
1214       assert(feedbackrot);
1215       switch (PRIVATE(this)->whatnum) {
1216       default:
1217       case 1:
1218         feedbackrot->rotation = SbRotation::identity();
1219         break;
1220       case 2:
1221         feedbackrot->rotation = SbRotation(SbVec3f(1.0f, 0.0f, 0.0f), float(M_PI));
1222         break;
1223       case 3:
1224         feedbackrot->rotation = SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), float(M_PI)*0.5f);
1225         break;
1226       case 4:
1227         feedbackrot->rotation = SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), -float(M_PI)*0.5f);
1228         break;
1229       case 5:
1230         feedbackrot->rotation = SbRotation(SbVec3f(1.0f, 0.0f, 0.0f), float(M_PI)*0.5f);
1231         break;
1232       case 6:
1233         feedbackrot->rotation = SbRotation(SbVec3f(1.0f, 0.0f, 0.0f), -float(M_PI)*0.5f);
1234         break;
1235       }
1236       (void) this->setDynamicTranslatorSwitches(event);
1237     }
1238     break;
1239   case WHATKIND_SCALE:
1240     {
1241       SoTranslation *t = SO_GET_ANY_PART(this, "axisFeedbackLocation", SoTranslation);
1242       t->translation = startpt;
1243       this->lineProj->setLine(SbLine(SbVec3f(0.0f, 0.0f, 0.0f), startpt));
1244       PRIVATE(this)->constraintState = CONSTRAINT_OFF;
1245       if (PRIVATE(this)->shiftDown) {
1246         PRIVATE(this)->constraintState = CONSTRAINT_WAIT;
1247       }
1248 
1249       str.sprintf("scale%dSwitch", PRIVATE(this)->whatnum);
1250       this->setAllPartSwitches(0, SO_SWITCH_NONE, SO_SWITCH_NONE);
1251       this->setSwitchValue(str.getString(), 1);
1252       (void) this->setDynamicScaleSwitches(event);
1253     }
1254     break;
1255   case WHATKIND_ROTATE:
1256     {
1257       SoTranslation *t = SO_GET_ANY_PART(this, "axisFeedbackLocation", SoTranslation);
1258       t->translation = startpt;
1259       this->sphereProj->setSphere(SbSphere(SbVec3f(0.0f, 0.0f, 0.0f), startpt.length()));
1260       this->sphereProj->setViewVolume(this->getViewVolume());
1261       this->sphereProj->setWorkingSpace(this->getWorkingToWorldMatrix());
1262 
1263       switch (this->getFrontOnProjector()) {
1264       case FRONT:
1265         this->sphereProj->setFront(TRUE);
1266         break;
1267       case BACK:
1268         this->sphereProj->setFront(TRUE);
1269         break;
1270       default: // avoid warnings
1271       case USE_PICK:
1272         this->sphereProj->setFront(this->sphereProj->isPointInFront(startpt));
1273         break;
1274       }
1275 
1276       SbVec3f projpt = this->sphereProj->project(this->getNormalizedLocaterPosition());
1277       this->getWorkingToWorldMatrix().multVecMatrix(projpt, PRIVATE(this)->prevWorldHitPt);
1278       PRIVATE(this)->prevMotionMatrix = this->getMotionMatrix();
1279 
1280       PRIVATE(this)->constraintState = CONSTRAINT_OFF;
1281       if (!PRIVATE(this)->shiftDown) {
1282         PRIVATE(this)->constraintState = CONSTRAINT_WAIT;
1283         // this plane is only used to find constraint direction
1284         this->planeProj->setPlane(SbPlane(startpt, startpt));
1285         PRIVATE(this)->normalizedStartLocaterPosition = this->getNormalizedLocaterPosition();
1286       }
1287       SoAntiSquish *squish = SO_GET_ANY_PART(this, "circleFeedbackAntiSquish", SoAntiSquish);
1288       SoAntiSquish::Sizing sizing;
1289       switch (PRIVATE(this)->dimension) {
1290       case 0: sizing = SoAntiSquish::X; break;
1291       case 1: sizing = SoAntiSquish::Y; break;
1292       case 2: sizing = SoAntiSquish::Z; break;
1293       default: assert(FALSE); sizing = SoAntiSquish::Z; // Dummy assignment to avoid compiler warning.
1294       }
1295       squish->sizing = sizing;
1296       squish->recalc();
1297       this->setAllPartSwitches(SO_SWITCH_NONE, 0, SO_SWITCH_NONE);
1298       (void) this->setDynamicRotatorSwitches(event);
1299     }
1300     break;
1301   default:
1302     assert(0 && "unknown whatkind");
1303     break;
1304   }
1305 }
1306 
1307 /*! \COININTERNAL
1308   Called when user drags the mouse after picking the dragger.
1309 */
1310 void
drag(void)1311 SoTransformerDragger::drag(void)
1312 {
1313   switch(PRIVATE(this)->whatkind) {
1314   case WHATKIND_SCALE:
1315     this->dragScale();
1316     break;
1317   case WHATKIND_TRANSLATE:
1318     this->dragTranslate();
1319     break;
1320   case WHATKIND_ROTATE:
1321     this->dragRotate();
1322     break;
1323   default:
1324     assert(0 && "illegal whatkind");
1325     break;
1326   }
1327 }
1328 
1329 void
dragTranslate()1330 SoTransformerDragger::dragTranslate()
1331 {
1332   SbVec3f startpt = this->getLocalStartingPoint();
1333   startpt = this->localToWorking(startpt);
1334 
1335   this->planeProj->setViewVolume(this->getViewVolume());
1336   this->planeProj->setWorkingSpace(this->getWorkingToWorldMatrix());
1337   SbVec3f projpt = this->planeProj->project(this->getNormalizedLocaterPosition());
1338 
1339   const SoEvent *event = this->getEvent();
1340   if (event->wasShiftDown() && PRIVATE(this)->constraintState == CONSTRAINT_OFF) {
1341     PRIVATE(this)->constraintState = CONSTRAINT_WAIT;
1342     this->setStartLocaterPosition(event->getPosition());
1343   }
1344   else if (!event->wasShiftDown() && PRIVATE(this)->constraintState != CONSTRAINT_OFF) {
1345     PRIVATE(this)->constraintState = CONSTRAINT_OFF;
1346   }
1347 
1348   // Every time something changes (shift or ctrl is pressed), resave
1349   // the state so that the transformation to come starts with blank
1350   // sheets.
1351   if (this->setDynamicTranslatorSwitches(event)) {
1352     this->saveStartParameters();
1353     SbVec3f n(0.0f, 0.0f, 0.0f);
1354     n[PRIVATE(this)->dimension] = 1.0f;
1355     this->lineProj->setLine(SbLine(projpt, projpt+n));
1356     SbVec3f worldpt;
1357     this->getWorkingToWorldMatrix().multVecMatrix(projpt, worldpt);
1358     this->setStartingPoint(worldpt);
1359     startpt = projpt;
1360   }
1361 
1362   SbVec3f motion;
1363   if (PRIVATE(this)->ctrlDown) {
1364     this->lineProj->setViewVolume(this->getViewVolume());
1365     this->lineProj->setWorkingSpace(this->getWorkingToWorldMatrix());
1366     projpt = this->lineProj->project(this->getNormalizedLocaterPosition());
1367     motion = projpt - startpt;
1368   }
1369   else {
1370     motion = projpt - startpt;
1371 
1372     switch(PRIVATE(this)->constraintState) {
1373     case CONSTRAINT_OFF:
1374       break;
1375     case CONSTRAINT_WAIT:
1376       if (this->isAdequateConstraintMotion()) {
1377         int biggest = 0;
1378         double bigval = fabs(motion[0]);
1379         if (fabs(motion[1]) > bigval) {
1380           biggest = 1;
1381           bigval = fabs(motion[1]);
1382         }
1383         if (fabs(motion[2]) > bigval) {
1384           biggest = 2;
1385         }
1386 
1387         // Set all but the constraint axis to 0
1388         motion[(biggest + 1) % 3] = 0.0f;
1389         motion[(biggest + 2) % 3] = 0.0f;
1390 
1391         PRIVATE(this)->constraintState = CONSTRAINT_X + biggest;
1392       }
1393       else {
1394         return;
1395       }
1396       break;
1397     case CONSTRAINT_X:
1398       motion[1] = 0.0f;
1399       motion[2] = 0.0f;
1400       break;
1401     case CONSTRAINT_Y:
1402       motion[0] = 0.0f;
1403       motion[2] = 0.0f;
1404       break;
1405     case CONSTRAINT_Z:
1406       motion[0] = 0.0f;
1407       motion[1] = 0.0f;
1408       break;
1409     }
1410   }
1411 
1412   SbMatrix mat, inv;
1413   this->getSurroundScaleMatrices(mat, inv);
1414   this->setMotionMatrix(this->appendTranslation(this->getStartMotionMatrix(), motion, &mat));
1415   this->unsquishKnobs();
1416 }
1417 
1418 void
dragScale()1419 SoTransformerDragger::dragScale()
1420 {
1421   SbVec3f startpt = this->getLocalStartingPoint();
1422   startpt = this->localToWorking(startpt);
1423 
1424   this->lineProj->setViewVolume(this->getViewVolume());
1425   this->lineProj->setWorkingSpace(this->getWorkingToWorldMatrix());
1426   SbVec3f projpt = this->lineProj->project(this->getNormalizedLocaterPosition());
1427   const SoEvent *event = this->getEvent();
1428 
1429   if (event->wasShiftDown() && PRIVATE(this)->constraintState == CONSTRAINT_OFF) {
1430     PRIVATE(this)->constraintState = CONSTRAINT_WAIT;
1431     this->setStartLocaterPosition(event->getPosition());
1432   }
1433   else if (!event->wasShiftDown() && PRIVATE(this)->constraintState != CONSTRAINT_OFF) {
1434     this->saveStartParameters();
1435     PRIVATE(this)->constraintState = CONSTRAINT_OFF;
1436     this->lineProj->setLine(SbLine(SbVec3f(0.0f, 0.0f, 0.0f), projpt));
1437     PRIVATE(this)->ctrlOffset = this->calcCtrlOffset(projpt);
1438     startpt = projpt;
1439     SbVec3f worldpt;
1440     this->getWorkingToWorldMatrix().multVecMatrix(projpt, worldpt);
1441     this->setStartingPoint(worldpt);
1442   }
1443 
1444   if (PRIVATE(this)->constraintState == CONSTRAINT_WAIT && this->isAdequateConstraintMotion()) {
1445     // detect which dimension user has moved mouse the most. Done by projecting
1446     // mouse positions onto the near plane, finding that world vector, and
1447     // transforming that world vector into working space.
1448     const SbViewVolume &vv = this->getViewVolume();
1449     const SbViewportRegion &vp = this->getViewportRegion();
1450     SbVec2s move = this->getLocaterPosition() - this->getStartLocaterPosition();
1451     SbVec2f normmove(
1452                    static_cast<float>(move[0])/static_cast<float>(vp.getViewportSizePixels()[0]),
1453                      static_cast<float>(move[1])/static_cast<float>(vp.getViewportSizePixels()[1])
1454                    );
1455     SbVec3f tmp = vv.getPlanePoint(vv.getNearDist(), SbVec2f(0.5f, 0.5f));
1456     SbVec3f dir = vv.getPlanePoint(vv.getNearDist(), SbVec2f(0.5f, 0.5f) + normmove);
1457     dir -= tmp;
1458     (void) dir.normalize(); // ok if null (no movement)
1459     this->getWorldToWorkingMatrix().multDirMatrix(dir, dir);
1460     int biggest = 0;
1461     double bigval = fabs(dir[0]);
1462     if (fabs(dir[1]) > bigval) {
1463       biggest = 1;
1464       bigval = fabs(dir[1]);
1465     }
1466     if (fabs(dir[2]) > bigval) {
1467       biggest = 2;
1468     }
1469     SbVec3f n(0.0f, 0.0f, 0.0f);
1470     n[biggest] = 1.0f;
1471 
1472     PRIVATE(this)->constraintState = CONSTRAINT_X + biggest;
1473 
1474     this->saveStartParameters();
1475     this->lineProj->setLine(SbLine(projpt, projpt+n));
1476     startpt = projpt;
1477     projpt[(biggest+1)%3] = 0.0f;
1478     projpt[(biggest+2)%3] = 0.0f;
1479     PRIVATE(this)->ctrlOffset = this->calcCtrlOffset(projpt);
1480     projpt = startpt;
1481     SbVec3f worldpt;
1482     this->getWorkingToWorldMatrix().multVecMatrix(projpt, worldpt);
1483     this->setStartingPoint(worldpt);
1484   }
1485 
1486   // If the control key is pressed or released,
1487   // setDynamicScaleSwitches will return TRUE. When this happens, we
1488   // have to resave our start motionmatrix to be able to do correct
1489   // scaling. If we do not do this, the scaled object will jump
1490   // because the scale center is changed, but not the scale.
1491   if (this->setDynamicScaleSwitches(event)) {
1492     this->saveStartParameters();
1493     startpt = projpt;
1494 
1495     SbVec3f worldpt;
1496     this->getWorkingToWorldMatrix().multVecMatrix(projpt, worldpt);
1497     this->setStartingPoint(worldpt);
1498   }
1499 
1500   if (PRIVATE(this)->constraintState == CONSTRAINT_WAIT) return;
1501 
1502   if (PRIVATE(this)->constraintState >= CONSTRAINT_X) {
1503     int num = PRIVATE(this)->constraintState - CONSTRAINT_X;
1504     projpt[(num+1)%3] = 0.0f;
1505     projpt[(num+2)%3] = 0.0f;
1506     startpt[(num+1)%3] = 0.0f;
1507     startpt[(num+2)%3] = 0.0f;
1508   }
1509 
1510   SbVec3f center(0.0f, 0.0f, 0.0f);
1511   if (PRIVATE(this)->ctrlDown) {
1512     center -= PRIVATE(this)->ctrlOffset;
1513   }
1514 
1515   float orglen = (startpt-center).length();
1516   float currlen = (projpt-center).length();
1517   float scale = 0.0f;
1518 
1519   if (orglen > 0.0f) scale = currlen / orglen;
1520   if (scale > 0.0f && (startpt-center).dot(projpt-center) <= 0.0f) scale = 0.0f;
1521 
1522   SbVec3f scalevec(scale, scale, scale);
1523   if (PRIVATE(this)->constraintState >= CONSTRAINT_X) {
1524     int num = PRIVATE(this)->constraintState - CONSTRAINT_X;
1525     scalevec[(num+1)%3] = 1.0f;
1526     scalevec[(num+2)%3] = 1.0f;
1527   }
1528 
1529   SbMatrix mat, inv;
1530   this->getSurroundScaleMatrices(mat, inv);
1531   this->setMotionMatrix(this->appendScale(this->getStartMotionMatrix(),
1532                                           scalevec,
1533                                           center, &mat));
1534   this->unsquishKnobs();
1535 }
1536 
1537 // The dragRotate method has been implemented somewhat differently
1538 // than the other drag functions. This is because of the unfortunate
1539 // effect that happens when a non-uniform scale has been applied to
1540 // the motion-matrix, and rotation is attempted to be done before the
1541 // scale. Both operations are perfectly valid, but the end result is
1542 // not what you'd expect; the transformation comes out sheared. To
1543 // prevent this from happening, the rotation matrices are applied in
1544 // world space, that is after all other transformations have been
1545 // applied (scale and translation are applied in local
1546 // space). What is basically done is this:
1547 //
1548 // Calculates the matrix (as done in SoDragger::appendRotation()):
1549 //
1550 // C := conversion
1551 // P := rotcenter
1552 // R := rot
1553 // M := new motion matrix
1554 // Mold := previous motionmatrix
1555 //
1556 // M = C^-1 * P^-1 * R * P * C * Mold
1557 //
1558 // What essentially happens is that we transform into C's coordinate
1559 // system, then we move the rotation center to origo and apply the
1560 // new rotation.  The rotation has now been applied, but we are in
1561 // the wrong coordinate system, so we reapply the rotation center
1562 // and the conversion. Finally we transform the matrix by the old
1563 // transformation, which gives us the new rotated transformation.
1564 //
1565 // The rotation happens in the local coordinate system of the object
1566 // if C = P = I, but if P is specified, then the rotation center
1567 // will be adjusted before the rotation is applied. If the
1568 // conversion matrix has been specified to be e.g:
1569 //
1570 // C = (Mold * W)^-1
1571 //
1572 // Then the resulting matrix looks something like this:
1573 //
1574 // M = ((Mold * W)^-1)^-1 * P^-1 * R * P * (Mold * W)^-1 * Mold
1575 //   = Mold * W * P^-1 * R * P * W^-1 * Mold^-1 * Mold
1576 //   = Mold * W * P^-1 * R * P * W^-1
1577 //
1578 // Take a closer look at the resulting matrix: It basically reverses
1579 // the transformation. Instead of having the rotation applied before
1580 // the old transformation, the rotation is applied after the old
1581 // transformation. The rotation also happens in world space - that is
1582 // the coordinate system is transformed to the world coordinate system
1583 // before the rotation is applied around rotationcenter. Finally the
1584 // matrix is transformed back to the local coordinate system.
1585 void
dragRotate(void)1586 SoTransformerDragger::dragRotate(void)
1587 {
1588   // Update the sphere projector to the current view so that it
1589   // accurately can react to events.
1590   this->sphereProj->setViewVolume(this->getViewVolume());
1591   this->sphereProj->setWorkingSpace(this->getWorkingToWorldMatrix());
1592 
1593   const SoEvent *event = this->getEvent();
1594 
1595   SbVec3f startpt, projpt;
1596   startpt = this->getLocalStartingPoint();
1597   startpt = this->localToWorking(startpt);
1598 
1599   // If shift was pressed and not in non-constrained state, then
1600   // we switch to non-constrained state.
1601   if (event->wasShiftDown() && PRIVATE(this)->constraintState != CONSTRAINT_OFF) {
1602     PRIVATE(this)->constraintState = CONSTRAINT_OFF;
1603     projpt = this->sphereProj->project(this->getNormalizedLocaterPosition());
1604     this->getWorkingToWorldMatrix().multVecMatrix(projpt, PRIVATE(this)->prevWorldHitPt);
1605     PRIVATE(this)->prevMotionMatrix = this->getMotionMatrix();
1606     this->saveStartParameters();
1607     this->setStartingPoint(PRIVATE(this)->prevWorldHitPt);
1608   }
1609   // If shift was released while in non-constrained state, then the
1610   // state jumps back to constrained.
1611   else if (!event->wasShiftDown() && PRIVATE(this)->constraintState == CONSTRAINT_OFF) {
1612     PRIVATE(this)->constraintState = CONSTRAINT_WAIT;
1613     this->setStartingPoint(PRIVATE(this)->prevWorldHitPt);
1614     startpt = this->getLocalStartingPoint();
1615     startpt = this->localToWorking(startpt);
1616     // This plane is only used to find the constraint direction
1617     this->planeProj->setPlane(SbPlane(startpt, startpt));
1618     this->setStartLocaterPosition(event->getPosition());
1619     PRIVATE(this)->normalizedStartLocaterPosition = this->getNormalizedLocaterPosition();
1620     this->saveStartParameters();
1621   }
1622 
1623   SbVec3f center(0.0f, 0.0f, 0.0f);
1624   if (PRIVATE(this)->ctrlDown) {
1625     // Adjust the center to be on the other side of the bounding box
1626     // when the control key has been pressed.
1627     center -= PRIVATE(this)->ctrlOffset * KNOB_DISTANCE;
1628   }
1629 
1630   // Show the necessary feedback geometry for rotation.
1631   (void) this->setDynamicRotatorSwitches(event);
1632 
1633   if (PRIVATE(this)->constraintState == CONSTRAINT_OFF) {
1634     SbVec3f worldcenter = this->getBoxPointInWorldSpace(center);
1635     SbVec3f prevworldprojpt = PRIVATE(this)->prevWorldHitPt;
1636     SbVec3f worldprojpt;
1637 
1638     // Find locater position in world space
1639     projpt = this->sphereProj->project(this->getNormalizedLocaterPosition());
1640     this->getWorkingToWorldMatrix().multVecMatrix(projpt, worldprojpt);
1641     SbVec3f wppt = worldprojpt;
1642 
1643     // Find the projection vectors from the box center in world space.
1644     worldprojpt -= worldcenter;
1645     prevworldprojpt -= worldcenter;
1646 
1647     // FIXME: Without this test, the rotation behaves very strange,
1648     // especially at the edge/outside of the sphere. I will
1649     // investigate this some more to understand what happens.
1650     // 20040804 jornskaa
1651 
1652     // Normalize the vectors before finding the dotproduct angle
1653     // between them.
1654     if ((worldprojpt.normalize() > 0.0f) &&
1655         (prevworldprojpt.normalize() > 0.0f) &&
1656         (worldprojpt.dot(prevworldprojpt) > 0.8f)) {
1657       PRIVATE(this)->prevWorldHitPt = wppt;
1658 
1659       // Calculate the rotation from previous prevworldprojpt to
1660       // worldprojpt
1661       SbRotation rot(prevworldprojpt, worldprojpt);
1662 
1663       // Calculate new motionmatrix by rotating in world space to
1664       // prevent shearing with non-uniform scale.
1665       SbMatrix mat = this->getWorldToLocalMatrix();
1666       PRIVATE(this)->prevMotionMatrix = this->appendRotation(PRIVATE(this)->prevMotionMatrix, rot,
1667                                                     worldcenter, &mat);
1668 
1669       this->setMotionMatrix(PRIVATE(this)->prevMotionMatrix);
1670     }
1671   }
1672   else if (PRIVATE(this)->constraintState == CONSTRAINT_WAIT && this->isAdequateConstraintMotion()) {
1673     this->planeProj->setViewVolume(this->getViewVolume());
1674     this->planeProj->setWorkingSpace(this->getWorkingToWorldMatrix());
1675     projpt = this->planeProj->project(this->getNormalizedLocaterPosition());
1676 
1677     SbVec3f diff = projpt - startpt;
1678     diff[PRIVATE(this)->dimension] = 0.0f;
1679 
1680     int biggest = 0;
1681     double bigval = fabs(diff[0]);
1682     if (fabs(diff[1]) > bigval) {
1683       biggest = 1;
1684       bigval = fabs(diff[1]);
1685     }
1686     if (fabs(diff[2]) > bigval) {
1687       biggest = 2;
1688     }
1689     PRIVATE(this)->constraintState = CONSTRAINT_X + biggest;
1690     SbVec3f n(0.0f, 0.0f, 0.0f);
1691     n[biggest] = 1.0f;
1692     SbVec3f dim(0.0f, 0.0f, 0.0f);
1693     dim[PRIVATE(this)->dimension] = 1.0f;
1694 
1695     // set plane to do disc-rotate in
1696     this->planeProj->setPlane(SbPlane(SbVec3f(0.0f, 0.0f, 0.0f), dim, dim+n));
1697     (void) this->setDynamicRotatorSwitches(event);
1698 
1699     // Initialize prevWorldHitPt and prevMotionMatrix.
1700     projpt = this->planeProj->project(PRIVATE(this)->normalizedStartLocaterPosition);
1701     this->getWorkingToWorldMatrix().multVecMatrix(projpt, PRIVATE(this)->prevWorldHitPt);
1702     PRIVATE(this)->prevMotionMatrix = this->getMotionMatrix();
1703   }
1704   if (PRIVATE(this)->constraintState >= CONSTRAINT_X) {
1705     this->planeProj->setViewVolume(this->getViewVolume());
1706     this->planeProj->setWorkingSpace(this->getWorkingToWorldMatrix());
1707 
1708     SbVec3f worldcenter = this->getBoxPointInWorldSpace(center);
1709     SbVec3f prevworldprojpt;
1710     SbVec3f prevprojpt;
1711     SbVec3f worldprojpt;
1712 
1713     prevworldprojpt = PRIVATE(this)->prevWorldHitPt;
1714 
1715     // Find current locater position projected onto the plane in world
1716     // space.
1717     projpt = this->planeProj->project(this->getNormalizedLocaterPosition());
1718     this->getWorkingToWorldMatrix().multVecMatrix(projpt, worldprojpt);
1719 
1720     // Project the vectors onto the projection plane in world space.
1721     // This is needed to make the rotation vectors more numerically
1722     // stable especially when the dragger has been scaled to be very
1723     // small before rotation is attempted.
1724     SbPlane plane = this->planeProj->getPlane();
1725     plane.transform(this->getWorkingToWorldMatrix());
1726     prevworldprojpt -= plane.getNormal() * plane.getDistance(prevworldprojpt);
1727     worldprojpt -= plane.getNormal() * plane.getDistance(worldprojpt);
1728 
1729     // Save the values for the new projection point before it is
1730     // altered.
1731     SbVec3f wppt = worldprojpt;
1732 
1733     // Adjust the rotation vectors of the dragger to match the center
1734     // of the dragger in working space and world space.
1735     projpt -= center;
1736     prevworldprojpt -= worldcenter;
1737     worldprojpt -= worldcenter;
1738 
1739     // Make sure the length of the projected vector is greater than
1740     // a scalar constant. This ensures that the rotation is defined
1741     // and there is little room for error that might happen when the
1742     // vectors come very close to the rotation center.
1743     if (projpt.sqrLength() > 0.1f) {
1744       // Since we are using incremental changes, the changes will
1745       // never be very big, and the dot product between the
1746       // rotation-vectors should always be above zero, and never
1747       // negative because that indicates the angle between the vectors
1748       // is above PI/2. This might happen if dragging is done over the
1749       // rotation center, but we do not allow that and just wait until
1750       // the locater comes into the valid range before rotating.
1751 
1752       // Normalize the vectors before finding the dotproduct angle
1753       // between them.
1754       if ((prevworldprojpt.normalize() > 0.0f) &&
1755           (worldprojpt.normalize() > 0.0f) &&
1756           (prevworldprojpt.dot(worldprojpt) > 0.3f)) { // 0.3 == 72.5degrees
1757         PRIVATE(this)->prevWorldHitPt = wppt;
1758 
1759         // Rotate between the two points in the plane
1760         SbRotation rot(prevworldprojpt, worldprojpt);
1761 
1762         // Rotate in world space to prevent shearing when having
1763         // non-uniform scale.
1764         SbMatrix mat = this->getWorldToLocalMatrix();
1765         PRIVATE(this)->prevMotionMatrix = (this->appendRotation(PRIVATE(this)->prevMotionMatrix, rot,
1766                                                        worldcenter, &mat));
1767 
1768         this->setMotionMatrix(PRIVATE(this)->prevMotionMatrix);
1769       }
1770     }
1771   }
1772   this->unsquishKnobs();
1773 }
1774 
1775 /*! \COININTERNAL
1776   Called when mouse button is released after picking and interacting
1777   with the dragger.
1778 */
1779 void
dragFinish(void)1780 SoTransformerDragger::dragFinish(void)
1781 {
1782   switch (PRIVATE(this)->whatkind) {
1783   case WHATKIND_TRANSLATE:
1784     this->setSwitchValue("translateBoxFeedbackSwitch", SO_SWITCH_NONE);
1785     break;
1786   case WHATKIND_ROTATE:
1787   this->setSwitchValue("xCircleFeedbackSwitch", SO_SWITCH_NONE);
1788   this->setSwitchValue("yCircleFeedbackSwitch", SO_SWITCH_NONE);
1789   this->setSwitchValue("zCircleFeedbackSwitch", SO_SWITCH_NONE);
1790     break;
1791   case WHATKIND_SCALE:
1792     this->setSwitchValue("radialFeedbackSwitch", SO_SWITCH_NONE);
1793     this->setSwitchValue("posXWallFeedbackSwitch", SO_SWITCH_NONE);
1794     this->setSwitchValue("negXWallFeedbackSwitch", SO_SWITCH_NONE);
1795     this->setSwitchValue("posYWallFeedbackSwitch", SO_SWITCH_NONE);
1796     this->setSwitchValue("negYWallFeedbackSwitch", SO_SWITCH_NONE);
1797     this->setSwitchValue("posZWallFeedbackSwitch", SO_SWITCH_NONE);
1798     this->setSwitchValue("negZWallFeedbackSwitch", SO_SWITCH_NONE);
1799     this->setSwitchValue("scaleBoxFeedbackSwitch", SO_SWITCH_NONE);
1800     break;
1801   default:
1802     assert(0 && "unknown whatkind");
1803     break;
1804   }
1805 
1806   PRIVATE(this)->whatkind = WHATKIND_NONE;
1807   this->state = INACTIVE;
1808   PRIVATE(this)->constraintState = CONSTRAINT_OFF;
1809   this->setAllPartSwitches(0,0,0);
1810   this->setSwitchValue("xAxisFeedbackSwitch", SO_SWITCH_NONE);
1811   this->setSwitchValue("yAxisFeedbackSwitch", SO_SWITCH_NONE);
1812   this->setSwitchValue("zAxisFeedbackSwitch", SO_SWITCH_NONE);
1813 
1814 #if COIN_DEBUG && 0 // used to debug motion matrix (pederb, 20000225)
1815   SbMatrix m = this->getMotionMatrix();
1816   SbRotation r,so;
1817   SbVec3f t,s;
1818 
1819   fprintf(stderr,"motion matrix:\n");
1820   m.print(stderr);
1821   m.getTransform(t, r, s, so);
1822   SbVec3f rx, sox;
1823   float ra, soa;
1824   r.getValue(rx, ra);
1825   so.getValue(sox, soa);
1826   fprintf(stderr,
1827           "\nt: %g %g %g\n"
1828           "r: %g %g %g, %g\n"
1829           "s: %g %g %g\n"
1830           "so: %g %g %g, %g\n\n",
1831           t[0], t[1], t[2],
1832           rx[0], rx[1], rx[2], ra,
1833           s[0], s[1], s[2],
1834           sox[0], sox[1], sox[2], soa);
1835 #endif // debug code
1836   invalidate_surroundscale(this);
1837 }
1838 
1839 void
updateAntiSquishList(void)1840 SoTransformerDragger::updateAntiSquishList(void)
1841 {
1842   if (this->antiSquishList.getLength() == 0) {
1843     SoSeparator *top = SO_GET_ANY_PART(this, "topSeparator", SoSeparator);
1844     assert(top);
1845 
1846     SoSearchAction sa;
1847     sa.setInterest(SoSearchAction::ALL);
1848     sa.setType(SoAntiSquish::getClassTypeId());
1849     sa.setSearchingAll(TRUE);
1850     sa.apply(top);
1851 
1852     SoPathList &pl = sa.getPaths();
1853     for (int i = 0; i < pl.getLength(); i++) {
1854       SoFullPath * path = reclassify_cast<SoFullPath *>(pl[i]);
1855       SoNode * tail = path->getTail();
1856       int j, n = this->antiSquishList.getLength();
1857       for (j = 0; j < n; j++) {
1858         if (this->antiSquishList[j] == tail) break;
1859       }
1860       if (j == n)
1861         this->antiSquishList.append(path->getTail());
1862     }
1863   }
1864   int n = this->antiSquishList.getLength();
1865   for (int i = 0; i < n; i++) {
1866     SoAntiSquish * squishy = coin_assert_cast<SoAntiSquish *>(this->antiSquishList[i]);
1867     squishy->recalc();
1868   }
1869 }
1870 
1871 void
setAllPartSwitches(int scalewhich,int rotatewhich,int translatewhich)1872 SoTransformerDragger::setAllPartSwitches(int scalewhich, int rotatewhich, int translatewhich)
1873 {
1874   int i;
1875   SoSwitch *sw;
1876   SbString str;
1877 
1878   for (i = 1; i <= 6; i++) {
1879     str.sprintf("translator%dSwitch", i);
1880     sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
1881     SoInteractionKit::setSwitchValue(sw, translatewhich);
1882   }
1883   for (i = 1; i <= 6; i++) {
1884     str.sprintf("rotator%dSwitch", i);
1885     sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
1886     SoInteractionKit::setSwitchValue(sw, rotatewhich);
1887   }
1888   for (i = 1; i <= 8; i++) {
1889     str.sprintf("scale%dSwitch", i);
1890     sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
1891     SoInteractionKit::setSwitchValue(sw, scalewhich);
1892   }
1893 }
1894 
1895 /*!
1896   Not implemented in Coin; should probably not have been public in the
1897   Open Inventor API.  We'll consider to implement it if requested.
1898 */
1899 int
getMouseGestureDirection(SbBool COIN_UNUSED_ARG (x_ok),SbBool COIN_UNUSED_ARG (y_ok),SbBool COIN_UNUSED_ARG (z_ok))1900 SoTransformerDragger::getMouseGestureDirection(SbBool COIN_UNUSED_ARG(x_ok), SbBool COIN_UNUSED_ARG(y_ok), SbBool COIN_UNUSED_ARG(z_ok))
1901 {
1902   COIN_OBSOLETED();
1903   return -1;
1904 }
1905 
1906 /*!
1907   Not implemented in Coin; should probably not have been public in the
1908   Open Inventor API.  We'll consider to implement it if requested.
1909 */
1910 int
getIgnoreAxis(SbVec2f COIN_UNUSED_ARG (axis[3][2]),SbBool COIN_UNUSED_ARG (x_ok),SbBool COIN_UNUSED_ARG (y_ok),SbBool COIN_UNUSED_ARG (z_ok))1911 SoTransformerDragger::getIgnoreAxis(SbVec2f COIN_UNUSED_ARG(axis[3][2]), SbBool COIN_UNUSED_ARG(x_ok), SbBool COIN_UNUSED_ARG(y_ok), SbBool COIN_UNUSED_ARG(z_ok))
1912 {
1913   COIN_OBSOLETED();
1914   return -1;
1915 }
1916 
1917 /*!
1918   Not implemented in Coin; should probably not have been public in the
1919   Open Inventor API.  We'll consider to implement it if requested.
1920 */
1921 void
makeMinorAxisPerpendicularIfColinear(SbVec2f COIN_UNUSED_ARG (origin),SbVec2f COIN_UNUSED_ARG (axisends[3][2]),int COIN_UNUSED_ARG (index_a),int COIN_UNUSED_ARG (index_b))1922 SoTransformerDragger::makeMinorAxisPerpendicularIfColinear(SbVec2f COIN_UNUSED_ARG(origin), SbVec2f COIN_UNUSED_ARG(axisends[3][2]), int COIN_UNUSED_ARG(index_a), int COIN_UNUSED_ARG(index_b))
1923 {
1924   COIN_OBSOLETED();
1925 }
1926 
1927 /*!
1928   Not implemented in Coin; should probably not have been public in the
1929   Open Inventor API.  We'll consider to implement it if requested.
1930 */
1931 SbBool
isColinear(SbVec2f COIN_UNUSED_ARG (a1[2]),SbVec2f COIN_UNUSED_ARG (a2[2]),int COIN_UNUSED_ARG (pixels))1932 SoTransformerDragger::isColinear(SbVec2f COIN_UNUSED_ARG(a1[2]), SbVec2f COIN_UNUSED_ARG(a2[2]), int COIN_UNUSED_ARG(pixels))
1933 {
1934   COIN_OBSOLETED();
1935   return FALSE;
1936 }
1937 
1938 void
getSurroundScaleMatrices(SbMatrix & mat,SbMatrix & inv)1939 SoTransformerDragger::getSurroundScaleMatrices(SbMatrix & mat, SbMatrix & inv)
1940 {
1941   if (this->surroundScale.getValue()) {
1942     this->getPartToLocalMatrix("surroundScale", mat, inv);
1943   }
1944   else {
1945     mat = inv = SbMatrix::identity();
1946   }
1947 }
1948 
1949 SoNode *
getNodeFieldNode(const char * fieldname)1950 SoTransformerDragger::getNodeFieldNode(const char *fieldname)
1951 {
1952   SoField *field = this->getField(fieldname);
1953   assert(field != NULL);
1954   assert(field->isOfType(SoSFNode::getClassTypeId()));
1955   assert(coin_assert_cast<SoSFNode *>(field)->getValue() != NULL);
1956   return coin_assert_cast<SoSFNode *>(field)->getValue();
1957 }
1958 
1959 SbMatrix
getWorkingToWorldMatrix()1960 SoTransformerDragger::getWorkingToWorldMatrix()
1961 {
1962   SbMatrix mat, inv;
1963   this->getSurroundScaleMatrices(mat, inv);
1964   mat.multRight(this->getLocalToWorldMatrix());
1965   return mat;
1966 }
1967 
1968 SbMatrix
getWorldToWorkingMatrix(void)1969 SoTransformerDragger::getWorldToWorkingMatrix(void)
1970 {
1971   SbMatrix mat, inv;
1972   this->getSurroundScaleMatrices(mat, inv);
1973   mat.multLeft(this->getWorldToLocalMatrix());
1974   return mat;
1975 }
1976 
1977 SbVec3f
localToWorking(const SbVec3f & v)1978 SoTransformerDragger::localToWorking(const SbVec3f &v)
1979 {
1980   SbMatrix mat, inv;
1981   this->getSurroundScaleMatrices(mat, inv);
1982   SbVec3f ret;
1983   inv.multVecMatrix(v, ret);
1984   return ret;
1985 }
1986 
1987 SbVec3f
workingToLocal(const SbVec3f & v)1988 SoTransformerDragger::workingToLocal(const SbVec3f &v)
1989 {
1990   SbMatrix mat, inv;
1991   this->getSurroundScaleMatrices(mat, inv);
1992   SbVec3f ret;
1993   mat.multVecMatrix(v, ret);
1994   return ret;
1995 }
1996 
1997 SbVec3f
calcCtrlOffset(const SbVec3f & startpt)1998 SoTransformerDragger::calcCtrlOffset(const SbVec3f &startpt)
1999 {
2000   SbVec3f v = startpt;
2001   for (int i = 0; i < 3; i++) {
2002     if (v[i] < -0.8) v[i] = -1.0f;
2003     else if (v[i] > 0.8) v[i] = 1.0f;
2004     else v[i] = 0.0f;
2005   }
2006   return v;
2007 }
2008 
2009 void
setSwitchValue(const char * str,const int which)2010 SoTransformerDragger::setSwitchValue(const char *str, const int which)
2011 {
2012   SoSwitch *sw = SO_GET_ANY_PART(this, str, SoSwitch);
2013   SoInteractionKit::setSwitchValue(sw, which);
2014 }
2015 
2016 SbBool
setDynamicTranslatorSwitches(const SoEvent * event)2017 SoTransformerDragger::setDynamicTranslatorSwitches(const SoEvent *event)
2018 {
2019   SbBool changed = FALSE;
2020   if (PRIVATE(this)->ctrlDown != event->wasCtrlDown()) {
2021     changed = TRUE;
2022     PRIVATE(this)->ctrlDown = !PRIVATE(this)->ctrlDown;
2023   }
2024   if (PRIVATE(this)->shiftDown != event->wasShiftDown()) {
2025     changed = TRUE;
2026     PRIVATE(this)->shiftDown = !PRIVATE(this)->shiftDown;
2027   }
2028 
2029   SbString str;
2030 
2031   if (PRIVATE(this)->constraintState >= CONSTRAINT_X) {
2032     int which = PRIVATE(this)->constraintState - CONSTRAINT_X;
2033     str.sprintf("%cAxisFeedbackSwitch", 'x' + which);
2034     this->setSwitchValue(str.getString(), 0);
2035     str.sprintf("%cAxisFeedbackSwitch", 'x' + (which+1)%3);
2036     this->setSwitchValue(str.getString(), SO_SWITCH_NONE);
2037     str.sprintf("%cAxisFeedbackSwitch", 'x' + (which+2)%3);
2038     this->setSwitchValue(str.getString(), SO_SWITCH_NONE);
2039   }
2040   else {
2041     str.sprintf("%cAxisFeedbackSwitch", 'x' + PRIVATE(this)->dimension);
2042     this->setSwitchValue(str.getString(), PRIVATE(this)->ctrlDown ? 0 : SO_SWITCH_NONE);
2043     int val = PRIVATE(this)->shiftDown ? 1 : 0;
2044     if (PRIVATE(this)->ctrlDown) val = SO_SWITCH_NONE;
2045     str.sprintf("%cAxisFeedbackSwitch", 'x' + (PRIVATE(this)->dimension+1)%3);
2046     this->setSwitchValue(str.getString(), val);
2047     str.sprintf("%cAxisFeedbackSwitch", 'x' + (PRIVATE(this)->dimension+2)%3);
2048     this->setSwitchValue(str.getString(), val);
2049   }
2050   return changed;
2051 }
2052 
2053 SbBool
setDynamicScaleSwitches(const SoEvent * event)2054 SoTransformerDragger::setDynamicScaleSwitches(const SoEvent *event)
2055 {
2056   SbBool changed = FALSE;
2057   if (PRIVATE(this)->ctrlDown != event->wasCtrlDown()) {
2058     changed = TRUE;
2059     PRIVATE(this)->ctrlDown = !PRIVATE(this)->ctrlDown;
2060   }
2061   if (PRIVATE(this)->shiftDown != event->wasShiftDown()) {
2062     changed = TRUE;
2063     PRIVATE(this)->shiftDown = !PRIVATE(this)->shiftDown;
2064   }
2065   if (PRIVATE(this)->constraintState == CONSTRAINT_WAIT) {
2066     this->setSwitchValue("xAxisFeedbackSwitch", 1);
2067     this->setSwitchValue("yAxisFeedbackSwitch", 1);
2068     this->setSwitchValue("zAxisFeedbackSwitch", 1);
2069     this->setSwitchValue("radialFeedbackSwitch", SO_SWITCH_NONE);
2070   }
2071   else if (PRIVATE(this)->constraintState >= CONSTRAINT_X) {
2072     int which = PRIVATE(this)->constraintState - CONSTRAINT_X;
2073     SbString str;
2074     str.sprintf("%cAxisFeedbackSwitch", 'x' + which);
2075     this->setSwitchValue(str.getString(), 0);
2076     str.sprintf("%cAxisFeedbackSwitch", 'x' + (which+1)%3);
2077     this->setSwitchValue(str.getString(), SO_SWITCH_NONE);
2078     str.sprintf("%cAxisFeedbackSwitch", 'x' + (which+2)%3);
2079     this->setSwitchValue(str.getString(), SO_SWITCH_NONE);
2080     this->setSwitchValue("radialFeedbackSwitch", SO_SWITCH_NONE);
2081   }
2082   else {
2083     this->setSwitchValue("xAxisFeedbackSwitch", SO_SWITCH_NONE);
2084     this->setSwitchValue("yAxisFeedbackSwitch", SO_SWITCH_NONE);
2085     this->setSwitchValue("zAxisFeedbackSwitch", SO_SWITCH_NONE);
2086     this->setSwitchValue("radialFeedbackSwitch", 0);
2087   }
2088 
2089   this->setSwitchValue("scaleBoxFeedbackSwitch", PRIVATE(this)->shiftDown ? 0 : SO_SWITCH_NONE);
2090 
2091   if (PRIVATE(this)->ctrlDown) {
2092     SbVec3f pt = this->getLocalStartingPoint();
2093     if (PRIVATE(this)->constraintState >= CONSTRAINT_X) {
2094       int num = PRIVATE(this)->constraintState - CONSTRAINT_X;
2095       pt[(num+1)%3] = 0.0f;
2096       pt[(num+2)%3] = 0.0f;
2097     }
2098     SbString str;
2099     for (int i = 0; i < 3; i++) {
2100       str.sprintf("pos%cWallFeedbackSwitch", 'X' + i);
2101       this->setSwitchValue(str.getString(), pt[i] < 0.0f ? 0 : SO_SWITCH_NONE);
2102       str.sprintf("neg%cWallFeedbackSwitch", 'X' + i);
2103       this->setSwitchValue(str.getString(), pt[i] > 0.0f ? 0 : SO_SWITCH_NONE);
2104     }
2105   }
2106   else {
2107     this->setSwitchValue("posXWallFeedbackSwitch", SO_SWITCH_NONE);
2108     this->setSwitchValue("negXWallFeedbackSwitch", SO_SWITCH_NONE);
2109     this->setSwitchValue("posYWallFeedbackSwitch", SO_SWITCH_NONE);
2110     this->setSwitchValue("negYWallFeedbackSwitch", SO_SWITCH_NONE);
2111     this->setSwitchValue("posZWallFeedbackSwitch", SO_SWITCH_NONE);
2112     this->setSwitchValue("negZWallFeedbackSwitch", SO_SWITCH_NONE);
2113   }
2114   return changed;
2115 }
2116 
2117 
2118 SbBool
setDynamicRotatorSwitches(const SoEvent * event)2119 SoTransformerDragger::setDynamicRotatorSwitches(const SoEvent *event)
2120 {
2121   SbBool changed = FALSE;
2122   if (PRIVATE(this)->ctrlDown != event->wasCtrlDown()) {
2123     changed = TRUE;
2124     PRIVATE(this)->ctrlDown = !PRIVATE(this)->ctrlDown;
2125   }
2126   if (PRIVATE(this)->shiftDown != event->wasShiftDown()) {
2127     changed = TRUE;
2128     PRIVATE(this)->shiftDown = !PRIVATE(this)->shiftDown;
2129   }
2130 
2131   SbString str;
2132   {
2133     int axis0 = PRIVATE(this)->whatnum-1;
2134     int axis1 = (axis0 & 1) ? axis0 - 1 : axis0 + 1;
2135 
2136     str.sprintf("rotator%dSwitch", axis0 + 1);
2137     this->setSwitchValue(str.getString(), 1);
2138     str.sprintf("rotator%dSwitch", axis1 + 1);
2139     this->setSwitchValue(str.getString(), PRIVATE(this)->ctrlDown ? 0 : 1);
2140   }
2141 
2142   int axisval[3];
2143   int circleval[3];
2144   int dim = PRIVATE(this)->dimension;
2145 
2146   if (PRIVATE(this)->constraintState == CONSTRAINT_WAIT) {
2147     axisval[dim] = SO_SWITCH_NONE;
2148     axisval[(dim+1)%3] = 1;
2149     axisval[(dim+2)%3] = 1;
2150     circleval[dim] = SO_SWITCH_NONE;
2151     circleval[(dim+1)%3] = 0;
2152     circleval[(dim+2)%3] = 0;
2153   }
2154   else if (PRIVATE(this)->constraintState >= CONSTRAINT_X) {
2155     dim = PRIVATE(this)->constraintState - CONSTRAINT_X;
2156     axisval[dim] = 0;
2157     axisval[(dim+1)%3] = SO_SWITCH_NONE;
2158     axisval[(dim+2)%3] = SO_SWITCH_NONE;
2159 
2160     const SbVec3f &n = this->planeProj->getPlane().getNormal();
2161     circleval[0] = n[0] != 0.0f ? 0 : SO_SWITCH_NONE;
2162     circleval[1] = n[1] != 0.0f ? 0 : SO_SWITCH_NONE;
2163     circleval[2] = n[2] != 0.0f ? 0 : SO_SWITCH_NONE;
2164   }
2165   else {
2166     circleval[0] = 0;
2167     circleval[1] = 0;
2168     circleval[2] = 0;
2169     axisval[0] = SO_SWITCH_NONE;
2170     axisval[1] = SO_SWITCH_NONE;
2171     axisval[2] = SO_SWITCH_NONE;
2172   }
2173   if (PRIVATE(this)->ctrlDown) {
2174     SoTransform *transform = SO_GET_ANY_PART(this, "circleFeedbackTransform", SoTransform);
2175     SbVec3f offset = -PRIVATE(this)->ctrlOffset * KNOB_DISTANCE;
2176     if (transform->translation.getValue() != offset)
2177       transform->translation = offset;
2178     if (transform->scaleFactor.getValue() != SbVec3f(2.0f, 2.0f, 2.0f))
2179       transform->scaleFactor = SbVec3f(2.0f, 2.0f, 2.0f);
2180     this->setSwitchValue("circleFeedbackTransformSwitch", SO_SWITCH_ALL);
2181   }
2182   else {
2183     this->setSwitchValue("circleFeedbackTransformSwitch", 0);
2184   }
2185 
2186   this->setSwitchValue("xAxisFeedbackSwitch", axisval[0]);
2187   this->setSwitchValue("yAxisFeedbackSwitch", axisval[1]);
2188   this->setSwitchValue("zAxisFeedbackSwitch", axisval[2]);
2189   this->setSwitchValue("xCircleFeedbackSwitch", circleval[0]);
2190   this->setSwitchValue("yCircleFeedbackSwitch", circleval[1]);
2191   this->setSwitchValue("zCircleFeedbackSwitch", circleval[2]);
2192 
2193   return changed;
2194 }
2195 
2196 
2197 // Undefine these again, as some of them are also used in other
2198 // dragger sourcecode files (which causes trouble when using the
2199 // compact build hack where all .cpp files are included into all.cpp).
2200 
2201 #undef WHATKIND_NONE
2202 #undef WHATKIND_SCALE
2203 #undef WHATKIND_TRANSLATE
2204 #undef WHATKIND_ROTATE
2205 
2206 #undef CONSTRAINT_OFF
2207 #undef CONSTRAINT_WAIT
2208 #undef CONSTRAINT_X
2209 #undef CONSTRAINT_Y
2210 #undef CONSTRAINT_Z
2211 
2212 #undef KNOB_DISTANCE
2213 
2214 #undef PRIVATE
2215 #undef THISP
2216 
2217 #ifdef COIN_TEST_SUITE
2218 
2219 #include <Inventor/SbDict.h>
2220 #include <Inventor/actions/SoCallbackAction.h>
2221 #include <Inventor/nodes/SoSeparator.h>
2222 
2223 static
2224 SoCallbackAction::Response
register_cb(void * data,SoCallbackAction * action,const SoNode * node)2225 register_cb(void * data, SoCallbackAction * action, const SoNode * node)
2226 {
2227   assert(data);
2228   SbDict * dict = static_cast<SbDict *>(data);
2229   dict->enter(reinterpret_cast<uintptr_t>(node), NULL);
2230   return SoCallbackAction::CONTINUE;
2231 }
2232 
2233 static
2234 void
ensure_unique_cb(uintptr_t entry,void * value,void * data)2235 ensure_unique_cb(uintptr_t entry, void * value, void * data)
2236 {
2237   SbDict * copydict = static_cast<SbDict *>(data);
2238   void * val = NULL;
2239   BOOST_ASSERT(!copydict->find(entry, val));
2240 }
2241 
BOOST_AUTO_TEST_CASE(dragger_deep_copy)2242 BOOST_AUTO_TEST_CASE(dragger_deep_copy)
2243 {
2244   SbDict origdict, copydict;
2245 
2246   SoSeparator * root = new SoSeparator;
2247   root->setName("dragger_deep_copy_root");
2248   root->ref();
2249   root->addChild(new SoTransformerDragger);
2250 
2251   SoSeparator * copy = static_cast<SoSeparator *>(root->copy());
2252   assert(copy);
2253   copy->setName("dragger_deep_copy_copy");
2254   copy->ref();
2255 
2256   {
2257     SoCallbackAction cba;
2258     cba.setCallbackAll(TRUE);
2259 
2260     cba.addPreCallback(SoNode::getClassTypeId(), register_cb, &origdict);
2261     cba.apply(root);
2262   }
2263 
2264   {
2265     SoCallbackAction cba;
2266     cba.setCallbackAll(TRUE);
2267 
2268     cba.addPreCallback(SoNode::getClassTypeId(), register_cb, &copydict);
2269     cba.apply(copy);
2270   }
2271 
2272   SbPList keys, values;
2273 
2274   origdict.makePList(keys, values);
2275   const int origdictsize = keys.getLength();
2276 
2277   keys.truncate(0);
2278   values.truncate(0);
2279   copydict.makePList(keys, values);
2280   const int copydictsize = keys.getLength();
2281 
2282   BOOST_ASSERT(origdictsize == copydictsize);
2283 
2284   // make sure pointer sets have an empty union
2285   origdict.applyToAll(ensure_unique_cb, &copydict);
2286 
2287   root->unref();
2288   copy->unref();
2289 }
2290 
2291 #endif // COIN_TEST_SUITE
2292 
2293 #endif // HAVE_DRAGGERS
2294