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 SoTabPlaneDragger SoTabPlaneDragger.h Inventor/draggers/SoTabPlaneDragger.h
41   \brief The SoTabPlaneDragger class is a dragger you can translate and scale within a plane.
42 
43   \ingroup draggers
44 
45   \DRAGGER_DEFAULT_SCREENSHOT
46 
47   <center>
48   \image html tabplane.png "Screen Shot of Default Dragger"
49   </center>
50 
51   For translation, click anywhere inside the dragger's plane and move
52   it about in 2D.
53 
54   For non-uniform scaling operations, click and drag any of the 4 side
55   tabs. For uniform scaling, click and drag any of the 4 corner tabs.
56 
57   \sa SoTabBoxDragger
58 */
59 
60 #include <Inventor/draggers/SoTabPlaneDragger.h>
61 
62 #include <cstring>
63 #include <cassert>
64 
65 #include <Inventor/nodes/SoCoordinate3.h>
66 #include <Inventor/nodes/SoIndexedFaceSet.h>
67 #include <Inventor/nodes/SoMaterial.h>
68 #include <Inventor/nodes/SoMaterialBinding.h>
69 #include <Inventor/nodes/SoNormal.h>
70 #include <Inventor/nodes/SoNormalBinding.h>
71 #include <Inventor/nodes/SoSeparator.h>
72 #include <Inventor/nodes/SoShapeHints.h>
73 #include <Inventor/nodes/SoSwitch.h>
74 #include <Inventor/projectors/SbPlaneProjector.h>
75 #include <Inventor/projectors/SbLineProjector.h>
76 #include <Inventor/events/SoKeyboardEvent.h>
77 #include <Inventor/sensors/SoFieldSensor.h>
78 #include <Inventor/actions/SoGLRenderAction.h>
79 #include <Inventor/elements/SoModelMatrixElement.h>
80 #include <Inventor/elements/SoViewVolumeElement.h>
81 #include <Inventor/elements/SoViewportRegionElement.h>
82 #include <Inventor/elements/SoCacheElement.h>
83 #include <Inventor/SbRotation.h>
84 
85 #include <data/draggerDefaults/tabPlaneDragger.h>
86 
87 #include "coindefs.h" // COIN_STUB()
88 #include "nodekits/SoSubKitP.h"
89 #include "SbBasicP.h"
90 
91 /*!
92   \var SoSFVec3f SoTabPlaneDragger::translation
93 
94   Continuously updated to contain the current translation from the
95   dragger's local origo position.
96 */
97 
98 /*!
99   \var SoSFVec3f SoTabPlaneDragger::scaleFactor
100 
101   Continuously updated to contain the current vector of scaling along
102   the X, Y and Z axes. The Z component will always be 1.0.
103 */
104 
105 #define WHATKIND_NONE      0
106 #define WHATKIND_SCALE     1
107 #define WHATKIND_TRANSLATE 2
108 
109 #define CONSTRAINT_OFF  0
110 #define CONSTRAINT_WAIT 1
111 #define CONSTRAINT_X    2
112 #define CONSTRAINT_Y    3
113 #define CONSTRAINT_Z    4
114 
115 
116 // used to quickly find correct position of tabs
117 
118 static float edgetab_lookup[] = {
119   0.0f, 1.0f,
120   1.0f, 0.0f,
121   0.0f, -1.0f,
122   -1.0f, 0.0f
123 };
124 
125 static float cornertab_lookup[] = {
126   1.0f, 1.0f,
127   1.0f, -1.0f,
128   -1.0f, -1.0f,
129   -1.0f, 1.0f
130 };
131 
132 // FIXME: find a better solution than this lame Z_OFFSET hack, pederb 20000301
133 #define Z_OFFSET 0.01f       // dummy offset for tabs to get "correct" picking
134 #define TABSIZE 10.0f        // size (in pixels when projected to screen) of tabs
135 
136 #define THISP(d) static_cast<SoTabPlaneDragger *>(d)
137 
138 class SoTabPlaneDraggerP {
139 public:
140 };
141 
142 SO_KIT_SOURCE(SoTabPlaneDragger);
143 
144 // doc in superclass
145 void
initClass(void)146 SoTabPlaneDragger::initClass(void)
147 {
148   SO_KIT_INTERNAL_INIT_CLASS(SoTabPlaneDragger, SO_FROM_INVENTOR_1);
149 }
150 
151 // FIXME: document which parts need to be present in the geometry
152 // scenegraph, and what role they play in the dragger. 20010913 mortene.
153 /*!
154   \DRAGGER_CONSTRUCTOR
155 
156   \NODEKIT_PRE_DIAGRAM
157 
158   \verbatim
159   CLASS SoTabPlaneDragger
160   -->"this"
161         "callbackList"
162         "topSeparator"
163            "motionMatrix"
164            "geomSeparator"
165   -->         "planeSwitch"
166   -->            "translator"
167   -->            "scaleTabs"
168   -->               "scaleTabMaterial"
169   -->               "scaleTabHints"
170   -->               "scaleTabMaterialBinding"
171   -->               "scaleTabNormalBinding"
172   -->               "scaleTabNormal"
173   -->               "edgeScaleCoords"
174   -->               "edgeScaleTab0"
175   -->               "edgeScaleTab1"
176   -->               "edgeScaleTab2"
177   -->               "edgeScaleTab3"
178   -->               "cornerScaleCoords"
179   -->               "cornerScaleTab0"
180   -->               "cornerScaleTab1"
181   -->               "cornerScaleTab2"
182   -->               "cornerScaleTab3"
183   \endverbatim
184 
185   \NODEKIT_POST_DIAGRAM
186 
187 
188   \NODEKIT_PRE_TABLE
189 
190   \verbatim
191   CLASS SoTabPlaneDragger
192   PVT   "this",  SoTabPlaneDragger  ---
193         "callbackList",  SoNodeKitListPart [ SoCallback, SoEventCallback ]
194   PVT   "topSeparator",  SoSeparator  ---
195   PVT   "motionMatrix",  SoMatrixTransform  ---
196   PVT   "geomSeparator",  SoSeparator  ---
197   PVT   "planeSwitch",  SoSwitch  ---
198         "translator",  SoSeparator  ---
199   PVT   "scaleTabs",  SoSeparator  ---
200         "scaleTabMaterial",  SoMaterial  ---
201         "scaleTabHints",  SoShapeHints  ---
202   PVT   "scaleTabMaterialBinding",  SoMaterialBinding  ---
203   PVT   "scaleTabNormalBinding",  SoNormalBinding  ---
204   PVT   "scaleTabNormal",  SoNormal  ---
205   PVT   "edgeScaleCoords",  SoCoordinate3  ---
206   PVT   "edgeScaleTab0",  SoIndexedFaceSet  ---
207   PVT   "edgeScaleTab1",  SoIndexedFaceSet  ---
208   PVT   "edgeScaleTab2",  SoIndexedFaceSet  ---
209   PVT   "edgeScaleTab3",  SoIndexedFaceSet  ---
210   PVT   "cornerScaleCoords",  SoCoordinate3  ---
211   PVT   "cornerScaleTab0",  SoIndexedFaceSet  ---
212   PVT   "cornerScaleTab1",  SoIndexedFaceSet  ---
213   PVT   "cornerScaleTab2",  SoIndexedFaceSet  ---
214   PVT   "cornerScaleTab3",  SoIndexedFaceSet  ---
215   \endverbatim
216 
217   \NODEKIT_POST_TABLE
218 */
SoTabPlaneDragger(void)219 SoTabPlaneDragger::SoTabPlaneDragger(void)
220 {
221   SO_KIT_INTERNAL_CONSTRUCTOR(SoTabPlaneDragger);
222 
223   SO_KIT_ADD_CATALOG_ENTRY(planeSwitch, SoSwitch, TRUE, geomSeparator, "", FALSE);
224   SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, planeSwitch, scaleTabs, TRUE);
225   SO_KIT_ADD_CATALOG_ENTRY(scaleTabs, SoSeparator, TRUE, planeSwitch, "", FALSE);
226   SO_KIT_ADD_CATALOG_ENTRY(scaleTabMaterial, SoMaterial, TRUE, scaleTabs, scaleTabHints, TRUE);
227   SO_KIT_ADD_CATALOG_ENTRY(scaleTabHints, SoShapeHints, TRUE, scaleTabs, scaleTabMaterialBinding, TRUE);
228   SO_KIT_ADD_CATALOG_ENTRY(scaleTabMaterialBinding, SoMaterialBinding, TRUE, scaleTabs, scaleTabNormalBinding, FALSE);
229   SO_KIT_ADD_CATALOG_ENTRY(scaleTabNormalBinding, SoNormalBinding, TRUE, scaleTabs, scaleTabNormal, FALSE);
230   SO_KIT_ADD_CATALOG_ENTRY(scaleTabNormal, SoNormal, TRUE, scaleTabs, edgeScaleCoords, FALSE);
231   SO_KIT_ADD_CATALOG_ENTRY(edgeScaleCoords, SoCoordinate3, TRUE, scaleTabs, edgeScaleTab0, FALSE);
232   SO_KIT_ADD_CATALOG_ENTRY(edgeScaleTab0, SoIndexedFaceSet, TRUE, scaleTabs, edgeScaleTab1, FALSE);
233   SO_KIT_ADD_CATALOG_ENTRY(edgeScaleTab1, SoIndexedFaceSet, TRUE, scaleTabs, edgeScaleTab2, FALSE);
234   SO_KIT_ADD_CATALOG_ENTRY(edgeScaleTab2, SoIndexedFaceSet, TRUE, scaleTabs, edgeScaleTab3, FALSE);
235   SO_KIT_ADD_CATALOG_ENTRY(edgeScaleTab3, SoIndexedFaceSet, TRUE, scaleTabs, cornerScaleCoords, FALSE);
236   SO_KIT_ADD_CATALOG_ENTRY(cornerScaleCoords, SoCoordinate3, TRUE, scaleTabs, cornerScaleTab0, FALSE);
237   SO_KIT_ADD_CATALOG_ENTRY(cornerScaleTab0, SoIndexedFaceSet, TRUE, scaleTabs, cornerScaleTab1, FALSE);
238   SO_KIT_ADD_CATALOG_ENTRY(cornerScaleTab1, SoIndexedFaceSet, TRUE, scaleTabs, cornerScaleTab2, FALSE);
239   SO_KIT_ADD_CATALOG_ENTRY(cornerScaleTab2, SoIndexedFaceSet, TRUE, scaleTabs, cornerScaleTab3, FALSE);
240   SO_KIT_ADD_CATALOG_ENTRY(cornerScaleTab3, SoIndexedFaceSet, TRUE, scaleTabs, "", FALSE);
241 
242   if (SO_KIT_IS_FIRST_INSTANCE()) {
243     SoInteractionKit::readDefaultParts("tabPlaneDragger.iv",
244                                        TABPLANEDRAGGER_draggergeometry,
245                                        static_cast<int>(strlen(TABPLANEDRAGGER_draggergeometry)));
246   }
247 
248   SO_KIT_ADD_FIELD(translation, (0.0f, 0.0f, 0.0f));
249   SO_KIT_ADD_FIELD(scaleFactor, (1.0f, 1.0f, 1.0f));
250 
251   SO_KIT_INIT_INSTANCE();
252 
253   this->setPartAsDefault("translator", "tabPlaneTranslator");
254   this->setPartAsDefault("scaleTabMaterial", "tabPlaneScaleTabMaterial");
255   this->setPartAsDefault("scaleTabHints", "tabPlaneScaleTabHints");
256 
257   SoSwitch *sw = SO_GET_ANY_PART(this, "planeSwitch", SoSwitch);
258   SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
259 
260   this->createPrivateParts();
261   this->prevsizex = this->prevsizey = 0.0f;
262   this->reallyAdjustScaleTabSize(NULL);
263   this->constraintState = CONSTRAINT_OFF;
264   this->whatkind = WHATKIND_NONE;
265   this->adjustTabs = TRUE;
266 
267   this->addStartCallback(SoTabPlaneDragger::startCB);
268   this->addMotionCallback(SoTabPlaneDragger::motionCB);
269   this->addFinishCallback(SoTabPlaneDragger::finishCB);
270   this->addValueChangedCallback(SoTabPlaneDragger::valueChangedCB);
271   this->addOtherEventCallback(SoTabPlaneDragger::metaKeyChangeCB);
272 
273   this->planeProj = new SbPlaneProjector;
274   this->lineProj = new SbLineProjector;
275 
276   this->translFieldSensor = new SoFieldSensor(SoTabPlaneDragger::fieldSensorCB, this);
277   this->translFieldSensor->setPriority(0);
278   this->scaleFieldSensor = new SoFieldSensor(SoTabPlaneDragger::fieldSensorCB, this);
279   this->scaleFieldSensor->setPriority(0);
280 
281   this->setUpConnections(TRUE, TRUE);
282 }
283 
284 /*!
285   Protected destructor.
286 
287   (Dragger classes are derived from SoBase, so they are reference
288   counted and automatically destroyed when their reference count goes
289   to 0.)
290  */
~SoTabPlaneDragger()291 SoTabPlaneDragger::~SoTabPlaneDragger()
292 {
293   delete this->translFieldSensor;
294   delete this->scaleFieldSensor;
295   delete this->planeProj;
296   delete this->lineProj;
297 }
298 
299 // Doc in superclass.
300 SbBool
setUpConnections(SbBool onoff,SbBool doitalways)301 SoTabPlaneDragger::setUpConnections(SbBool onoff, SbBool doitalways)
302 {
303   if (!doitalways && this->connectionsSetUp == onoff) return onoff;
304 
305   if (onoff) {
306     inherited::setUpConnections(onoff, doitalways);
307 
308     SoTabPlaneDragger::fieldSensorCB(this, NULL);
309 
310     if (this->translFieldSensor->getAttachedField() != &this->translation) {
311       this->translFieldSensor->attach(&this->translation);
312     }
313     if (this->scaleFieldSensor->getAttachedField() != &this->scaleFactor) {
314       this->scaleFieldSensor->attach(&this->scaleFactor);
315     }
316   }
317   else {
318     if (this->translFieldSensor->getAttachedField() != NULL) {
319       this->translFieldSensor->detach();
320     }
321     if (this->scaleFieldSensor->getAttachedField() != NULL) {
322       this->scaleFieldSensor->detach();
323     }
324     inherited::setUpConnections(onoff, doitalways);
325   }
326   return !(this->connectionsSetUp = onoff);
327 }
328 
329 // Doc in superclass.
330 void
setDefaultOnNonWritingFields(void)331 SoTabPlaneDragger::setDefaultOnNonWritingFields(void)
332 {
333   this->edgeScaleCoords.setDefault(TRUE);
334   this->cornerScaleCoords.setDefault(TRUE);
335 
336   inherited::setDefaultOnNonWritingFields();
337 }
338 
339 /*! \COININTERNAL */
340 void
fieldSensorCB(void * d,SoSensor *)341 SoTabPlaneDragger::fieldSensorCB(void * d, SoSensor *)
342 {
343   SoTabPlaneDragger * thisp = static_cast<SoTabPlaneDragger *>(d);
344   SbMatrix matrix = thisp->getMotionMatrix();
345   thisp->workFieldsIntoTransform(matrix);
346   thisp->setMotionMatrix(matrix);
347 }
348 
349 /*! \COININTERNAL */
350 void
valueChangedCB(void *,SoDragger * d)351 SoTabPlaneDragger::valueChangedCB(void *, SoDragger * d)
352 {
353   SoTabPlaneDragger * thisp = static_cast<SoTabPlaneDragger *>(d);
354   SbMatrix matrix = thisp->getMotionMatrix();
355   SbVec3f trans, scale;
356   SbRotation rot, scaleOrient;
357   matrix.getTransform(trans, rot, scale, scaleOrient);
358 
359   thisp->translFieldSensor->detach();
360   if (thisp->translation.getValue() != trans)
361     thisp->translation = trans;
362   thisp->translFieldSensor->attach(&thisp->translation);
363 
364   thisp->scaleFieldSensor->detach();
365   if (thisp->scaleFactor.getValue() != scale)
366     thisp->scaleFactor = scale;
367   thisp->scaleFieldSensor->attach(&thisp->scaleFactor);
368 }
369 
370 // Doc in superclass.
371 void
GLRender(SoGLRenderAction * action)372 SoTabPlaneDragger::GLRender(SoGLRenderAction * action)
373 {
374   // FIXME: it'd be better to adjust even when scaling, but it's a bit
375   // complicated to calculate the correct scale factor adjustment (to
376   // make the pointer stay at the exact same position in the tab no
377   // matter how the dragger is scaled). pederb, 2004-09-22
378   if (this->whatkind != WHATKIND_SCALE) {
379     // disable notification do avoid multiple redraws, but
380     // remember to invalidate open caches.
381     SbBool oldnotify = this->enableNotify(FALSE);
382     SoCacheElement::invalidate(action->getState());
383 
384     this->reallyAdjustScaleTabSize(action);
385     this->adjustTabs = FALSE;
386 
387     (void) this->enableNotify(oldnotify);
388   }
389   inherited::GLRender(action);
390 }
391 
392 /*!
393   Signals the dragger to recalculate the size of its tabs. This method
394   is not doing anything useful in Coin, since the tab sizes are recalculated
395   every time the dragger is rendered, even though this method has not
396   been called.
397 */
398 void
adjustScaleTabSize(void)399 SoTabPlaneDragger::adjustScaleTabSize(void)
400 {
401   this->adjustTabs = TRUE;
402 }
403 
404 /*!
405   Recalculates the size of the tabs, based on the current view volume,
406   the current viewport, the current model matrix and the current scale
407   factor. If \a action == \e NULL, a default size will be used.
408 */
409 void
reallyAdjustScaleTabSize(SoGLRenderAction * action)410 SoTabPlaneDragger::reallyAdjustScaleTabSize(SoGLRenderAction *action)
411 {
412   SoCoordinate3 *coordnode;
413   SbVec3f *coords;
414 
415   float sizex = 0.08f;
416   float sizey = 0.08f;
417   if (action != NULL) {
418     SoState *state = action->getState();
419     SbMatrix toworld = SoModelMatrixElement::get(state);
420     toworld.multLeft(this->getMotionMatrix());
421     const SbViewVolume &vv = SoViewVolumeElement::get(state);
422     const SbViewportRegion &vp = SoViewportRegionElement::get(state);
423     SbVec3f center(0.0f, 0.0f, 0.0f);
424     toworld.multVecMatrix(center, center);
425     sizex = sizey =
426       vv.getWorldToScreenScale(center, TABSIZE/float(vp.getViewportSizePixels()[0]));
427 
428     SbVec3f scale;
429     {
430       SbRotation r, so;
431       SbVec3f t;
432       toworld.getTransform(t, r, scale, so);
433     }
434 
435     // Take absolute value to allow scales to be negative (which is a
436     // common trick when e.g. using a left-handed coordinate system).
437     sizex = static_cast<float>(fabs(sizex / scale[0]));
438     sizey = static_cast<float>(fabs(sizey / scale[1]));
439   }
440 
441   if (sizex == this->prevsizex && this->prevsizey == sizey) return;
442 
443   this->prevsizex = sizex;
444   this->prevsizey = sizey;
445 
446   float halfx = sizex * 0.5f;
447   float halfy = sizey * 0.5f;
448 
449   coordnode = SO_GET_ANY_PART(this, "edgeScaleCoords", SoCoordinate3);
450   coordnode->point.setNum(16);
451   coords = coordnode->point.startEditing();
452   {
453     coords[0].setValue(halfx, 1.0f, Z_OFFSET);
454     coords[1].setValue(-halfx, 1.0f, Z_OFFSET);
455     coords[2].setValue(-halfx, 1.0f-sizey, Z_OFFSET);
456     coords[3].setValue(halfx, 1.0f-sizey, Z_OFFSET);
457 
458     coords[4].setValue(1.0f, -halfy, Z_OFFSET);
459     coords[5].setValue(1.0f, halfy, Z_OFFSET);
460     coords[6].setValue(1.0f-sizex, halfy, Z_OFFSET);
461     coords[7].setValue(1.0f-sizex, -halfy, Z_OFFSET);
462 
463     coords[8].setValue(-halfx, -1.0f, Z_OFFSET);
464     coords[9].setValue(halfx, -1.0f, Z_OFFSET);
465     coords[10].setValue(halfx, -1.0f+sizey, Z_OFFSET);
466     coords[11].setValue(-halfx, -1.0f+sizey, Z_OFFSET);
467 
468     coords[12].setValue(-1.0f, halfy, Z_OFFSET);
469     coords[13].setValue(-1.0f, -halfy, Z_OFFSET);
470     coords[14].setValue(-1.0f+sizex, -halfy, Z_OFFSET);
471     coords[15].setValue(-1.0f+sizex, halfy, Z_OFFSET);
472   }
473   coordnode->point.finishEditing();
474 
475   coordnode = SO_GET_ANY_PART(this, "cornerScaleCoords", SoCoordinate3);
476   coordnode->point.setNum(16);
477   coords = coordnode->point.startEditing();
478   {
479     coords[0].setValue(1.0f, 1.0f, Z_OFFSET);
480     coords[1].setValue(1.0f-sizex, 1.0f, Z_OFFSET);
481     coords[2].setValue(1.0f-sizex, 1.0f-sizey, Z_OFFSET);
482     coords[3].setValue(1.0f, 1.0f-sizey, Z_OFFSET);
483 
484     coords[4].setValue(1.0f, -1.0f, Z_OFFSET);
485     coords[5].setValue(1.0f, -1.0f+sizey, Z_OFFSET);
486     coords[6].setValue(1.0f-sizex, -1.0f+sizey, Z_OFFSET);
487     coords[7].setValue(1.0f-sizex, -1.0f, Z_OFFSET);
488 
489     coords[8].setValue(-1.0f, -1.0f, Z_OFFSET);
490     coords[9].setValue(-1.0f+sizex, -1.0f, Z_OFFSET);
491     coords[10].setValue(-1.0f+sizex, -1.0f+sizey, Z_OFFSET);
492     coords[11].setValue(-1.0f, -1.0f+sizey, Z_OFFSET);
493 
494     coords[12].setValue(-1.0f, 1.0f, Z_OFFSET);
495     coords[13].setValue(-1.0f, 1.0f-sizey, Z_OFFSET);
496     coords[14].setValue(-1.0f+sizex, 1.0f-sizey, Z_OFFSET);
497     coords[15].setValue(-1.0f+sizex, 1.0f, Z_OFFSET);
498   }
499   coordnode->point.finishEditing();
500 }
501 
502 /*!
503   Not implemented.
504 */
505 void
getXYScreenLengths(SbVec2f & COIN_UNUSED_ARG (lengths),const SbMatrix & COIN_UNUSED_ARG (localtoscreen),const SbVec2s & COIN_UNUSED_ARG (winsize))506 SoTabPlaneDragger::getXYScreenLengths(SbVec2f & COIN_UNUSED_ARG(lengths),
507                                       const SbMatrix & COIN_UNUSED_ARG(localtoscreen),
508                                       const SbVec2s & COIN_UNUSED_ARG(winsize))
509 {
510   // FIXME: I found this method just defined in the header file, but
511   // not implemented (!). We should obviously implement it if it's
512   // useful. 20011127 mortene.
513   COIN_STUB();
514 }
515 
516 /*! \COININTERNAL
517   Called when dragger is selected (picked) by the user.
518 */
519 void
dragStart(void)520 SoTabPlaneDragger::dragStart(void)
521 {
522   int i;
523   const SoPath *pickpath = this->getPickPath();
524   const SoEvent *event = this->getEvent();
525 
526   SbBool found = FALSE;
527   SbVec3f startpt = this->getLocalStartingPoint();
528 
529   this->constraintState = CONSTRAINT_OFF;
530 
531   SbString str;
532   if (!found) {
533     for (i = 0; i < 4; i++) {
534       str.sprintf("edgeScaleTab%d", i);
535       if (pickpath->findNode(this->getNodeFieldNode(str.getString())) >= 0 ||
536           this->getSurrogatePartPickedName() == str.getString()) break;
537     }
538     if (i < 4) {
539       found = TRUE;
540       this->constraintState = i & 1 ? CONSTRAINT_X : CONSTRAINT_Y;
541       this->whatkind = WHATKIND_SCALE;
542       this->scaleCenter.setValue(-edgetab_lookup[i*2], -edgetab_lookup[i*2+1], 0.0f);
543     }
544   }
545   if (!found) {
546     for (i = 0; i < 4; i++) {
547       str.sprintf("cornerScaleTab%d", i);
548       if (pickpath->findNode(this->getNodeFieldNode(str.getString())) >= 0 ||
549           this->getSurrogatePartPickedName() == str.getString()) break;
550     }
551     if (i < 4) {
552       found = TRUE;
553       this->whatkind = WHATKIND_SCALE;
554       this->scaleCenter.setValue(-cornertab_lookup[i*2], -cornertab_lookup[i*2+1], 0.0f);
555     }
556   }
557   if (!found) {
558     assert(pickpath->findNode(this->getNodeFieldNode("translator")) >= 0 ||
559            this->getSurrogatePartPickedName() == "translator");
560     found = TRUE;
561     this->whatkind = WHATKIND_TRANSLATE;
562   }
563 
564   if (this->whatkind == WHATKIND_SCALE) {
565     // startpt might have a non-zero z-component that could cause
566     // trouble when scaling down to very small sizes, so create a
567     // point equal to startpt, but with the z-component set to zero.
568     // scaleCenter already has a zero z-value.
569     SbVec3f linept(startpt[0], startpt[1], 0.0f);
570     this->lineProj->setLine(SbLine(this->scaleCenter, linept));
571   }
572   else { // translate
573     this->planeProj->setPlane(SbPlane(SbVec3f(0.0f, 0.0f, 1.0f), startpt));
574     this->constraintState = CONSTRAINT_OFF;
575     if (event->wasShiftDown()) {
576       this->getLocalToWorldMatrix().multVecMatrix(startpt, this->worldRestartPt);
577       this->constraintState = CONSTRAINT_WAIT;
578     }
579   }
580 }
581 
582 /*! \COININTERNAL
583   Called when user drags the mouse after picking the dragger.
584 */
585 void
drag(void)586 SoTabPlaneDragger::drag(void)
587 {
588   if (this->whatkind == WHATKIND_SCALE) {
589     SbVec3f startpt = this->getLocalStartingPoint();
590     this->lineProj->setViewVolume(this->getViewVolume());
591     this->lineProj->setWorkingSpace(this->getLocalToWorldMatrix());
592     SbVec3f projpt = this->lineProj->project(this->getNormalizedLocaterPosition());
593 
594     SbVec3f center = this->scaleCenter;
595     SbVec3f orgvec = startpt - center;
596     SbVec3f currvec = projpt - center;
597 
598     // Ignore the possible z-component of the vectors
599     orgvec[2] = 0.0f;
600     currvec[2] = 0.0f;
601 
602     float orglen = orgvec.length();
603     float currlen = currvec.length();
604     float scale = 0.0f;
605 
606     if (orglen > 0.0f) scale = currlen / orglen;
607     if (scale > 0.0f && orgvec.dot(currvec) <= 0.0f) scale = 0.0f;
608 
609     SbVec3f scalevec(scale, scale, 1.0f);
610     if (this->constraintState == CONSTRAINT_X) {
611       scalevec[1] = 1.0f;
612     }
613     else if (this->constraintState == CONSTRAINT_Y) {
614       scalevec[0] = 1.0f;
615     }
616     this->setMotionMatrix(this->appendScale(this->getStartMotionMatrix(),
617                                             scalevec,
618                                             center));
619 
620   }
621   else { // translate
622     SbVec3f startpt = this->getLocalStartingPoint();
623     this->planeProj->setViewVolume(this->getViewVolume());
624     this->planeProj->setWorkingSpace(this->getLocalToWorldMatrix());
625     SbVec3f projpt = this->planeProj->project(this->getNormalizedLocaterPosition());
626 
627     const SoEvent *event = this->getEvent();
628     SbBool reset = FALSE;
629     if (event->wasShiftDown() && this->constraintState == CONSTRAINT_OFF) {
630       this->constraintState = CONSTRAINT_WAIT;
631       this->setStartLocaterPosition(event->getPosition());
632       this->getLocalToWorldMatrix().multVecMatrix(projpt, this->worldRestartPt);
633       reset = TRUE;
634     }
635     else if (!event->wasShiftDown() && this->constraintState != CONSTRAINT_OFF) {
636       this->constraintState = CONSTRAINT_OFF;
637       reset = TRUE;
638     }
639     if (reset) {
640       this->saveStartParameters();
641       SbVec3f worldpt;
642       this->getLocalToWorldMatrix().multVecMatrix(projpt, worldpt);
643       this->setStartingPoint(worldpt);
644       startpt = projpt;
645     }
646     SbVec3f motion;
647     SbVec3f localrestartpt;
648     if (this->constraintState != CONSTRAINT_OFF) {
649       this->getWorldToLocalMatrix().multVecMatrix(this->worldRestartPt,
650                                                   localrestartpt);
651       motion = localrestartpt - startpt;
652     }
653     else motion = projpt - startpt;
654     switch(this->constraintState) {
655     case CONSTRAINT_OFF:
656       break;
657     case CONSTRAINT_WAIT:
658       if (this->isAdequateConstraintMotion()) {
659         SbVec3f newmotion = projpt - localrestartpt;
660         int biggest = 0;
661         double bigval = fabs(newmotion[0]);
662         if (fabs(newmotion[1]) > bigval) {
663           biggest = 1;
664         }
665         motion[biggest] += newmotion[biggest];
666         this->constraintState = CONSTRAINT_X + biggest;
667       }
668       else {
669         return;
670       }
671       break;
672     case CONSTRAINT_X:
673       motion[0] += projpt[0] - localrestartpt[0];
674       break;
675     case CONSTRAINT_Y:
676       motion[1] += projpt[1] - localrestartpt[1];
677       break;
678     case CONSTRAINT_Z:
679       motion[2] += projpt[2] - localrestartpt[2];
680     }
681     this->setMotionMatrix(this->appendTranslation(this->getStartMotionMatrix(), motion));
682   }
683 }
684 
685 /*! \COININTERNAL
686   Called when mouse button is released after picking and interacting
687   with the dragger.
688 */
689 void
dragFinish(void)690 SoTabPlaneDragger::dragFinish(void)
691 {
692   this->whatkind = WHATKIND_NONE;
693 }
694 
695 /*! \COININTERNAL */
696 void
startCB(void *,SoDragger * d)697 SoTabPlaneDragger::startCB(void *, SoDragger * d)
698 {
699   SoTabPlaneDragger * thisp = static_cast<SoTabPlaneDragger *>(d);
700   thisp->dragStart();
701 }
702 
703 /*! \COININTERNAL */
704 void
motionCB(void *,SoDragger * d)705 SoTabPlaneDragger::motionCB(void *, SoDragger * d)
706 {
707   SoTabPlaneDragger * thisp = static_cast<SoTabPlaneDragger *>(d);
708   thisp->drag();
709 }
710 
711 /*! \COININTERNAL */
712 void
finishCB(void *,SoDragger * d)713 SoTabPlaneDragger::finishCB(void *, SoDragger * d)
714 {
715   SoTabPlaneDragger * thisp = static_cast<SoTabPlaneDragger *>(d);
716   thisp->dragFinish();
717 }
718 
719 /*! \COININTERNAL */
720 void
metaKeyChangeCB(void *,SoDragger * d)721 SoTabPlaneDragger::metaKeyChangeCB(void *, SoDragger * d)
722 {
723   SoTabPlaneDragger * thisp = static_cast<SoTabPlaneDragger *>(d);
724   if (!thisp->isActive.getValue()) return;
725   if (!(thisp->whatkind == WHATKIND_TRANSLATE)) return;
726 
727   const SoEvent *event = thisp->getEvent();
728   if (event->wasShiftDown() && thisp->constraintState == CONSTRAINT_OFF) {
729     thisp->drag();
730   }
731   else if (!event->wasShiftDown() && thisp->constraintState != CONSTRAINT_OFF) {
732     thisp->drag();
733   }
734 }
735 
736 
737 //
738 // this method is not as naughty as it sounds :-) It simply creates the parts
739 // it is not possible to configure through the dragger defaults file.
740 //
741 void
createPrivateParts(void)742 SoTabPlaneDragger::createPrivateParts(void)
743 {
744   SoMaterialBinding *mb = SO_GET_ANY_PART(this, "scaleTabMaterialBinding", SoMaterialBinding);
745   mb->value = SoMaterialBinding::OVERALL;
746   this->scaleTabMaterialBinding.setDefault(TRUE);
747 
748   SoNormalBinding *nb = SO_GET_ANY_PART(this, "scaleTabNormalBinding", SoNormalBinding);
749   nb->value = SoNormalBinding::OVERALL;
750   this->scaleTabNormalBinding.setDefault(TRUE);
751 
752   SoNormal *normal = SO_GET_ANY_PART(this, "scaleTabNormal", SoNormal);
753   normal->vector.setValue(SbVec3f(0.0f, 0.0f, 1.0f));
754   this->scaleTabNormal.setDefault(TRUE);
755 
756   SoIndexedFaceSet *fs;
757   SbString str;
758   int idx = 0;
759   int i, j;
760   int32_t *ptr;
761 
762   for (i = 0; i < 8; i++) {
763     if (i == 0 || i == 4) idx = 0;
764     if (i < 4)
765       str.sprintf("edgeScaleTab%d", i);
766     else
767       str.sprintf("cornerScaleTab%d", i-4);
768     fs = coin_assert_cast<SoIndexedFaceSet *>(this->getAnyPart(SbName(str.getString()), TRUE));
769     fs->coordIndex.setNum(5);
770 
771     ptr = fs->coordIndex.startEditing();
772     {
773       for (j = 0; j < 4; j++) ptr[j] = idx++;
774       ptr[4] = -1;
775     }
776     fs->coordIndex.finishEditing();
777     fs->normalIndex.setValue(0);
778     fs->materialIndex.setValue(0);
779 
780     SoField * f = this->getField(SbName(str.getString()));
781     assert(f);
782     f->setDefault(TRUE);
783   }
784 
785   // turn off render caching since the geometry below this node might
786   // change very often.
787   SoSeparator *sep = SO_GET_ANY_PART(this, "scaleTabs", SoSeparator);
788   sep->renderCaching = SoSeparator::OFF;
789   sep->renderCaching.setDefault(TRUE);
790   // this is the default, so don't write it
791   this->scaleTabs.setDefault(TRUE);
792 }
793 
794 //
795 // returns the node in the SoSFNode field fieldname
796 //
797 SoNode *
getNodeFieldNode(const char * fieldname)798 SoTabPlaneDragger::getNodeFieldNode(const char *fieldname)
799 {
800   SoField * field = this->getField(fieldname);
801   assert(field != NULL);
802   assert(coin_assert_cast<SoSFNode *>(field)->getValue() != NULL);
803   return coin_assert_cast<SoSFNode *>(field)->getValue();
804 }
805 
806 // Undefine these again, as some of them are also used in other
807 // dragger sourcecode files (which causes trouble when using the
808 // compact build hack where all .cpp files are included into all.cpp).
809 
810 #undef WHATKIND_NONE
811 #undef WHATKIND_SCALE
812 #undef WHATKIND_TRANSLATE
813 #undef CONSTRAINT_OFF
814 #undef CONSTRAINT_WAIT
815 #undef CONSTRAINT_X
816 #undef CONSTRAINT_Y
817 #undef CONSTRAINT_Z
818 #undef Z_OFFSET
819 #undef TABSIZE
820 
821 #undef THISP
822 #endif // HAVE_DRAGGERS
823