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 SoRotateSphericalDragger SoRotateSphericalDragger.h Inventor/draggers/SoRotateSphericalDragger.h
41   \brief The SoRotateSphericalDragger class is for rotating geometry in any direction.
42 
43   \ingroup draggers
44 
45   \DRAGGER_DEFAULT_SCREENSHOT
46 
47   <center>
48   \image html rotatespherical.png "Screen Shot of Default Dragger"
49   </center>
50 
51   Use an instance of this dragger class in your scenegraph to let the
52   end-users of your application rotate geometry freely in any
53   direction.
54 
55   For the initial dragger orientation and the dragger geometry
56   positioning itself, use some kind of transformation node in your
57   scenegraph, as usual.
58 */
59 
60 #include <Inventor/draggers/SoRotateSphericalDragger.h>
61 
62 #include <cstring>
63 
64 #include <Inventor/nodes/SoSeparator.h>
65 #include <Inventor/nodes/SoSwitch.h>
66 #include <Inventor/projectors/SbSpherePlaneProjector.h>
67 #include <Inventor/sensors/SoFieldSensor.h>
68 
69 #include <data/draggerDefaults/rotateSphericalDragger.h>
70 
71 #include "nodekits/SoSubKitP.h"
72 #include "coindefs.h"
73 #include "SbBasicP.h"
74 
75 /*!
76   \var SoSFRotation SoRotateSphericalDragger::rotation
77 
78   This field is continuously updated to contain the rotation of the
79   current direction vector of the dragger.
80 
81   The application programmer using this dragger in his scenegraph
82   should connect the relevant node fields in the scene to this field
83   to make them follow the dragger orientation.
84 */
85 
86 /*!
87   \var SoFieldSensor * SoRotateSphericalDragger::fieldSensor
88   \COININTERNAL
89 */
90 /*!
91   \var SbSphereProjector * SoRotateSphericalDragger::sphereProj
92   \COININTERNAL
93 */
94 /*!
95   \var SbBool SoRotateSphericalDragger::userProj
96   \COININTERNAL
97 */
98 /*!
99   \var SbMatrix SoRotateSphericalDragger::prevMotionMatrix
100   \COININTERNAL
101 */
102 /*!
103   \var SbVec3f SoRotateSphericalDragger::prevWorldHitPt
104   \COININTERNAL
105 */
106 
107 #define THISP(d) static_cast<SoRotateSphericalDragger *>(d)
108 
109 class SoRotateSphericalDraggerP {
110 public:
111 };
112 
113 SO_KIT_SOURCE(SoRotateSphericalDragger);
114 
115 
116 // doc in superclass
117 void
initClass(void)118 SoRotateSphericalDragger::initClass(void)
119 {
120   SO_KIT_INTERNAL_INIT_CLASS(SoRotateSphericalDragger, SO_FROM_INVENTOR_1);
121 }
122 
123 // FIXME: document which parts need to be present in the geometry
124 // scenegraph, and what role they play in the dragger. 20010913 mortene.
125 /*!
126   \DRAGGER_CONSTRUCTOR
127 
128   \NODEKIT_PRE_DIAGRAM
129 
130   \verbatim
131   CLASS SoRotateSphericalDragger
132   -->"this"
133         "callbackList"
134         "topSeparator"
135            "motionMatrix"
136            "geomSeparator"
137   -->         "rotatorSwitch"
138   -->            "rotator"
139   -->            "rotatorActive"
140   -->         "feedbackSwitch"
141   -->            "feedback"
142   -->            "feedbackActive"
143   \endverbatim
144 
145   \NODEKIT_POST_DIAGRAM
146 
147 
148   \NODEKIT_PRE_TABLE
149 
150   \verbatim
151   CLASS SoRotateSphericalDragger
152   PVT   "this",  SoRotateSphericalDragger  ---
153         "callbackList",  SoNodeKitListPart [ SoCallback, SoEventCallback ]
154   PVT   "topSeparator",  SoSeparator  ---
155   PVT   "motionMatrix",  SoMatrixTransform  ---
156   PVT   "geomSeparator",  SoSeparator  ---
157   PVT   "rotatorSwitch",  SoSwitch  ---
158         "rotator",  SoSeparator  ---
159         "rotatorActive",  SoSeparator  ---
160   PVT   "feedbackSwitch",  SoSwitch  ---
161         "feedback",  SoSeparator  ---
162         "feedbackActive",  SoSeparator  ---
163   \endverbatim
164 
165   \NODEKIT_POST_TABLE
166 */
SoRotateSphericalDragger(void)167 SoRotateSphericalDragger::SoRotateSphericalDragger(void)
168 {
169   SO_KIT_INTERNAL_CONSTRUCTOR(SoRotateSphericalDragger);
170 
171   SO_KIT_ADD_CATALOG_ENTRY(rotatorSwitch, SoSwitch, TRUE, geomSeparator, feedbackSwitch, FALSE);
172   SO_KIT_ADD_CATALOG_ENTRY(rotator, SoSeparator, TRUE, rotatorSwitch, rotatorActive, TRUE);
173   SO_KIT_ADD_CATALOG_ENTRY(rotatorActive, SoSeparator, TRUE, rotatorSwitch, "", TRUE);
174   SO_KIT_ADD_CATALOG_ENTRY(feedbackSwitch, SoSwitch, TRUE, geomSeparator, "", FALSE);
175   SO_KIT_ADD_CATALOG_ENTRY(feedback, SoSeparator, TRUE, feedbackSwitch, feedbackActive, TRUE);
176   SO_KIT_ADD_CATALOG_ENTRY(feedbackActive, SoSeparator, TRUE, feedbackSwitch, "", TRUE);
177 
178   if (SO_KIT_IS_FIRST_INSTANCE()) {
179     SoInteractionKit::readDefaultParts("rotateSphericalDragger.iv",
180                                        ROTATESPHERICALDRAGGER_draggergeometry,
181                                        static_cast<int>(strlen(ROTATESPHERICALDRAGGER_draggergeometry)));
182   }
183 
184   SO_KIT_ADD_FIELD(rotation, (SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f)));
185   SO_KIT_INIT_INSTANCE();
186 
187   // initialize default parts
188   this->setPartAsDefault("rotator", "rotateSphericalRotator");
189   this->setPartAsDefault("rotatorActive", "rotateSphericalRotatorActive");
190   this->setPartAsDefault("feedback", "rotateSphericalFeedback");
191   this->setPartAsDefault("feedbackActive", "rotateSphericalFeedbackActive");
192 
193   // initialize swich values
194   SoSwitch *sw;
195   sw = SO_GET_ANY_PART(this, "rotatorSwitch", SoSwitch);
196   SoInteractionKit::setSwitchValue(sw, 0);
197   sw = SO_GET_ANY_PART(this, "feedbackSwitch", SoSwitch);
198   SoInteractionKit::setSwitchValue(sw, 0);
199 
200   // setup projector
201   this->sphereProj = new SbSpherePlaneProjector();
202   this->userProj = FALSE;
203   this->addStartCallback(SoRotateSphericalDragger::startCB);
204   this->addMotionCallback(SoRotateSphericalDragger::motionCB);
205   this->addFinishCallback(SoRotateSphericalDragger::doneCB);
206 
207   this->addValueChangedCallback(SoRotateSphericalDragger::valueChangedCB);
208 
209   this->fieldSensor = new SoFieldSensor(SoRotateSphericalDragger::fieldSensorCB, this);
210   this->fieldSensor->setPriority(0);
211 
212   this->setUpConnections(TRUE, TRUE);
213 }
214 
215 /*!
216   Protected destructor.
217 
218   (Dragger classes are derived from SoBase, so they are reference
219   counted and automatically destroyed when their reference count goes
220   to 0.)
221  */
~SoRotateSphericalDragger()222 SoRotateSphericalDragger::~SoRotateSphericalDragger()
223 {
224   delete this->fieldSensor;
225   if (!this->userProj) delete this->sphereProj;
226 }
227 
228 // Doc in superclass.
229 SbBool
setUpConnections(SbBool onoff,SbBool doitalways)230 SoRotateSphericalDragger::setUpConnections(SbBool onoff, SbBool doitalways)
231 {
232   if (!doitalways && this->connectionsSetUp == onoff) return onoff;
233 
234   SbBool oldval = this->connectionsSetUp;
235 
236   if (onoff) {
237     inherited::setUpConnections(onoff, doitalways);
238 
239     SoRotateSphericalDragger::fieldSensorCB(this, NULL);
240 
241     if (this->fieldSensor->getAttachedField() != &this->rotation) {
242       this->fieldSensor->attach(&this->rotation);
243     }
244   }
245   else {
246     if (this->fieldSensor->getAttachedField() != NULL) {
247       this->fieldSensor->detach();
248     }
249     inherited::setUpConnections(onoff, doitalways);
250   }
251   this->connectionsSetUp = onoff;
252   return oldval;
253 }
254 
255 /*! \COININTERNAL */
256 void
fieldSensorCB(void * d,SoSensor *)257 SoRotateSphericalDragger::fieldSensorCB(void *d, SoSensor *)
258 {
259   assert(d);
260   SoRotateSphericalDragger * thisp = THISP(d);
261   SbMatrix matrix = thisp->getMotionMatrix();
262   SbVec3f trans, scale;
263   SbRotation rot, scaleOrient;
264   matrix.getTransform(trans, rot, scale, scaleOrient);
265   matrix.setTransform(trans, thisp->rotation.getValue(), scale, scaleOrient);
266   thisp->setMotionMatrix(matrix);
267 }
268 
269 /*! \COININTERNAL */
270 void
valueChangedCB(void *,SoDragger * d)271 SoRotateSphericalDragger::valueChangedCB(void *, SoDragger * d)
272 {
273   SoRotateSphericalDragger * thisp = THISP(d);
274   SbMatrix matrix = thisp->getMotionMatrix();
275 
276   SbVec3f trans, scale;
277   SbRotation rot, scaleOrient;
278   matrix.getTransform(trans, rot, scale, scaleOrient);
279   thisp->fieldSensor->detach();
280   if (thisp->rotation.getValue() != rot)
281     thisp->rotation = rot;
282   thisp->fieldSensor->attach(&thisp->rotation);
283 }
284 
285 /*!
286   Replace the default sphere projection strategy. You may want to do
287   this if you change the dragger geometry, for instance.
288 
289   The default projection is an SbSpherePlaneProjector.
290 
291   \sa SbSphereSectionProjector, SbSphereSheetProjector
292 */
293 void
setProjector(SbSphereProjector * p)294 SoRotateSphericalDragger::setProjector(SbSphereProjector * p)
295 {
296   if (!this->userProj) delete this->sphereProj;
297   this->userProj = TRUE;
298   this->sphereProj = p;
299 }
300 
301 /*!
302   Returns projector instance used for converting from user interaction
303   dragger movements to 3D dragger re-orientation.
304 
305   \sa setProjector()
306 */
307 const SbSphereProjector *
getProjector(void) const308 SoRotateSphericalDragger::getProjector(void) const
309 {
310   return this->sphereProj;
311 }
312 
313 // Doc in superclass.
314 void
copyContents(const SoFieldContainer * fromfc,SbBool copyconnections)315 SoRotateSphericalDragger::copyContents(const SoFieldContainer * fromfc,
316                                        SbBool copyconnections)
317 {
318   inherited::copyContents(fromfc, copyconnections);
319   const SoRotateSphericalDragger * from = coin_assert_cast<const SoRotateSphericalDragger *>(fromfc);
320 
321   if (!this->userProj) {
322     delete this->sphereProj;
323   }
324   if (from->sphereProj) {
325     this->sphereProj = static_cast<SbSphereProjector *>(from->sphereProj->copy());
326   }
327   else {
328     this->sphereProj = new SbSpherePlaneProjector();
329   }
330   // we copied or created a new one, and need to delete it
331   this->userProj = FALSE;
332 }
333 
334 /*! \COININTERNAL */
335 void
startCB(void *,SoDragger * d)336 SoRotateSphericalDragger::startCB(void *, SoDragger * d)
337 {
338   SoRotateSphericalDragger * thisp = THISP(d);
339   thisp->dragStart();
340 }
341 
342 /*! \COININTERNAL */
343 void
motionCB(void *,SoDragger * d)344 SoRotateSphericalDragger::motionCB(void *, SoDragger * d)
345 {
346   SoRotateSphericalDragger * thisp = THISP(d);
347   thisp->drag();
348 }
349 
350 /*! \COININTERNAL */
351 void
doneCB(void * COIN_UNUSED_ARG (f),SoDragger * d)352 SoRotateSphericalDragger::doneCB(void * COIN_UNUSED_ARG(f), SoDragger * d)
353 {
354   SoRotateSphericalDragger * thisp = THISP(d);
355   thisp->dragFinish();
356 }
357 
358 /*! \COININTERNAL
359   Called when dragger is selected (picked) by the user.
360 */
361 void
dragStart(void)362 SoRotateSphericalDragger::dragStart(void)
363 {
364   SoSwitch *sw;
365   sw = SO_GET_ANY_PART(this, "rotatorSwitch", SoSwitch);
366   SoInteractionKit::setSwitchValue(sw, 1);
367   sw = SO_GET_ANY_PART(this, "feedbackSwitch", SoSwitch);
368   SoInteractionKit::setSwitchValue(sw, 1);
369 
370   SbVec3f hitPt = this->getLocalStartingPoint();
371 
372   float radius = hitPt.length();
373   this->sphereProj->setSphere(SbSphere(SbVec3f(0.0f, 0.0f, 0.0f), radius));
374 
375   this->sphereProj->setViewVolume(this->getViewVolume());
376   this->sphereProj->setWorkingSpace(this->getLocalToWorldMatrix());
377 
378   switch (this->getFrontOnProjector()) {
379   case FRONT:
380     this->sphereProj->setFront(TRUE);
381     break;
382   case BACK:
383     this->sphereProj->setFront(TRUE);
384     break;
385   default: // avoid warnings
386   case USE_PICK:
387     this->sphereProj->setFront(this->sphereProj->isPointInFront(hitPt));
388     break;
389   }
390   SbVec3f projPt = this->sphereProj->project(this->getNormalizedLocaterPosition());
391   this->getLocalToWorldMatrix().multVecMatrix(projPt, this->prevWorldHitPt);
392   this->prevMotionMatrix = this->getMotionMatrix();
393 }
394 
395 /*! \COININTERNAL
396   Called when user drags the mouse after picking the dragger.
397 */
398 void
drag(void)399 SoRotateSphericalDragger::drag(void)
400 {
401   this->sphereProj->setViewVolume(this->getViewVolume());
402   this->sphereProj->setWorkingSpace(this->getLocalToWorldMatrix());
403 
404   SbVec3f startPt;
405   this->getWorldToLocalMatrix().multVecMatrix(this->prevWorldHitPt, startPt);
406   SbVec3f projPt = this->sphereProj->project(this->getNormalizedLocaterPosition());
407   this->getLocalToWorldMatrix().multVecMatrix(projPt, this->prevWorldHitPt);
408 
409   SbRotation rot = this->sphereProj->getRotation(startPt, projPt);
410 
411   this->prevMotionMatrix = this->appendRotation(this->prevMotionMatrix, rot,
412                                                 SbVec3f(0.0f, 0.0f, 0.0f));
413   this->setMotionMatrix(this->prevMotionMatrix);
414 }
415 
416 /*! \COININTERNAL
417   Called when mouse button is released after picking and interacting
418   with the dragger.
419 */
420 void
dragFinish(void)421 SoRotateSphericalDragger::dragFinish(void)
422 {
423   SoSwitch *sw;
424   sw = SO_GET_ANY_PART(this, "rotatorSwitch", SoSwitch);
425   SoInteractionKit::setSwitchValue(sw, 0);
426   sw = SO_GET_ANY_PART(this, "feedbackSwitch", SoSwitch);
427   SoInteractionKit::setSwitchValue(sw, 0);
428 }
429 
430 #undef THISP
431 #endif // HAVE_DRAGGERS
432