text: /*
   *
   *  Copyright (C) 2000 Silicon Graphics, Inc.  All Rights Reserved. 
   *
   *  This library is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU Lesser General Public
   *  License as published by the Free Software Foundation; either
   *  version 2.1 of the License, or (at your option) any later version.
   *
   *  This library is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   *  Lesser General Public License for more details.
   *
   *  Further, this software is distributed without any warranty that it is
   *  free of the rightful claim of any third person regarding infringement
   *  or the like.  Any license provided herein, whether implied or
   *  otherwise, applies only to this software file.  Patent licenses, if
   *  any, provided herein do not apply to combinations of this program with
   *  other software, or any other product whatsoever.
   * 
   *  You should have received a copy of the GNU Lesser General Public
   *  License along with this library; if not, write to the Free Software
   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   *
   *  Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
   *  Mountain View, CA  94043, or:
   * 
   *  http://www.sgi.com 
   * 
   *  For further information regarding this notice, see: 
   * 
   *  http://oss.sgi.com/projects/GenInfo/NoticeExplan/
   *
   */
  
  /*--------------------------------------------------------------
   *  This is an example from the Inventor Toolmaker,
   *  chapter 10.
   * 
   *  This shows off several concepts:
   *      + the use of 'buildNow' in the constructor
   *      + the use of the visibility change callback
   *      + registering the widget
   *      + getting resources with XtResource
   *
   *  Source file for "SceneTumble" component.
   *------------------------------------------------------------*/
  
  #include <Xm/Form.h>
  #include <Xm/Scale.h>
  #include <Inventor/Xt/SoXtResource.h>
  #include <Inventor/nodes/SoDirectionalLight.h>
  #include <Inventor/nodes/SoPerspectiveCamera.h>
  #include <Inventor/nodes/SoRotation.h>
  #include <Inventor/nodes/SoSeparator.h>
  #include <Inventor/sensors/SoTimerSensor.h>
  #include "SceneTumble.h"
  
  #define MIN_SPEED 0
  #define MAX_SPEED 100
  
  // speed factor is a small angle
  #define SPEED_FACTOR (M_PI/3600.0)
  
  //
  // Public constructor.
  //
  
  SceneTumble::SceneTumble(
     Widget parent,
     const char *name, 
     SbBool buildInsideParent)
      : SoXtRenderArea(parent, name, buildInsideParent, 
  		    TRUE, TRUE, FALSE) // FALSE = don't build just yet
  {
     // Passing TRUE means build the component right now
     constructorCommon(TRUE);
  }
  
  
  //
  // Protected constructor for subclasses to call.
  //
  
  SceneTumble::SceneTumble(
     Widget parent,
     const char *name, 
     SbBool buildInsideParent, 
     SbBool buildNow)
      : SoXtRenderArea(parent, name, buildInsideParent, FALSE, FALSE)
  {
     // Subclass tells us whether to build now
     constructorCommon(buildNow);
  }
  
  
  //
  // Actual work done at construction time.
  //
  
  void
  SceneTumble::constructorCommon(SbBool buildNow)
  {
     speed = MAX_SPEED/2;
     
     animationSensor = 
       new SoTimerSensor(SceneTumble::animationSensorCB, this);
     animationSensor->setInterval(1/60.0); // 60 frames per second
  
     userScene = NULL;
     root = new SoSeparator;
     camera = new SoPerspectiveCamera;
     rotx = new SoRotation;
     roty = new SoRotation;
     rotz = new SoRotation;
     
     root->addChild(camera);
     root->addChild(new SoDirectionalLight);
     root->addChild(rotx);
     root->addChild(roty);
     root->addChild(rotz);
     root->ref();
     
     addVisibilityChangeCallback(visibilityChangeCB, this);
     setClassName("SceneTumble");
     setTitle("Tumble");
  
     // If we do not build now, the subclass will build when ready
     if (buildNow) {
       Widget w = buildWidget(getParentWidget());
       setBaseWidget(w);
     }
  }
  
  
  //
  // Destructor.
  //
  
  SceneTumble::~SceneTumble()
  {
     root->unref();
     delete animationSensor;
  }
  
  
  //
  // Set the scene graph to tumble. We add this scene graph
  // to our local graph so that we can rotate our own camera
  // to create the tumbling effect. Our local scene graph
  // root is passed to the render area for rendering.
  //
  
  void
  SceneTumble::setSceneGraph(SoNode *newScene)
  {
     // Replace the existing scene with this one
     if (userScene != NULL)
          root->replaceChild(userScene, newScene);
     else root->addChild(newScene);
     userScene = newScene;
     
     // Make certain the scene is in view
     camera->viewAll(root, getViewportRegion(), 2.0);
     
     // Render area will handle redraws for us
     SoXtRenderArea::setSceneGraph(root);
  }
  
  
  //
  // Return the users scene graph, not our local graph.
  //
  
  SoNode *
  SceneTumble::getSceneGraph()
  {
     return userScene;
  }
  
  
  //
  // Build the widget - create a form widget, and place
  // in it a render area and a scale slider to control
  // the speed.
  //
  
  Widget
  SceneTumble::buildWidget(Widget parent)
  {
     Arg args[8];
     int n;
  
     // Create a form widget as the container.
     Widget form = XtCreateWidget(getWidgetName(), xmFormWidgetClass, 
               parent, NULL, 0);
     
     // Register the widget, so we can get resources
     registerWidget(form);
     
     // Get our starting speed from the resource.
     // Resource file should say:
     //    *SceneTumble*speed: <int between 0 and 100>
     short s;
     SoXtResource xr(form);
     if (xr.getResource("speed", "Speed", s)) {
        if (s > MAX_SPEED)
           speed = MAX_SPEED;
        else if (s < MIN_SPEED)
           speed = MIN_SPEED;
        else 
           speed = s;
     }
       
     // Create render area
     Widget raWidget = SoXtRenderArea::buildWidget(form);
  
     // Create slider to control speed
     n = 0;
     XtSetArg(args[n], XmNminimum, MIN_SPEED); n++;
     XtSetArg(args[n], XmNmaximum, MAX_SPEED); n++;
     XtSetArg(args[n], XmNvalue, speed); n++;
     XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
     speedSlider =
       XtCreateWidget("Speed", xmScaleWidgetClass, form, args, n);
  
     // Callbacks on the slider
     XtAddCallback(speedSlider, XmNdragCallback,
       SceneTumble::speedCB, this);
     XtAddCallback(speedSlider, XmNvalueChangedCallback,
       SceneTumble::speedCB, this);
     
     // Layout 
     n = 0;
     XtSetArg(args[n], XmNtopAttachment,	    XmNONE); n++;
     XtSetArg(args[n], XmNleftAttachment,	    XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNbottomAttachment,   XmATTACH_FORM); n++;
     XtSetValues(speedSlider, args, n);
     
     n = 0;
     XtSetArg(args[n], XmNtopAttachment,	    XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNleftAttachment,	    XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNbottomAttachment,   XmATTACH_WIDGET); n++;
     XtSetArg(args[n], XmNbottomWidget,	    speedSlider); n++;
     XtSetValues(raWidget, args, n);
     
     // Make the widgets visible
     XtManageChild(speedSlider);
     XtManageChild(raWidget);
     
     return form;
  }
  
  
  // 
  // Do the tumble animation. This entails updating our three
  // rotation nodes, one each for the x,y,and z axes.
  //
  
  void
  SceneTumble::doTumbleAnimation()
  { 
     SbRotation r;
     float angle;
     
     // Rotate about three axes in three speeds
     angle = speed * SPEED_FACTOR;
     r = rotx->rotation.getValue() * SbRotation(SbVec3f(1, 0, 0), angle);
     rotx->rotation.setValue(r);
  
     angle = speed * SPEED_FACTOR * 1.5;
     r = roty->rotation.getValue() * SbRotation(SbVec3f(0, 1, 0), angle);
     roty->rotation.setValue(r);
  
     angle = speed * SPEED_FACTOR * 2.0;
     r = rotz->rotation.getValue() * SbRotation(SbVec3f(0, 0, 1), angle);
     rotz->rotation.setValue(r);
  }
  
  
  //
  // Turn tumbling on and off. We simply schedule or unschedule
  // the animation sensor.
  //
  
  void
  SceneTumble::setTumbling(SbBool onOff)
  { 
     if (onOff) 
          animationSensor->schedule();
     else animationSensor->unschedule();
  }
  
  
  // 
  // Return whether we are tumbling.
  //
  
  SbBool
  SceneTumble::isTumbling() const
  {
     return animationSensor->isScheduled();
  }
  
  
  // 
  // This is called when the render area visibility changes
  // because it is shown, hidden, or iconified. If the 
  // component is not visible, we turn off the tumble animation.
  // 
  
  void
  SceneTumble::visibilityChangeCB(void *userData, SbBool visible)
  {
     // Set tumbling on when the component is visible,
     // and set it off when the component is not visible.
     SceneTumble *tumbler = (SceneTumble *) userData;   
     tumbler->setTumbling(visible);
  }
  
  
  // 
  // Animation sensor callback keeps the tumbling going.
  //
  
  void
  SceneTumble::animationSensorCB(void *userData, SoSensor *)
  { 
     ((SceneTumble *) userData)->doTumbleAnimation();
  }
  
  
  // 
  // This is invoked when the speed slider changes value.
  // We use the value of the slider to change the tumble speed.
  //
  
  void
  SceneTumble::speedCB(Widget, XtPointer userData, XtPointer clientData)
  {
     SceneTumble *tumbler = (SceneTumble *) userData;
     XmScaleCallbackStruct *data = (XmScaleCallbackStruct *) clientData;
     tumbler->setSpeed(data->value);
  }
  
  