[BACK]Return to pfiSphericD.C CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfuiD

File: [Development] / performer / src / lib / libpfuiD / pfiSphericD.C (download)

Revision 1.1, Tue Nov 21 21:39:36 2000 UTC (16 years, 10 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * WARNING! DO NOT EDIT!
 * pfiSphericD.C automatically generated from ../../lib/libpfui/pfiSpheric.C
 */
/*
 * Copyright 1993, 1994, 1995, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
 */


/*
 * file: pfiSphericD.C
 * ----------------------
 *
 * pfiSphericD motion model class routines
 *
 * $Revision: 1.1 $
 * $Date: 2000/11/21 21:39:36 $
 *
 */

#include <math.h>
#include <stdlib.h>
#include <Performer/prmath.h>

#define _PFI_XFORMER_C_
#include <Performer/pfuiD.h>
#include <Performer/pfuiD/pfiInputXformD.h>
#include <Performer/pfuiD/pfiSphericD.h>

typedef struct
{
  short                 numChildren;
  short                 hasIn;
  int                   children[PFIXSPH_MAX_CHILDREN];
  int                   parent;
  double                 minRadius, maxRadius;
  double                 farMinAround, farMaxAround;
  double                 nearMinAround, nearMaxAround;
  double                 farMinTilt, farMaxTilt;
  double                 nearMinTilt, nearMaxTilt;
  double                 farMinRoll, farMaxRoll;
  double                 nearMinRoll, nearMaxRoll;
  pfVec3d                center;
  double                 inRadius[PFIXSPH_MAX_CHILDREN], outRadius;
  double                 inAround[PFIXSPH_MAX_CHILDREN], outAround;
  double                 inTilt[PFIXSPH_MAX_CHILDREN], outTilt;
  double                 farMaxLinearVelocity, farMaxAngularVelocity;
  double                 farMaxRollVelocity;
  double                 nearMaxLinearVelocity, nearMaxAngularVelocity;
  double                 nearMaxRollVelocity;
} PathInfo;

/******************************************************************************
 *		Spheric XformerD Routines
 ******************************************************************************
 */
 

pfType *pfiInputXformDSpheric::classType = NULL;

double pfiInputXformDSpheric::getParameter(int param) {
    switch(param) {
    case PFIXSPH_RADIUS:
	return radius;
    case PFIXSPH_MIN_RADIUS:
	return minRadius;
    case PFIXSPH_MAX_RADIUS:
	return maxRadius;

    case PFIXSPH_AROUND:
	return around;
    case PFIXSPH_NEAR_MIN_AROUND:
	return nearMinAround;
    case PFIXSPH_NEAR_MAX_AROUND:
	return nearMaxAround;
    case PFIXSPH_FAR_MIN_AROUND:
	return farMinAround;
    case PFIXSPH_FAR_MAX_AROUND:
	return farMaxAround;

    case PFIXSPH_TILT:
	return tilt;
    case PFIXSPH_NEAR_MIN_TILT:
	return nearMinTilt;
    case PFIXSPH_NEAR_MAX_TILT:
	return nearMaxTilt;
    case PFIXSPH_FAR_MIN_TILT:
	return farMinTilt;
    case PFIXSPH_FAR_MAX_TILT:
	return farMaxTilt;

    case PFIXSPH_ROLL:
	return roll;
    case PFIXSPH_NEAR_MIN_ROLL:
	return nearMinRoll;
    case PFIXSPH_NEAR_MAX_ROLL:
	return nearMaxRoll;
    case PFIXSPH_FAR_MIN_ROLL:
	return farMinRoll;
    case PFIXSPH_FAR_MAX_ROLL:
	return farMaxRoll;

    case PFIXSPH_NEAR_MAX_LINEAR_VELOCITY:
	return nearMaxLinearVelocity;
    case PFIXSPH_NEAR_MAX_ANGULAR_VELOCITY:
	return nearMaxAngularVelocity;
    case PFIXSPH_NEAR_MAX_ROLL_VELOCITY:
	return nearMaxRollVelocity;
    case PFIXSPH_FAR_MAX_LINEAR_VELOCITY:
	return farMaxLinearVelocity;
    case PFIXSPH_FAR_MAX_ANGULAR_VELOCITY:
	return farMaxAngularVelocity;
    case PFIXSPH_FAR_MAX_ROLL_VELOCITY:
	return farMaxRollVelocity;

    case PFIXSPH_CENTER_X:
	return center[PF_X];
    case PFIXSPH_CENTER_Y:
	return center[PF_Y];
    case PFIXSPH_CENTER_Z:
	return center[PF_Z];

    case PFIXSPH_INT_NUM_CHILDREN:
	return (double) numChildren;
    case PFIXSPH_INT_AT_RADIUS:
	return (double) atRadius;
    case PFIXSPH_INT_AT_AROUND:
	return (double) atAround;
    case PFIXSPH_INT_AT_TILT:
	return (double) atTilt;

    case PFIXSPH_INT_CHILD_NUM:
	return (double) childNum;

    case PFIXSPH_OUT_RADIUS:
	return outRadius;

    case PFIXSPH_IN_RADIUS_0:
    case PFIXSPH_IN_RADIUS_1:
	return inRadius[param - PFIXSPH_IN_RADIUS_0];

    case PFIXSPH_OUT_AROUND:
	return outAround;

    case PFIXSPH_IN_AROUND_0:
    case PFIXSPH_IN_AROUND_1:
	return inAround[param - PFIXSPH_IN_AROUND_0];

    case PFIXSPH_OUT_TILT:
	return outTilt;

    case PFIXSPH_IN_TILT_0:
    case PFIXSPH_IN_TILT_1:
	return inTilt[param - PFIXSPH_IN_TILT_0];

    default:
	pfNotify(PFNFY_WARN,PFNFY_USAGE, "unknown parameter - "
		 "pfiInputXformDSpheric::getParameter(%i)", param);
    } // switch
    // XXX - is there a way to return NaN ??
    return 0.0f;
} // pfiInputXformDSpheric::getParameter

void pfiInputXformDSpheric::setParameter(int param, double num) {
    switch(param) {
    case PFIXSPH_RADIUS:
	radius = num;
	break;
    case PFIXSPH_MIN_RADIUS:
	minRadius = num;
	break;
    case PFIXSPH_MAX_RADIUS:
	maxRadius = num;
	break;

    case PFIXSPH_AROUND:
	around = num;
	break;
    case PFIXSPH_NEAR_MIN_AROUND:
	nearMinAround = num;
	break;
    case PFIXSPH_NEAR_MAX_AROUND:
	nearMaxAround = num;
	break;
    case PFIXSPH_FAR_MIN_AROUND:
	farMinAround = num;
	break;
    case PFIXSPH_FAR_MAX_AROUND:
	farMaxAround = num;
	break;

    case PFIXSPH_TILT:
	tilt = num;
	break;
    case PFIXSPH_NEAR_MIN_TILT:
	nearMinTilt = num;
	break;
    case PFIXSPH_NEAR_MAX_TILT:
	nearMaxTilt = num;
	break;
    case PFIXSPH_FAR_MIN_TILT:
	farMinTilt = num;
	break;
    case PFIXSPH_FAR_MAX_TILT:
	farMaxTilt = num;
	break;

    case PFIXSPH_ROLL:
	roll = num;
	break;
    case PFIXSPH_NEAR_MIN_ROLL:
	nearMinRoll = num;
	break;
    case PFIXSPH_NEAR_MAX_ROLL:
	nearMaxRoll = num;
	break;
    case PFIXSPH_FAR_MIN_ROLL:
	farMinRoll = num;
	break;
    case PFIXSPH_FAR_MAX_ROLL:
	farMaxRoll = num;
	break;

    case PFIXSPH_NEAR_MAX_LINEAR_VELOCITY:
	nearMaxLinearVelocity = num;
	break;
    case PFIXSPH_NEAR_MAX_ANGULAR_VELOCITY:
	nearMaxAngularVelocity = num;
	break;
    case PFIXSPH_NEAR_MAX_ROLL_VELOCITY:
	nearMaxRollVelocity = num;
	break;
    case PFIXSPH_FAR_MAX_LINEAR_VELOCITY:
	farMaxLinearVelocity = num;
	break;
    case PFIXSPH_FAR_MAX_ANGULAR_VELOCITY:
	farMaxAngularVelocity = num;
	break;
    case PFIXSPH_FAR_MAX_ROLL_VELOCITY:
	farMaxRollVelocity = num;
	break;

    case PFIXSPH_CENTER_X:
	center[PF_X] = num;
	break;
    case PFIXSPH_CENTER_Y:
	center[PF_Y] = num;
	break;
    case PFIXSPH_CENTER_Z:
	center[PF_Z] = num;
	break;

    case PFIXSPH_INT_NUM_CHILDREN:
	numChildren = (int) num;
	break;
    case PFIXSPH_INT_AT_RADIUS:
	atRadius = (int) num;
	break;
    case PFIXSPH_INT_AT_AROUND:
	atAround = (int) num;
	break;
    case PFIXSPH_INT_AT_TILT:
	atTilt = (int) num;
	break;

    case PFIXSPH_INT_CHILD_NUM:
	childNum = (int) childNum;
	break;

    case PFIXSPH_OUT_RADIUS:
	outRadius = num;
	break;

    case PFIXSPH_IN_RADIUS_0:
    case PFIXSPH_IN_RADIUS_1:
	inRadius[param - PFIXSPH_IN_RADIUS_0] = num;
	hasIn = TRUE;
	break;

    case PFIXSPH_OUT_AROUND:
	outAround = num;
	break;

    case PFIXSPH_IN_AROUND_0:
    case PFIXSPH_IN_AROUND_1:
	inAround[param - PFIXSPH_IN_AROUND_0] = num;
	hasIn = TRUE;
	break;

    case PFIXSPH_OUT_TILT:
	outTilt = num;
	break;

    case PFIXSPH_IN_TILT_0:
    case PFIXSPH_IN_TILT_1:
	inTilt[param - PFIXSPH_IN_TILT_0] = num;
	hasIn = TRUE;
	break;

    default:
	pfNotify(PFNFY_WARN,PFNFY_USAGE, "unknown parameter - "
		 "pfiInputXformDSpheric::setParameter(%i)", param);
    } // switch
} // pfiInputXformDSpheric::setParameter

void
pfiInputXformDSpheric::init()
{
    if (classType == NULL)
    {
	pfiInputXformD::init();
	classType = 
	    new pfType(pfiInputXformDTravel::getClassType(), "pfiInputXformDSpheric");
    }
}

 
pfiInputXformDSpheric::pfiInputXformDSpheric(void)
{
    setType(classType);

    xTracking = FALSE;

    radius = 200.0f;
    minRadius = 0.0f;
    maxRadius = 100.0f;
    around = 0.0f;
    nearMinAround = -180.0f;
    nearMaxAround = 180.0f;
    farMinAround = -180.0f;
    farMaxAround = 180.0f;
    tilt = 0.0f;
    nearMinTilt = -180.0f;
    nearMaxTilt = 180.0f;
    farMinTilt = -180.0f;
    farMaxTilt = 180.0f;
    roll = 0.0f;
    nearMinRoll = -180.0f;
    nearMaxRoll = 180.0f;
    farMinRoll = -180.0f;
    farMaxRoll = 180.0f;

    nearMaxLinearVelocity = 30.0f;
    nearMaxAngularVelocity = 30.0f;
    nearMaxRollVelocity = 30.0f;
    
    farMaxLinearVelocity = 30.0f;
    farMaxAngularVelocity = 30.0f;
    farMaxRollVelocity = 30.0f;

    pathList = new pfList;
    
    numChildren = 0;
    hasIn = FALSE;
    int i;
    for(i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
	inRadius[i] = 0.0f;
    outRadius = 10.0f;
    for(i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
	inAround[i] = 180.0f;
    outAround = 0.0f;
    for(i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
	inTilt[i] = 0.0f;
    outTilt = 30.0f;

    // set up some path stuff
    numWorlds = -1;
    currentWorld = -1;
}

void
pfiInputXformDSpheric::_setBSphere(pfSphere* _sphere)
{
    xDBSphere = *_sphere;
    xDBSize =  2.0f * xDBSphere.radius;
    xDBSizeLg = logf(xDBSize);
    if (!(xUserSetMask & _PFIX_USET_RESET_POS))	    // -- If user did not specify reset_pos -- //
    {
	PFCOPY_VEC3(xMotionCoordD.startPos.xyz, xDBSphere.center);
	xMotionCoordD.startPos.xyz[1] -= xDBSize*1.5f;
	xMotionCoordD.startPos.xyz[2] += xDBSphere.radius*0.5f;
	PFSET_VEC3(xMotionCoordD.startPos.hpr, 0.0f, 0.0f, 0.0f);
    }
    if  (!(xUserSetMask & _PFIX_USET_START_MOTION))
    {
	xMotionCoordD.startAccel	= xMotionCoordD.accel = PF_MAX2(5.0f, xDBSize*0.006f);
    }
    if  (!(xUserSetMask & _PFIX_USET_MOTION_LIMITS))
    {
	xMotionCoordD.maxSpeed	= PF_MAX2(200.0f, xDBSize*5.0f);
	if (xMotionCoordD.minSpeed < 0.0f)
	    xMotionCoordD.minSpeed = -xMotionCoordD.maxSpeed;
	xMotionCoordD.maxAccel	= xDBSize*2.0f;
	if (xMotionCoordD.minAccel < 0.0f)
	    xMotionCoordD.minAccel = -xMotionCoordD.maxAccel;
    }
    if (!(xUserSetMask & _PFIX_USET_SPHERIC_LIMITS))	// If users have not set spheric props
    {
	    // --- Should set up all global spheric params
	minRadius = 1.01f * xDBSphere.radius;
	maxRadius = 10.0f * xDBSphere.radius;
	radius = (minRadius + maxRadius)/2.0f;	

	//
	// Values that seemed all right to me at the time...
	// Choose minRadius to correspond to the data available (or make it 0).
	// maxLinearVelocity is always 1/20 HAT per update (i.e. per frame?)
	// maxAngularVelocity is always the same as maxLinearVelocity
	// (i.e. can achieve same max airspeed) when you're at the equator.
	// -DH
	//
	static double _PFI_SPHERIC_MIN_RADIUS = -1;
	static double _PFI_SPHERIC_MAX_RADIUS = -1;
	if (_PFI_SPHERIC_MIN_RADIUS == -1)
	{
	    char *e;
	    e = getenv("_PFI_SPHERIC_MIN_RADIUS");
	    _PFI_SPHERIC_MIN_RADIUS = (e ? atof(e) : 1.001f);
	    e = getenv("_PFI_SPHERIC_MAX_RADIUS");
	    _PFI_SPHERIC_MAX_RADIUS = (e ? atof(e) : 8.0f);
	    /*
	    pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT,
		"_PFI_SPHERIC_MIN_RADIUS = %lf times sphere radius",
		_PFI_SPHERIC_MIN_RADIUS);
	    pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT,
		"_PFI_SPHERIC_MAX_RADIUS = %lf times sphere radius",
		_PFI_SPHERIC_MAX_RADIUS);
	    */
	}
	hasIn = FALSE;
	minRadius = _PFI_SPHERIC_MIN_RADIUS * xDBSphere.radius;
	maxRadius = _PFI_SPHERIC_MAX_RADIUS * xDBSphere.radius;
	radius = (minRadius + maxRadius)/2.0f; // XXX ??? clobber prev value?
	nearMaxLinearVelocity = (minRadius - xDBSphere.radius) / 20;
	farMaxLinearVelocity = (maxRadius - xDBSphere.radius) / 20;
	nearMaxAngularVelocity = nearMaxLinearVelocity/xDBSphere.radius * 180/M_PI;
	farMaxAngularVelocity = farMaxLinearVelocity/xDBSphere.radius * 180/M_PI;
    }
}

    // PURPOSE: Makes parameters valid for current location
    //	        Use if coord suddenly jumps or changes. (Reposition, collision, etc.)
void pfiInputXformDSpheric::recalc(void)
{
  double sinAround, cosAround;
  double sinTilt, cosTilt;

  // reset postion - generate radius, around, tilt - recalculate hpr...

  pfVec3d vec3 = xMotionCoordD.pos.xyz;
  vec3 -= center;
      
  radius = vec3.length();
  tilt = pfArcSind(xMotionCoordD.pos.xyz[PF_Z] /
			   radius);
  pfSinCosd(tilt, &sinTilt, &cosTilt);
  around = pfArcSind(xMotionCoordD.pos.xyz[PF_Y] /
			     (cosTilt * radius));
  roll = 0.0f;
  
  /* generate position and orientation */
  pfSinCosd(around, &sinAround, &cosAround);
  pfSinCosd(tilt, &sinTilt, &cosTilt);
  xMotionCoordD.pos.xyz[PF_X] = cosAround * cosTilt * radius;
  xMotionCoordD.pos.xyz[PF_Y] = sinAround * cosTilt * radius;
  xMotionCoordD.pos.xyz[PF_Z] = sinTilt * radius;

  xMotionCoordD.pos.hpr[PF_H] = around + 90.0f;
  xMotionCoordD.pos.hpr[PF_P] = -tilt;
  xMotionCoordD.pos.hpr[PF_R] = roll;

  xMotionCoordD.makeMat();
}

    // PURPOSE: This actually calculates the new Coord
void pfiInputXformDSpheric::generateLocation(void)
{
  double sinAround, cosAround;
  double sinTilt, cosTilt;

  /* generate position and orientation */
  pfSinCosd(around, &sinAround, &cosAround);
  pfSinCosd(tilt, &sinTilt, &cosTilt);
  xMotionCoordD.pos.xyz[PF_X] = cosAround * cosTilt * radius;
  xMotionCoordD.pos.xyz[PF_Y] = sinAround * cosTilt * radius;
  xMotionCoordD.pos.xyz[PF_Z] = sinTilt * radius;

  xMotionCoordD.pos.xyz += center;

  xMotionCoordD.pos.hpr[PF_H] = around + 90.0f;
  xMotionCoordD.pos.hpr[PF_P] = -tilt;
  xMotionCoordD.pos.hpr[PF_R] = roll;

  xMotionCoordD.makeMat();
}

void
pfiInputXformDSpheric::resetPosition(void)
{
  radius = outRadius;
  around = outTilt;
  tilt = outTilt;

  xMotionCoordD.prevPos = xMotionCoordD.pos;

  generateLocation();
}

void
pfiInputXformDSpheric::setMode(int _mode, int _val)
{
    switch(_mode)
    {
    case PFIXSPH_MODE_MOTION:
	if (xMotionMode == _val)
	    return;
	if (!xInMotion)
	    xMotionCoordD.speed = xMotionCoordD.startSpeed;
	xMotionMode = _val;
	break;
    case PFIXSPH_MODE_MOTION_MOD:
	xMotionMod = _val;
	break;
    case PFIXSPH_MODE_AUTO:
	xAuto = _val;
	break;
    default:
	pfNotify(PFNFY_WARN,PFNFY_USAGE,
	    "pfiInputXformDSpheric::setMode() - unknown mode %d", _mode);
	break;
    }
}

int
pfiInputXformDSpheric::getMode(int _mode) const
{
    switch(_mode)
    {
    case PFIXSPH_MODE_MOTION:
	return xMotionMode;
    case PFIXSPH_MODE_MOTION_MOD:
	return xMotionMod;
    case PFIXSPH_MODE_AUTO:
	return xAuto;
    default:
	pfNotify(PFNFY_WARN,PFNFY_USAGE,
	    "pfi2DInputXformDSpheric::getMode() - unknown mode %d", _mode);
	return -1;
    }
}

void pfiInputXformDSpheric::setWorld(int worldNumber, int in_or_out) {

  if ((worldNumber > numWorlds) || (worldNumber < 0))
    return;
    
#if 0
  if (worldNumber == (numWorlds)+1)
    worldNumber = 0;
  else if (worldNumber == -1)
    worldNumber = numWorlds;
#endif

  PathInfo *current = (PathInfo *) pathList->get(worldNumber);
  if (current == NULL) {
//    pfNotify(PFNFY_WARN, PFNFY_PRINT, "setWorld called with out of bounds world number %i\n", worldNumber);
    return;
  }

//   if (in_or_out == PFIXSPH_PATH_OUT)
//       pfNotify(PFNFY_INFO, PFNFY_PRINT, "setWorld %i OUT", worldNumber);
//   else
//       pfNotify(PFNFY_INFO, PFNFY_PRINT, "setWorld %i IN", worldNumber);

  int prevWorldNumber = currentWorld;
  currentWorld = worldNumber;

  if (in_or_out == PFIXSPH_PATH_OUT) {
      radius = current->outRadius;
      around = current->outAround;
      tilt = current->outTilt;
  }
  else {
      int childNum = 0;
      int i;
      for(i = 1; i < current->numChildren; i++)
	  if (current->children[i] == prevWorldNumber)
	      childNum = i;
      radius = current->inRadius[childNum];
      around = current->inAround[childNum];
      tilt = current->inTilt[childNum];
  }
  minRadius = current->minRadius;
  maxRadius = current->maxRadius;
  nearMinAround = current->nearMinAround;
  nearMaxAround = current->nearMaxAround;
  farMinAround = current->farMinAround;
  farMaxAround = current->farMaxAround;
  nearMinTilt = current->nearMinTilt;
  nearMaxTilt = current->nearMaxTilt;
  farMinTilt = current->farMinTilt;
  farMaxTilt = current->farMaxTilt;
  nearMinRoll = current->nearMinRoll;
  nearMaxRoll = current->nearMaxRoll;
  farMinRoll = current->farMinRoll;
  farMaxRoll = current->farMaxRoll;
  center = current->center;
  numChildren = current->numChildren;
  hasIn = current->hasIn;
  int i;
  for(i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
      inRadius[i] = current->inRadius[i];
  outRadius = current->outRadius;
  for(i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
      inAround[i] = current->inAround[i];
  outAround = current->outAround;
  for(i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
      inTilt[i] = current->inTilt[i];
  outTilt = current->outTilt;

  nearMaxLinearVelocity = current->nearMaxLinearVelocity;
  nearMaxAngularVelocity = current->nearMaxAngularVelocity;
  nearMaxRollVelocity = current->nearMaxRollVelocity;
  farMaxLinearVelocity = current->farMaxLinearVelocity;
  farMaxAngularVelocity = current->farMaxAngularVelocity;
  farMaxRollVelocity = current->farMaxRollVelocity;
  
  generateLocation();
  xMotionCoordD.prevPos = xMotionCoordD.pos;
  xMotionCoordD.startPos = xMotionCoordD.pos;
//   ViewState->xformer->xMotionCoordD.pos = xMotionCoordD.pos;

//   // Pass data here to draw process - in main.C
//   if (current->blend) {
//       for (chani=0; chani < ViewState->numChans; chani++) {
// 	  pd[chani]->transitioning = 1;
// 	  ViewState->chan[chani]->passChanData();
//       }
//   }
//   else if (pd[0]->transitioning == 1) { // just changed back
//       for (chani=0; chani < ViewState->numChans; chani++) {
// 	  pd[chani]->transitioning = 0;
// 	  ViewState->chan[chani]->passChanData();
//       }
//   }
  generateLocation();
  xMotionCoordD.prevPos = xMotionCoordD.pos;
  xMotionCoordD.startPos = xMotionCoordD.pos;

} // setWorld

void pfiInputXformDSpheric::readPathFile(char *filename) {
  int i, j;
  FILE *fp;
  char str[80];
  char tag[80];
  double fnum;
  int inum;
  PathInfo *current = NULL;
  int localNumWorlds = -1;
  int lines = 0;

  if (!(fp = fopen(filename, "r"))) {
    pfNotify(PFNFY_WARN, PFNFY_PRINT,
	     "spheric readPathFile couldn't open %s.\n", filename);
    return;
  }
  pfNotify(PFNFY_INFO, PFNFY_PRINT,
	     "spheric readPathFile reading %s...\n", filename);

  while (!feof(fp)) {
    fscanf(fp, "%[^\n]", str);
    fscanf(fp, "\n");
     lines++;
    sscanf(str, "%s %lf", tag, &fnum);
    inum = (int) fnum;
    if (tag[0] == '#')
      /* comment - do nothing */ ;
    else if (strcmp(tag, "world") == 0) {
      if (current != NULL) 
	pathList->set(localNumWorlds, current);
      localNumWorlds++;
      if (localNumWorlds != inum) {
	  pfNotify(PFNFY_WARN, PFNFY_PRINT, "World order in file must start "
		   "at 0 and go up by one each world...");
	  pfExit();
      }
      current = (PathInfo *) pathList->get(localNumWorlds);
      if (current == NULL)
	current = (PathInfo *) pfMalloc( sizeof(PathInfo),
					 pfGetSharedArena() );
      int i;
      current->numChildren = 0;
      current->hasIn = FALSE;
      for (i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
	  current->children[i] = -1;
      current->parent = -1;
      current->minRadius = 0.0f;
      current->maxRadius = 5000.0f;
      current->nearMinAround = -180.0f;
      current->nearMaxAround = 180.0f;
      current->farMinAround = -180.0f;
      current->farMaxAround = 180.0f;
      current->nearMinTilt = -180.0f;
      current->nearMaxTilt = 180.0f;
      current->farMinTilt = -180.0f;
      current->farMaxTilt = 180.0f;
      current->nearMinRoll = -180.0f;
      current->nearMaxRoll = 180.0f;
      current->farMinRoll = -180.0f;
      current->farMaxRoll = 180.0f;
      current->center.set(0.0f, 0.0f, 0.0f);
      for (i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
	  current->inRadius[i] = 20.0f;
      current->outRadius = 5000.0f;
      for (i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
	  current->inAround[i] = 0.0f;
      current->outAround = 0.0f;
      for (i = 0; i < PFIXSPH_MAX_CHILDREN; i++)
	  current->inTilt[i] = 0.0f;
      current->outTilt = 0.0f;
      current->nearMaxLinearVelocity = 1.0f;
      current->nearMaxAngularVelocity = 30.0f;
      current->nearMaxRollVelocity = 30.0f;
      current->farMaxLinearVelocity = 1.0f;
      current->farMaxAngularVelocity = 30.0f;
      current->farMaxRollVelocity = 30.0f;
    }
    else if (current == NULL) {
      pfNotify(PFNFY_FATAL, PFNFY_PRINT,
	       "First tag in paths must be <world>\n");
      pfExit();
    }
    else if (strcmp(tag, "child") == 0) {
	if (current->numChildren == PFIXSPH_MAX_CHILDREN)
	    pfNotify(PFNFY_WARN, PFNFY_PRINT, "Too many children (MAX %i)",
		     PFIXSPH_MAX_CHILDREN);
	else {
	    current->children[current->numChildren] = inum;
	    current->numChildren++;
	}
    }
    else if (strcmp(tag, "minRadius") == 0)
      current->minRadius = fnum;
    else if (strcmp(tag, "maxRadius") == 0)
      current->maxRadius = fnum;
    else if (strcmp(tag, "nearMinAround") == 0)
      current->nearMinAround = fnum;
    else if (strcmp(tag, "nearMaxAround") == 0)
      current->nearMaxAround = fnum;
    else if (strcmp(tag, "farMinAround") == 0)
      current->farMinAround = fnum;
    else if (strcmp(tag, "farMaxAround") == 0)
      current->farMaxAround = fnum;
    else if (strcmp(tag, "nearMinTilt") == 0)
      current->nearMinTilt = fnum;
    else if (strcmp(tag, "nearMaxTilt") == 0)
      current->nearMaxTilt = fnum;
    else if (strcmp(tag, "farMinTilt") == 0)
      current->farMinTilt = fnum;
    else if (strcmp(tag, "farMaxTilt") == 0)
      current->farMaxTilt = fnum;
    else if (strcmp(tag, "nearMinRoll") == 0)
      current->nearMinRoll = fnum;
    else if (strcmp(tag, "farMinRoll") == 0)
      current->farMinRoll = fnum;
    else if (strcmp(tag, "nearMaxRoll") == 0)
      current->nearMaxRoll = fnum;
    else if (strcmp(tag, "farMaxRoll") == 0)
      current->farMaxRoll = fnum;
    else if (strcmp(tag, "center") == 0)
      sscanf(str, "%s %lf %lf %lf", tag, &current->center[PF_X],
	     &current->center[PF_Y], &current->center[PF_Z]);
    else if (strcmp(tag, "inRadius") == 0) {
      current->inRadius[0] = fnum;
      current->hasIn = TRUE;
    }
    else if (strcmp(tag, "inRadius1") == 0) {
      current->inRadius[1] = fnum;
      current->hasIn = TRUE;
    }
    else if (strcmp(tag, "outRadius") == 0)
      current->outRadius = fnum;
    else if (strcmp(tag, "inAround") == 0) {
      current->inAround[0] = fnum;
      current->hasIn = TRUE;
    }
    else if (strcmp(tag, "inAround1") == 0) {
      current->inAround[1] = fnum;
      current->hasIn = TRUE;
    }
    else if (strcmp(tag, "outAround") == 0)
      current->outAround = fnum;
    else if (strcmp(tag, "inTilt") == 0) {
      current->inTilt[0] = fnum;
      current->hasIn = TRUE;
    }
    else if (strcmp(tag, "inTilt1") == 0) {
      current->inTilt[1] = fnum;
      current->hasIn = TRUE;
    }
    else if (strcmp(tag, "outTilt") == 0)
      current->outTilt = fnum;
    else if (strcmp(tag, "nearMaxLinearVelocity") == 0)
      current->nearMaxLinearVelocity = fnum;
    else if (strcmp(tag, "farMaxLinearVelocity") == 0)
      current->farMaxLinearVelocity = fnum;
    else if (strcmp(tag, "nearMaxAngularVelocity") == 0)
      current->nearMaxAngularVelocity = fnum;
    else if (strcmp(tag, "farMaxAngularVelocity") == 0)
      current->farMaxAngularVelocity = fnum;
    else if (strcmp(tag, "nearMaxRollVelocity") == 0)
      current->nearMaxRollVelocity = fnum;
    else if (strcmp(tag, "farMaxRollVelocity") == 0)
      current->farMaxRollVelocity = fnum;
    else
      pfNotify(PFNFY_WARN, PFNFY_PRINT,
	       "Unknown tag <%s> in paths on line %i.", tag, lines);
  }
  fclose(fp);

  if (current != NULL)
    pathList->set(localNumWorlds, current);

  numWorlds = localNumWorlds;

  // manage tree stuff
  for(i = 0; i <= (numWorlds); i++) {
      current = (PathInfo *) pathList->get(i);
      if (current == NULL) {
	  pfNotify(PFNFY_WARN, PFNFY_PRINT, "pathList[%i] returned NULL\n", i);
	  pfExit();
      }
      for(j = 0; j < current->numChildren; j++) {
	  PathInfo *child = (PathInfo *) pathList->get(current->children[j]);
	  if (child == NULL) {
	      pfNotify(PFNFY_WARN, PFNFY_PRINT, "pathList[%i] returned NULL\n",
		       j);
	      pfExit();
	  }
	  child->parent = i;
      }
  }
  // can't go backwards from the root
  current = (PathInfo *) pathList->get(0);
  current->parent = -1;

  pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "Done reading paths");
} // readPathFile

void pfiInputXformDSpheric::printPathStuff(void) {
    pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "Path stuff **********************\n");
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "numWorlds = %i\n", numWorlds);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "currentWorld = %i\n", currentWorld);

    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "center = %.12f %.12f %.12f\n", center[PF_X],
	     center[PF_Y], center[PF_Z]);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "radius = %.12f\n", radius);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "around = %.12f\n", around);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "tilt = %.12f\n", tilt);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "roll = %.12f\n", roll);

    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "min/max radius = %.12f / %.12f\n",
	     minRadius, maxRadius);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "far min/max around = %.12f / %.12f\n",
	     farMinAround, farMaxAround);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "near min/max around = %.12f / %.12f\n",
	     nearMinAround, nearMaxAround);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "far min/max tilt = %.12f / %.12f\n",
	     farMinTilt, farMaxTilt);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "near min/max tilt = %.12f / %.12f\n",
	     nearMinTilt, nearMaxTilt);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "far min/max roll = %.12f / %.12f\n",
	     farMinRoll, farMaxRoll);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE, "near min/max roll = %.12f / %.12f\n",
	     nearMinRoll, nearMaxRoll);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE,
	     "far/near max linear velocity = %.12f / %.12f\n",
	     farMaxLinearVelocity, nearMaxLinearVelocity);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE,
	     "far/near max angular velocity = %.12f / %.12f\n",
	     farMaxAngularVelocity, nearMaxAngularVelocity);
    pfNotify(PFNFY_NOTICE, PFNFY_MORE,
	     "far/near max roll velocity = %.12f / %.12f\n",
	     farMaxRollVelocity, nearMaxRollVelocity);

//     cerr << "xMotionCoordD.pos.xyz = "
// 	 << xMotionCoordD.pos.xyz[PF_X] << ", "
// 	 << xMotionCoordD.pos.xyz[PF_Y] << ", "
// 	 << xMotionCoordD.pos.xyz[PF_Z] << "\n";
}

int
pfiUpdate2DIXformDSpheric(pfiInputXformD *_ix, pfiInputCoordD *_ic, void *)
{
    pfiInputXformDSpheric *_spheric = (pfiInputXformDSpheric *)_ix;

    if (_spheric->xUpdateMode != PFI_CB_CONT)
	return _spheric->xUpdateMode;

    if (_spheric->xMotionMode == PFIXSPH_MOTION_STOP)
    {
	_spheric->stop();
	_spheric->xTracking = FALSE;
    }
    else if (_spheric->xInMotion)
	return _spheric->doUpdate(_ic);

    return PFI_CB_CONT;
}

pfiInputXformDSpheric *
pfiCreate2DIXformDSpheric(void *arena)
{
    pfiInputXformDSpheric *spheric;
    
    spheric = new(arena) pfiInputXformDSpheric;
    spheric->setInputCoordDPtr(new(arena) pfi2DInputCoordD);
    spheric->setUpdateFunc(pfiUpdate2DIXformDSpheric, NULL);
    return spheric;
}

int pfiInputXformDSpheric::doUpdate(pfiInputCoordD *_ic) {

    pfi2DInputCoordD *_icoord = (pfi2DInputCoordD *)_ic;
    double	    x, y;

    /* Apply quadratic-scaling to create deadened center */
    x = 0.5f - _icoord->getCoord(PF_X);
    x *= PF_ABS(x)*4.0f;
    y = 0.5f - _icoord->getCoord(PF_Y);
    y *= PF_ABS(y)*4.0f;

	// x and y should be between -1 and 1

    if (y > 1.0f)
	y = 1.0f;
    else if (y < -1.0f)
	y = -1.0;
    if (x > 1.0f)
	x = 1.0f;
    else if (x < -1.0f)
	x = -1.0f;

//	pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "x = %lf, y = %lf", x, y);
    
    double percentage = (radius - minRadius) / 
(maxRadius - minRadius);
	
	// a(c) + b(1-c) == ac + b - bc == (a-b)*c + b
    double maxAround = (farMaxAround - nearMaxAround) * 
percentage + nearMaxAround;
    double minAround = (farMinAround - nearMinAround) * 
percentage + nearMinAround;
    double maxTilt = (farMaxTilt - nearMaxTilt) * 
percentage + nearMaxTilt;
    double minTilt = (farMinTilt - nearMinTilt) * 
percentage + nearMinTilt;
			    
    double maxLinearVelocity =
(farMaxLinearVelocity - nearMaxLinearVelocity) *
percentage + nearMaxLinearVelocity;
    double maxAngularVelocity =
(farMaxAngularVelocity -
 nearMaxAngularVelocity) *
percentage + nearMaxAngularVelocity;
			    
    if (xMotionMod & PFIXSPH_MOTION_MOD_SPIN)
    {
	double maxRoll = (farMaxRoll - nearMaxRoll) *
    percentage + nearMaxRoll;
	double minRoll = (farMinRoll - nearMinRoll) *
    percentage + nearMinRoll;
	double maxRollVelocity =
    (farMaxRollVelocity - nearMaxRollVelocity) *
    percentage + nearMaxRollVelocity;

	roll += x * maxRollVelocity;
	if (roll >= 180.0f)
	    roll -= 360.0f;
	else if (roll <= -180.0f)
	    roll += 360.0f;
	if (roll >= maxRoll)
	    roll = maxRoll;
	else if (roll <= minRoll)
	    roll = minRoll;
    }
    else {
	roll = 0.0f;
    }

    switch(xMotionMode)
    {
    case PFIXSPH_MOTION_INOUT:
	radius += y * maxLinearVelocity;

	if (radius >= maxRadius)
	    radius = maxRadius;
	else if (radius <= minRadius)
	    radius = minRadius;

	// stay inside the shrinking lines
	if (around >= maxAround)
	    around = maxAround;
	else if (around <= minAround)
	    around = minAround;
	    
	if (tilt >= maxTilt)
	    tilt = maxTilt;
	else if (tilt <= minTilt)
	    tilt = minTilt;

	generateLocation();
	break;

    case PFIXSPH_MOTION_AROUND:
	around += x * maxAngularVelocity;
	tilt += y * maxAngularVelocity;

	if (around >= 180.0f)
	    around -= 360.0f;
	else if (around <= -180.0f)
	    around += 360.0f;
	if (around >= maxAround)
	    around = maxAround;
	else if (around <= minAround)
	    around = minAround;

	if (tilt >= 180.0f)
	    tilt -= 360.0f;
	else if (tilt <= -180.0f)
	    tilt += 360.0f;
	if (tilt >= maxTilt)
	    tilt = maxTilt;
	else if (tilt <= minTilt)
	    tilt = minTilt;

	generateLocation();
	break;

    case PFIXSPH_MOTION_TRACK_INOUT:	// --- Goto area of interest --- //
	// don't go to an area of interest if there isn't one...
	if (!hasIn)
	    return PFI_CB_CONT;

	int lastTracking = xTracking;
	if (y > 0.0f)
	    xTracking = PFIXSPH_PATH_OUT;
	else
	    xTracking = PFIXSPH_PATH_IN;

	if ((lastTracking == xTracking) &&
	    (atRadius && atAround && atTilt)) {
	    PathInfo *current = (PathInfo *) pathList->get(currentWorld);
	    if (current)
		if (xTracking == PFIXSPH_PATH_IN)
		    setWorld(current->children[childNum], PFIXSPH_PATH_OUT);
		else
		    setWorld(current->parent, PFIXSPH_PATH_IN);
	}

	double mult;
	double rLen[PFIXSPH_MAX_CHILDREN], aLen[PFIXSPH_MAX_CHILDREN],
    tLen[PFIXSPH_MAX_CHILDREN];
	int i;

	if (y > 0.0f) {
	    for (i = 0; i < PF_MAX2(1,numChildren); i++) {
		rLen[i] = outRadius - radius;
		aLen[i] = outAround - around;
		tLen[i] = outTilt - tilt;
	    }
	}
	else {
	    for (i = 0; i < PF_MAX2(1,numChildren); i++) {
		rLen[i] = inRadius[i] - radius;
		aLen[i] = inAround[i] - around;
		tLen[i] = inTilt[i] - tilt;
	    }
	}
	    
	// pick the shortest distance around the circle
	// remember:  -180 and 180 are the same...
	for (i = 0; i < PF_MAX2(1,numChildren); i++) {
	    if (aLen[i] > 180.0f)
		aLen[i] -= 360.0f;
	    else if (aLen[i] < -180.0f)
		aLen[i] += 360.0f;
	    if (tLen[i] > 180.0f)
		tLen[i] -= 360.0f;
	    else if (tLen[i] < -180.0f)
		tLen[i] += 360.0f;
	}
	    
	double len = aLen[0] * aLen[0] + tLen[0] * tLen[0];
	childNum = 0;
	for (i = 1; i < numChildren; i++) {
	    double newlen = aLen[i] * aLen[i] + tLen[i] * tLen[i];
	    if (newlen < len) {
		newlen = len;
		childNum = i;
	    }
	}

	double scaledRLen =
    PF_ABS(rLen[childNum]/maxLinearVelocity);
	double scaledALen =
    PF_ABS(aLen[childNum]/maxAngularVelocity);
	double scaledTLen =
    PF_ABS(tLen[childNum]/maxAngularVelocity);

	double rStride = PF_ABS(y) * maxLinearVelocity;
	double aStride = PF_ABS(y) * maxAngularVelocity;
	double tStride = PF_ABS(y) * maxAngularVelocity;

	double longLen = scaledRLen;
	if (longLen < scaledALen)
	    longLen = scaledALen;
	if (longLen < scaledTLen)
	    longLen = scaledTLen;

	if (longLen > 0.0f) {
	    mult = scaledRLen/longLen;
	    if (mult < 1.0f)
		rStride *= mult;

	    mult = scaledALen/longLen;
	    if (mult < 1.0f)
		aStride *= mult;

	    mult = scaledTLen/longLen;
	    if (mult < 1.0f)
		tStride *= mult;
	}

	/**** radius ****/
	if (PF_ABS(rLen[childNum]) <= rStride) {
	    atRadius = TRUE;
	    if (y > 0.0f)
		radius = outRadius;
	    else
		radius = inRadius[childNum];
	}
	else {
	    atRadius = FALSE;
	    if (rLen[childNum] > 0.0f)
		radius += rStride;
	    else
		radius -= rStride;
	}

	/**** around ****/
	if (PF_ABS(aLen[childNum]) <= aStride) {
	    atAround = TRUE;
	    if (y > 0.0f)
		around = outAround;
	    else
		around = inAround[childNum];
	}
	else {
	    atAround = FALSE;
	    if (aLen[childNum] > 0.0f)
		around += aStride;
	    else
		around -= aStride;
	}

	/**** tilt ****/
	if (PF_ABS(tLen[childNum]) <= tStride) {
	    atTilt = TRUE;
	    if (y > 0.0f)
		tilt = outTilt;
	    else
		tilt = inTilt[childNum];
	}
	else {
	    atTilt = FALSE;
	    if (tLen[childNum] > 0.0f)
		tilt += tStride;
	    else
		tilt -= tStride;
	}

	xMotionCoordD.pos.hpr[PF_R] = roll;
	generateLocation();

	/****  This works - which means that it currently does nothing with
	  rotation...
	  pfQuatd quat;
	  pfMatrix4d rot;

	  xMotionCoordD.mat.getOrthoQuat(quat);
	  rot.makeQuat(quat);
	  \
	  xMotionCoordD.mat.postTrans(rot,
	  xMotionCoordD.pos.xyz[PF_X],
	  xMotionCoordD.pos.xyz[PF_Y],
	  xMotionCoordD.pos.xyz[PF_Z]);
	  \
	  xMotionCoordD.mat.getOrthoCoordd(&xMotionCoordD.pos);
	  ****/
	    
	break;
    }

    return PFI_CB_CONT;
} // pfiInputXformDSpheric::doUpdate