17.4. Simple Draggers

A simple dragger moves in 3D space in response to click-drag-release mouse events. Its position in space is determined by its position in the scene graph. Each simple dragger has a field that reflects the current state of the dragger. For example, the SoRotateDiscDragger has a rotation field that indicates its current rotation value. This field can be connected to other fields in the scene graph or to the input field of an engine (see the following section). Callback functions can also be used with simple draggers, as described in the section called “Callback Functions”.

A convenient way to use a dragger is to connect its fields to other fields or engines in the scene graph. For example, the SoTranslate1Dragger( C++ | Java | .NET ) has a translation field that could be used in a variety of ways. Figure 17.5, “ Connecting a Dragger's Field to Another Field in the Scene Graph shows how this field could be used to edit the radius of a cone node. Since the dragger's translation field is an SoSFVec3f( C++ | Java | .NET ), you need to use an SoDecomposeVec3f( C++ | Java | .NET ) engine to extract the x value of the dragger's

translation. This x value is then fed into the bottomRadius field of the cone node. Now, whenever the dragger is translated, the radius of the cone changes.


Example 17.1, “ Using a Simple Dragger ” shows the code for connecting these fields and engines. Figure 17.6, “ Using a Dragger and Engine to Edit the Radius of a Cone shows an image created by this example.

Example 17.1.  Using a Simple Dragger


C++
// Create myDragger with an initial translation of (1,0,0)
SoTranslate1Dragger *myDragger = new SoTranslate1Dragger;
root->addChild(myDragger);
myDragger->translation.setValue(1,0,0);

// Place an SoCone below myDragger
SoTransform *myTransform = new SoTransform;
SoCone *myCone = new SoCone;
root->addChild(myTransform);
root->addChild(myCone);
myTransform->translation.setValue(0,3,0);

// SoDecomposeVec3f engine extracts myDragger's x-component
// The result is connected to myCone's bottomRadius.
SoDecomposeVec3f *myEngine = new SoDecomposeVec3f;
myEngine->vector.connectFrom(&myDragger->translation);
myCone->bottomRadius.connectFrom(&myEngine->x);
    

.NET
// Create myDragger with an initial translation of (1, 0, 0)
SoTranslate1Dragger myDragger = new SoTranslate1Dragger();
root.AddChild(myDragger);
myDragger.translation.SetValue(1f, 0f, 0f);

// Place an SoCone above myDragger
SoTransform myTransform = new SoTransform();
SoCone myCone = new SoCone();
root.AddChild(myTransform);
root.AddChild(myCone);
myTransform.translation.SetValue(0, 3, 0);

// SoDecomposeVec3f engine extracts myDragger's x-component
// The result is connected to myCone's bottomRadius.
myEngine = new SoDecomposeVec3f();
myEngine.vector.ConnectFrom(myDragger.translation);
myCone.bottomRadius.ConnectFrom(myEngine.x);
    

Java
 // Create myDragger with an initial translation of (1, 0, 0)
SoTranslate1Dragger myDragger = new SoTranslate1Dragger();
root.addChild(myDragger);
myDragger.translation.setValue(1f, 0f, 0f);

// Place an SoCone above myDragger
SoTransform myTransform = new SoTransform();
SoCone myCone = new SoCone();
root.addChild(myTransform);
root.addChild(myCone);
myTransform.translation.setValue(0, 3, 0);

// SoDecomposeVec3f engine extracts myDragger's x-component
// The result is connected to myCone's bottomRadius.
myEngine = new SoDecomposeVec3f();
myEngine.vector.connectFrom(myDragger.translation);
myCone.bottomRadius.connectFrom(myEngine.x);
    


Any dragger or manipulator can use callback functions to pass data back to the application. This callback mechanism can be used to augment the default functionality of the dragger or manipulator. Several lists of callback functions and associated data, of class SoCallbackList( C++ ), are automatically created when a dragger is constructed. You can add functions to and remove functions from these lists and pass a pointer to the user callback data. Draggers use these lists of callback functions:

The following methods add functions to and remove functions from these callback lists:

addStartCallback(functionName, userData) removeStartCallback(functionName, userData)

addMotionCallback(functionName, userData) removeMotionCallback(functionName, userData)

addValueChangedCallback(functionName, userData) removeValueChangedCallback(functionName, userData)

addFinishCallback(functionName, userData) removeFinishCallback(functionName, userData)

These methods are called on SoDragger( C++ | Java | .NET ). To call one of these methods on the manipulator, call getDragger() first, then call the callback list method.

For example, you could write a start callback function that would turn an object to wireframe during manipulation and a finish callback function that would turn it back to filled drawing style when manipulation finishes. You could write a value-changed callback to find out when the value being manipulated has changed, and then use the getValue() method to obtain the field's new value.

Example 17.2, “ Using Multiple Draggers ” uses three translate1Draggers to change the x, y, and z components of a translation that affects some 3D text. Figure 17.7, “ A Slider Box That Uses Draggers and Engines to Move Text shows two images created by this program.


Example 17.2.  Using Multiple Draggers


C++
//  Uses 3 translate1Draggers to change the x, y, and z 
//  components of a translation. A calculator engine assembles 
//  the components.
//  Arranges these draggers along edges of a box containing the
//  3D text to be moved.
//  The 3D text and the box are made with SoShapeKits

#include <Inventor/engines/SoCalculator.h>
#include <Inventor/nodekits/SoShapeKit.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoText3.h>
#include <Inventor/nodes/SoTransform.h>

#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>

#include <Inventor/draggers/SoTranslate1Dragger.h>

main(int , char **argv)
{
   Widget myWindow = SoXt::init(argv[0]);
   if (myWindow == NULL) exit(1);

   SoSeparator *root = new SoSeparator;
   root->ref();

   // Create 3 translate1Draggers and place them in space.
   SoSeparator *xDragSep = new SoSeparator;
   SoSeparator *yDragSep = new SoSeparator;
   SoSeparator *zDragSep = new SoSeparator;
   root->addChild(xDragSep);
   root->addChild(yDragSep);
   root->addChild(zDragSep);
   // Separators will each hold a different transform
   SoTransform *xDragXf = new SoTransform;
   SoTransform *yDragXf = new SoTransform;
   SoTransform *zDragXf = new SoTransform;
   xDragXf->set("translation 0 -4 8");
   yDragXf->set("translation -8 0 8 rotation 0 0 1 1.57");
   zDragXf->set("translation -8 -4 0 rotation 0 1 0 -1.57");
   xDragSep->addChild(xDragXf);
   yDragSep->addChild(yDragXf);
   zDragSep->addChild(zDragXf);

   // Add the draggers under the separators, after transforms
   SoTranslate1Dragger *xDragger = new SoTranslate1Dragger;
   SoTranslate1Dragger *yDragger = new SoTranslate1Dragger;
   SoTranslate1Dragger *zDragger = new SoTranslate1Dragger;
   xDragSep->addChild(xDragger);
   yDragSep->addChild(yDragger);
   zDragSep->addChild(zDragger);

   // Create shape kit for the 3D text
   // The text says 'Slide Arrows To Move Me'
   SoShapeKit *textKit = new SoShapeKit;
   root->addChild(textKit);
   SoText3 *myText3 = new SoText3;
   textKit->setPart("shape", myText3);
   myText3->justification = SoText3::CENTER;
   myText3->string.set1Value(0,"Slide Arrows");
   myText3->string.set1Value(1,"To");
   myText3->string.set1Value(2,"Move Me");
   textKit->set("font { size 2}");
   textKit->set("material { diffuseColor 1 1 0}");

   // Create shape kit for surrounding box.
   // It's an unpickable cube, sized as (16,8,16)
   SoShapeKit *boxKit = new SoShapeKit;
   root->addChild(boxKit);
   boxKit->setPart("shape", new SoCube);
   boxKit->set("drawStyle { style LINES }");
   boxKit->set("pickStyle { style UNPICKABLE }");
   boxKit->set("material { emissiveColor 1 0 1 }");
   boxKit->set("shape { width 16 height 8 depth 16 }");

   // Create the calculator to make a translation
   // for the text. The x component of a translate1Dragger's 
   // translation field shows how far it moved in that 
   // direction. So our text's translation is:
   // (xDragTranslate[0],yDragTranslate[0],zDragTranslate[0])
   SoCalculator *myCalc = new SoCalculator;
   myCalc->ref();
   myCalc->A.connectFrom(&xDragger->translation);
   myCalc->B.connectFrom(&yDragger->translation);
   myCalc->C.connectFrom(&zDragger->translation);
   myCalc->expression = "oA = vec3f(A[0],B[0],C[0])";

   // Connect the the translation in textKit from myCalc
   SoTransform *textXf = (SoTransform *)
            textKit->getPart("transform",TRUE);
   textXf->translation.connectFrom(&myCalc->oA);

   SoXtExaminerViewer *myViewer = new
            SoXtExaminerViewer(myWindow);
   myViewer->setSceneGraph(root);
   myViewer->setTitle("Slider Box");
   myViewer->viewAll();
   myViewer->show();

   SoXt::show(myWindow);
   SoXt::mainLoop();
}
                      

.NET
/*----------------------------------------------------------------
 *  Uses 3 translate1Draggers to change the x, y, and z 
 *  components of a translation. A calculator engine assembles 
 *  the components.
 *  Arranges these draggers along edges of a box containing the
 *  3D text to be moved.
 *  The 3D text and the box are made with SoShapeKits
 *----------------------------------------------------------------*/
using System.Windows.Forms;

using OIV.Inventor.Nodes;
using OIV.Inventor.Win.Viewers;
using OIV.Inventor.Engines;
using OIV.Inventor.Draggers;
using OIV.Inventor.Nodekits;

namespace _15_2_SliderBox
{
    public partial class MainForm : Form
    {
        SoWinExaminerViewer myViewer;
        SoCalculator myCalc;

        public MainForm()
        {
            InitializeComponent();
            CreateSample();
        }

        public void CreateSample()
        {
            SoSeparator root = new SoSeparator();

            // Create 3 translate1Draggers and place them in space.
            SoSeparator xDragSep = new SoSeparator();
            SoSeparator yDragSep = new SoSeparator();
            SoSeparator zDragSep = new SoSeparator();
            root.AddChild(xDragSep);
            root.AddChild(yDragSep);
            root.AddChild(zDragSep);
            // Separators will each hold a different transform
            SoTransform xDragXf = new SoTransform();
            SoTransform yDragXf = new SoTransform();
            SoTransform zDragXf = new SoTransform();
            xDragXf.Set("translation  0 -4 8");
            yDragXf.Set("translation -8  0 8 rotation 0 0 1  1.57");
            zDragXf.Set("translation -8 -4 0 rotation 0 1 0 -1.57");
            xDragSep.AddChild(xDragXf);
            yDragSep.AddChild(yDragXf);
            zDragSep.AddChild(zDragXf);

            // Add the draggers under the separators, after transforms
            SoTranslate1Dragger xDragger = new SoTranslate1Dragger();
            SoTranslate1Dragger yDragger = new SoTranslate1Dragger();
            SoTranslate1Dragger zDragger = new SoTranslate1Dragger();
            xDragSep.AddChild(xDragger);
            yDragSep.AddChild(yDragger);
            zDragSep.AddChild(zDragger);

            // Create shape kit for the 3D text
            // The text says 'Slide Cubes To Move Me'
            SoShapeKit textKit = new SoShapeKit();
            root.AddChild(textKit);
            SoText3 myText3 = new SoText3();
            textKit.SetPart("shape", myText3);
            myText3.justification.Value = SoText3.Justifications.CENTER;
            myText3.stringField[0] = "Slide Arrows";
            myText3.stringField[1] = "To";
            myText3.stringField[2] = "Move Me";
            textKit.Set("font { size 2}");
            textKit.Set("material { diffuseColor 1 1 0}");

            // Create shape kit for surrounding box.
            // It's an unpickable cube, sized as (16,8,16)
            SoShapeKit boxKit = new SoShapeKit();
            root.AddChild(boxKit);
            boxKit.SetPart("shape", new SoCube());
            boxKit.Set("drawStyle { style LINES }");
            boxKit.Set("pickStyle { style UNPICKABLE }");
            boxKit.Set("material { emissiveColor 1 0 1 }");
            boxKit.Set("shape { width 16 height 8 depth 16 }");

            // Create the calculator to make a translation
            // for the text.  The x component of a translate1Dragger's 
            // translation field shows how far it moved in that 
            // direction. So our text's translation is:
            // (xDragTranslate[0],yDragTranslate[0],zDragTranslate[0])
            myCalc = new SoCalculator();

            myCalc.A.ConnectFrom(xDragger.translation);
            myCalc.B.ConnectFrom(yDragger.translation);
            myCalc.C.ConnectFrom(zDragger.translation);
            myCalc.expression.SetValue("oA = vec3f(A[0],B[0],C[0])");

            // Connect the the translation in textKit from myCalc
            SoTransform textXf = (SoTransform)textKit.GetPart("transform", true);
            textXf.translation.ConnectFrom(myCalc.oA);

            myViewer = new SoWinExaminerViewer(this, "", true,
                SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.BROWSER);
            myViewer.SetSceneGraph(root);
            myViewer.SetTitle("Slider Box");
            myViewer.ViewAll();
        }
    }
}
                    

Java
/*----------------------------------------------------------------
 *  Uses 3 translate1Draggers to change the x, y, and z 
 *  components of a translation. A calculator engine assembles 
 *  the components.
 *  Arranges these draggers along edges of a box containing the
 *  3D text to be moved.
 *  The 3D text and the box are made with SoShapeKits
 *----------------------------------------------------------------*/

package inventor.mentor.sliderBox; 
import tools.*;

import com.openinventor.inventor.nodes.*;
import com.openinventor.inventor.awt.*;
import com.openinventor.inventor.engines.*;
import com.openinventor.inventor.nodekits.*;
import com.openinventor.inventor.draggers.*;

import java.awt.*;

public class Main extends DemoInventor {

  public static void main(String[] args) {
    Main applet = new Main();
    DemoInventor.isAnApplet = false;
    applet.start();
    demoMain(applet, "Slider Box");
  }

  public void start() {
    super.start();

    // Create 3 translate1Draggers and place them in space.
    SoSeparator xDragSep = new SoSeparator();
    SoSeparator yDragSep = new SoSeparator();
    SoSeparator zDragSep = new SoSeparator();

    // Separators will each hold a different transform
    SoTransform xDragXf = new SoTransform();
    SoTransform yDragXf = new SoTransform();
    SoTransform zDragXf = new SoTransform();
    xDragXf.set("translation  0 -4 8");
    yDragXf.set("translation -8  0 8 rotation 0 0 1  1.57");
    zDragXf.set("translation -8 -4 0 rotation 0 1 0 -1.57");
    xDragSep.addChild(xDragXf);
    yDragSep.addChild(yDragXf);
    zDragSep.addChild(zDragXf);

    // Add the draggers under the separators, after transforms
    SoTranslate1Dragger xDragger = new SoTranslate1Dragger();
    SoTranslate1Dragger yDragger = new SoTranslate1Dragger();
    SoTranslate1Dragger zDragger = new SoTranslate1Dragger();
    xDragSep.addChild(xDragger);
    yDragSep.addChild(yDragger);
    zDragSep.addChild(zDragger);

    // Create shape kit for the 3D text
    // The text says 'Slide Cubes To Move Me'
    SoShapeKit textKit = new SoShapeKit();
    SoText3 myText3 = new SoText3();
    textKit.setPart("shape", myText3);
    myText3.justification.setValue(SoText3.Justifications.CENTER);
    myText3.string.set1Value(0,"Slide Arrows");
    myText3.string.set1Value(1,"To");
    myText3.string.set1Value(2,"Move Me");
    textKit.set("font { size 2}");
    textKit.set("material { diffuseColor 1 1 0}");

    // Create shape kit for surrounding box.
    // It's an unpickable cube, sized as (16,8,16)
    SoShapeKit boxKit = new SoShapeKit();
    boxKit.setPart("shape", new SoCube());
    boxKit.set("drawStyle { style LINES }");
    boxKit.set("pickStyle { style UNPICKABLE }");
    boxKit.set("material { emissiveColor 1 0 1 }");
    boxKit.set("shape {width 16 height 8 depth 16}");

    // Create the calculator to make a translation
    // for the text.  The x component of a translate1Dragger's
    // translation field shows how far it moved in that
    // direction. So our text's translation is:
    // (xDragTranslate[0],yDragTranslate[0],zDragTranslate[0])
    SoCalculator myCalc = new SoCalculator();
    myCalc.A.connectFrom(xDragger.translation);
    myCalc.B.connectFrom(yDragger.translation);
    myCalc.C.connectFrom(zDragger.translation);
    myCalc.expression.setValue("oA = vec3f(A[0],B[0],C[0])");

    // Connect the translation in textKit from myCalc
    SoTransform textXf = (SoTransform)textKit.getPart("transform", true);
    textXf.translation.connectFrom(myCalc.oA);

    SoSeparator root = new SoSeparator();
    { // Assemble scene graph
      root.addChild(xDragSep);
      root.addChild(yDragSep);
      root.addChild(zDragSep);
      root.addChild(textKit);
      root.addChild(boxKit);
    }

    SwSimpleViewer myViewer = new SwSimpleViewer();
    myViewer.setSceneGraph(root);

    setLayout(new BorderLayout());
    add(myViewer, BorderLayout.CENTER);
  }
}