7.11. JumpingJackKit

This section describes one more new class of node kit, JumpingJackKit. It is a stick figure of a man that responds to mouse events by moving his arms and legs back and forth. Figure 7.6, “ Catalog Diagram for JumpingJackKit shows a diagram of Jack's catalog.

All parts of the body (“head,” “body,” “leftArm,” “rightArm,” “leftLeg,” “rightLeg”) are of type SoShapeKit( C++ | Java | .NET ), so that users can replace them with any shape they want.

An SoEventCallback( C++ | Java | .NET ) node is added as a child of the “callbackList” part. When the mouse goes down over Jack, this node's callback animates the body by editing the transforms of the arm and leg body parts.

Catalog Diagram for JumpingJackKit

Figure 7.6.  Catalog Diagram for JumpingJackKit


Example 7.5, “ JumpingJackKit.h shows the header file for JumpingJackKit.

Example 7.5.  JumpingJackKit.h


C++
#include <Inventor/nodekits/SoBaseKit.h>

class SoEventCallback;

class JumpingJackKit : public SoBaseKit {

   SO_KIT_HEADER(JumpingJackKit);

   SO_KIT_CATALOG_ENTRY_HEADER(body);
   SO_KIT_CATALOG_ENTRY_HEADER(head);
   SO_KIT_CATALOG_ENTRY_HEADER(leftArm);
   SO_KIT_CATALOG_ENTRY_HEADER(rightArm);
   SO_KIT_CATALOG_ENTRY_HEADER(leftLeg);
   SO_KIT_CATALOG_ENTRY_HEADER(rightLeg);

  public:
     JumpingJackKit();

     // Overrides default method. All the parts are shapeKits,
     // so this node will not affect the state.
     virtual SbBool affectsState() const;

   static void initClass();
   static void exitClass();

  private:

   // Constructor calls to build and set up parts.
   void createInitialJack();

   // An SoEventCallback will be inserted into the 
   // "callbackList" (inherited from SoBaseKit) as the part 
   // "callbackList[0]". This routine jumpJackJump() will be 
   // set as the callback function for that part. It is this 
   // routine which changes the angles in the joints.
   static void jumpJackJump(void *userData, 
     SoEventCallback *eventCB);

   virtual ~JumpingJackKit();
};

The constructor for JumpingJackKit calls createInitialJack(). This routine, shown in Example 7.6, “ JumpingJackKit.c++, constructs the man, moves the parts to a starting position, and creates an SoEventCallback( C++ | Java | .NET ) node, which it installs as “callbackList[0].” The constructor also creates the node-kit catalog and performs other standard construction tasks.

The callback is called “jumpJackJump.” Basically, it sees if the mouse-down event occurred over the object. If so, then the limbs are rotated.

Example 7.6.  JumpingJackKit.c++


C++
#include <Inventor/SoPickedPoint.h>
#include <Inventor/events/SoMouseButtonEvent.h>
#include <Inventor/nodekits/SoShapeKit.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoCylinder.h>
#include <Inventor/nodes/SoEventCallback.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoTransform.h>

#include "JumpingJackKit.h"

SO_KIT_SOURCE(JumpingJackKit);

void
JumpingJackKit::initClass()
{
   SO_KIT_INIT_CLASS(JumpingJackKit,SoBaseKit, "BaseKit");
}

void
JumpingJackKit::exitClass()
{
   SO__KIT_EXIT_CLASS(JumpingJackKit);
}

JumpingJackKit::JumpingJackKit()
{
   SO_KIT_CONSTRUCTOR(JumpingJackKit);

   // Add the body parts to the catalog...
   SO_KIT_ADD_CATALOG_ENTRY(body, SoShapeKit, 
                            TRUE, this, "", TRUE);
   SO_KIT_ADD_CATALOG_ENTRY(head, SoShapeKit, 
                            TRUE, this, "", TRUE);
   SO_KIT_ADD_CATALOG_ENTRY(leftArm, SoShapeKit, 
                            TRUE, this, "", TRUE);
   SO_KIT_ADD_CATALOG_ENTRY(rightArm, SoShapeKit, 
                            TRUE, this, "", TRUE);
   SO_KIT_ADD_CATALOG_ENTRY(leftLeg, SoShapeKit, 
                            TRUE, this, "", TRUE);
   SO_KIT_ADD_CATALOG_ENTRY(rightLeg, SoShapeKit, 
                            TRUE, this, "", TRUE);

   SO_KIT_INIT_INSTANCE();

   createInitialJack();
}

JumpingJackKit::~JumpingJackKit()
{
}

// This kit is made up entirely of SoShapeKits.
// Since SoShapeKits do not affect state, neither does this.
SbBool
JumpingJackKit::affectsState() const
{
   return FALSE;
}

// Set up parts for default configuration of the jumping jack
void
JumpingJackKit::createInitialJack()
{
   // Create the head.
   SoSphere *headSphere = new SoSphere;
   setPart("head.shape", headSphere);

   // Create the body.
   SoCube *bodyCube = new SoCube;
   setPart("body.shape", bodyCube);

   // Create the limbs
   SoCylinder *limbCylinder = new SoCylinder;
   setPart("leftLeg.shape",  limbCylinder);
   setPart("leftArm.shape",  limbCylinder);
   setPart("rightLeg.shape", limbCylinder);
   setPart("rightArm.shape", limbCylinder);

   // Place the body and head
   set("body.transform", "scaleFactor 1 2 1");
   set("head.transform", "translation 0 3 0");

   // Place the limbs
   set("leftArm.transform",  "scaleFactor 0.5 1.5 0.5");
   set("leftLeg.transform",  "scaleFactor 0.5 1.5 0.5");
   set("rightArm.transform", "scaleFactor 0.5 1.5 0.5");
   set("rightLeg.transform", "scaleFactor 0.5 1.5 0.5");
   set("leftArm.transform",  "center 0 1 0");
   set("leftLeg.transform",  "center 0 1 0");
   set("rightArm.transform", "center 0 1 0");
   set("rightLeg.transform", "center 0 1 0");
   set("leftArm.transform",  "translation -1  1   0.5");
   set("leftLeg.transform",  "translation -1 -2.5 0.5");
   set("rightArm.transform", "translation  1  1   0.5");
   set("rightLeg.transform", "translation  1 -2.5 0.5");

   // Create the Event Callback to make jack jump.
   // When it receives a mouse button event, it will
   // call the method jumpJackJump.
   SoEventCallback *myEventCB = new SoEventCallback;
   myEventCB->addEventCallback(
           SoMouseButtonEvent::getClassTypeId(), 
           JumpingJackKit::jumpJackJump, this);
   setPart("callbackList[0]", myEventCB);
}

// Animates the jumping jack (called by the "eventCallback[0]" 
// part when a left mouse button press occurs).
void
JumpingJackKit::jumpJackJump(void *userData, 
                             SoEventCallback *myEventCB)
{
   const SoEvent *myEvent = myEventCB->getEvent();

   // See if it's a left mouse down event
   if (SO_MOUSE_PRESS_EVENT(myEvent, BUTTON1)) {
     JumpingJackKit *myJack = (JumpingJackKit *) userData;

     // See if the jumping jack was picked.
     const SoPickedPoint *myPickedPoint;
     myPickedPoint = myEventCB->getPickedPoint();
     if (myPickedPoint && myPickedPoint->getPath() &&
         myPickedPoint->getPath()->containsNode(myJack)) {

       // The jumping jack was picked. Make it jump!
       SoTransform *myXf;
       SbVec3f zAxis(0,0,1);
       SbRotation noRot = SbRotation::identity();

       myXf = SO_GET_PART(myJack,
                          "leftArm.transform",SoTransform);
       if (myXf->rotation.getValue() == noRot)
         myXf->rotation.setValue(zAxis ,-1.6);
       else
         myXf->rotation.setValue(noRot);

       myXf = SO_GET_PART(myJack,
                          "leftLeg.transform",SoTransform);
       if (myXf->rotation.getValue() == noRot)
         myXf->rotation.setValue(zAxis ,-1.2);
       else
         myXf->rotation.setValue(noRot);
       myXf = SO_GET_PART(myJack,
                          "rightArm.transform",SoTransform);
       if (myXf->rotation.getValue() == noRot)
         myXf->rotation.setValue(zAxis , 1.6);
       else
         myXf->rotation.setValue(noRot);

       myXf = SO_GET_PART(myJack,
                          "rightLeg.transform",SoTransform);
       if (myXf->rotation.getValue() == noRot)
         myXf->rotation.setValue(zAxis , 1.2);
       else
         myXf->rotation.setValue(noRot);
       myEventCB->setHandled();
     }
   }
}

Example 7.7, “ JumpingJackTest.c++ shows a short program to create a jumping jack that responds to user events. Notice how it calls initClass() for JumpingJackKit before it creates an instance of this new node-kit class.

Example 7.7.  JumpingJackTest.c++


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

// Header files for new node class
#include "JumpingJackKit.h"

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

   // Initialize the new node class
   JumpingJackKit::initClass();

   JumpingJackKit *jackyBaby = new JumpingJackKit;
   jackyBaby->ref();

   SoXtExaminerViewer *viewer = 
     new SoXtExaminerViewer(myWindow);
   viewer->setSceneGraph(jackyBaby);
   viewer->setTitle("JumpingJackKit");
   viewer->show();
   viewer->viewAll();

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