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