File: [Development] / inventor / lib / interaction / src / draggers / SoDragger.c++ (download)
Revision 1.2, Tue Sep 25 00:45:44 2001 UTC (16 years, 1 month ago) by jlim
Branch: MAIN
CVS Tags: release-2_1_5-9, release-2_1_5-8, release-2_1_5-10, HEAD Changes since 1.1: +4 -4
lines
Various changes:
* Fixed Bug 63 and 64.
* Handled nonstandard sed location.
* Used for-loop in man page install.
* Included casts for 64-bit builds.
* Added placeholder for FreeBSD options.
* Included unistd.h for getopt() and stdlib.h for malloc().
* Implemented SoText[23] workaround for glibc-2.2.* iconv().
* Split long lines in SoHandleBoxDraggerGeom.h and
SoTransformerDraggerGeom.h in lib/interaction/src/draggers/geom.
* Added IV_NO_OVERLAYS/OIV_NO_OVERLAYS variables to disable overlay planes.
|
/*
*
* 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/
*
*/
/*
* Copyright (C) 1990,91 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.2 $
|
| Classes:
| SoDragger
|
| Author(s): Paul Isaacs, David Mott, Howard Look
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <stdio.h>
#include <Inventor/SbLinear.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoPickedPoint.h>
#include <Inventor/SoPath.h>
#include <Inventor/fields/SoSFRotation.h>
#include <Inventor/fields/SoSFVec3f.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/events/SoEvent.h>
#include <Inventor/events/SoKeyboardEvent.h>
#include <Inventor/events/SoLocation2Event.h>
#include <Inventor/events/SoMouseButtonEvent.h>
#include <Inventor/actions/SoHandleEventAction.h>
#include <Inventor/actions/SoGetBoundingBoxAction.h>
#include <Inventor/actions/SoGetMatrixAction.h>
#include <Inventor/actions/SoSearchAction.h>
#include <Inventor/nodes/SoCamera.h>
#include <Inventor/nodes/SoMatrixTransform.h>
#include <Inventor/misc/SoState.h>
#include <Inventor/misc/SoTempPath.h>
#include <Inventor/misc/SoAuditorList.h>
#include <Inventor/misc/SoChildList.h>
#include <Inventor/elements/SoViewVolumeElement.h>
#include <Inventor/elements/SoViewportRegionElement.h>
#include <Inventor/draggers/SoDragger.h>
// The smallest scale that any dragger will write. If the user attempts
// to go below this amount, the dragger will set it to this minimum.
// Default is .001
float SoDragger::minScale = .001;
SO_KIT_SOURCE(SoDragger);
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor
//
SoDragger::SoDragger()
//
////////////////////////////////////////////////////////////////////////
{
SO_KIT_CONSTRUCTOR(SoDragger);
isBuiltIn = TRUE;
SO_KIT_ADD_CATALOG_ENTRY(motionMatrix, SoMatrixTransform, FALSE,
topSeparator, geomSeparator,FALSE);
// initialize field.
SO_KIT_ADD_FIELD(isActive,(0));
SO_KIT_INIT_INSTANCE();
// init local variables
startingWorldPoint = SbVec3f(0,0,0);
// callback lists
startCallbacks = new SoCallbackList;
motionCallbacks = new SoCallbackList;
finishCallbacks = new SoCallbackList;
valueChangedCallbacks = new SoCallbackList;
valueChangedCallbacksEnabled = TRUE;
activeChildDragger = NULL;
otherEventCallbacks = new SoCallbackList;
// initialize tempPathToThis, pickPath
tempPathToThis = NULL;
tempPathNumKidsHack = NULL;
pickPath = NULL;
// initialize surrogatePick info.
surrogateNameInPickOwner = "";
pathToSurrogatePickOwner = NULL;
surrogatePathInPickOwner = NULL;
// The matrix cache starts as invalid
cachedPathToThisValid = FALSE;
cachedMotionMatrixValid = FALSE;
// By default, do include this dragger in bounding box calculations.
// This is temporarily overridden when the dragger itself applies
// a bbox action so that it doesn't include itself.
ignoreInBbox = FALSE;
// Minimum amount to move before choosing a constraint based
// on the user's gesture.
minGesture = 8; // pixels
setHandleEventAction( NULL );
setCameraInfo( NULL );
// By default, projectors for rotation will call setFront() based
// on where the mouse went down: on the front or rear of the virtual sphere
// or cylinder.
setFrontOnProjector( USE_PICK );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor
//
SoDragger::~SoDragger()
//
////////////////////////////////////////////////////////////////////////
{
if ( pickPath != NULL )
pickPath->unref();
// tempPaths actually get deleted, not unref'ed
if ( tempPathToThis != NULL ) {
delete tempPathToThis;
tempPathToThis = NULL;
}
if ( tempPathNumKidsHack != NULL ) {
delete tempPathNumKidsHack;
tempPathNumKidsHack = NULL;
}
if ( activeChildDragger )
activeChildDragger->unref();
setNoPickedSurrogate();
delete startCallbacks;
delete motionCallbacks;
delete finishCallbacks;
delete valueChangedCallbacks;
delete otherEventCallbacks;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This attempts to handle the passed event. It will call methods on
// the SoHandleEventAction if it wants to grab and if it handles the event.
//
// Use: public
//
void
SoDragger::handleEvent(SoHandleEventAction *ha)
//
////////////////////////////////////////////////////////////////////////
{
// let base class traverse the children
if ( ha->getGrabber() != this )
SoBaseKit::handleEvent( ha );
// if no children handled the event, we will!
if ( ha->isHandled() )
return;
setHandleEventAction( ha );
// get event and window size from the action
const SoEvent *event = ha->getEvent();
if (SO_MOUSE_PRESS_EVENT(event, BUTTON1)) {
SbBool happyPath = FALSE;
const SoPickedPoint *pp = ha->getPickedPoint();
SoPath *pPath = ( pp != NULL ) ? pp->getPath() : NULL;
if ( pPath ) {
// Is our current traversal path part of the pickPath?
if ( pPath->containsPath(ha->getCurPath())) {
happyPath = TRUE;
}
// Is the path a surrogate path for us or any draggers
// contained within us?
else {
SoPath *pathToOwner, *surrogatePath;
SbName surrogateName;
if (isPathSurrogateInMySubgraph( pPath, pathToOwner,
surrogateName, surrogatePath) ) {
pathToOwner->ref();
surrogatePath->ref();
if (shouldGrabBasedOnSurrogate( pPath, surrogatePath )) {
setPickedSurrogate( pathToOwner, surrogateName,
surrogatePath );
happyPath = TRUE;
}
surrogatePath->unref();
pathToOwner->unref();
}
}
}
if ( happyPath ) {
setStartingPoint( pp );
// Since the pick path may be on a surrogate object, use
// the current action path to get a path to this node.
setTempPathToThis( ha->getCurPath() );
setCameraInfo( ha );
setPickPath( pPath );
ha->setGrabber(this);
ha->setHandled();
}
else
otherEventCallbacks->invokeCallbacks(this);
}
else if ( event->isOfType(SoLocation2Event::getClassTypeId() ) &&
ha->getGrabber() == this ) {
mouseMovedYet = TRUE;
motionCallbacks->invokeCallbacks(this);
ha->setHandled();
}
else if ( SO_MOUSE_RELEASE_EVENT(event, BUTTON1) &&
ha->getGrabber() == this ) {
// Releasing the grabber will result in a call to
// grabEventsCleanup(), which will do some important things...
ha->releaseGrabber();
if ( mouseMovedYet == TRUE ) {
// If the mouse didn't move, then don't handle the event.
// Let some other node make use of it (for example, the
// selection node might want to de-select what's inside
// the dragger.
ha->setHandled();
}
}
else
otherEventCallbacks->invokeCallbacks(this);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This virtual function is called when a dragger gains
// status as "grabber" of events.
//
// Use: public
//
void
SoDragger::grabEventsSetup()
//
////////////////////////////////////////////////////////////////////////
{
renderCaching = OFF;
setStartLocaterPosition( getLocaterPosition() );
saveStartParameters();
mouseMovedYet = FALSE;
isActive.setValue( TRUE );
startCallbacks->invokeCallbacks(this);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This virtual function is called when a dragger loses
// status as "grabber" of events.
//
// Use: public
//
void
SoDragger::grabEventsCleanup()
//
////////////////////////////////////////////////////////////////////////
{
isActive.setValue( FALSE );
finishCallbacks->invokeCallbacks(this);
setPickPath( NULL );
setNoPickedSurrogate();
renderCaching = AUTO;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns the event carried by the current handleEventAction,
// if any.
//
// Use: protected
//
const SoPath *
SoDragger::getPickPath() const
//
////////////////////////////////////////////////////////////////////////
{
const SoDragger *subDragger = getActiveChildDragger();
if (subDragger != NULL)
return subDragger->getPickPath();
else
return pickPath;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns the event carried by the current handleEventAction,
// if any.
//
// Use: protected
//
const SoEvent *
SoDragger::getEvent() const
//
////////////////////////////////////////////////////////////////////////
{
if ( getHandleEventAction() == NULL ) {
#ifdef DEBUG
SoDebugError::post("SoDragger::getEvent", "HandleEvent action is NULL");
#endif
return NULL;
}
return ( getHandleEventAction()->getEvent() );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns the point in space that begins the dragging gesture.
// Usually this is just the picked point, but in special
// cases, the point is set without picking.
// For example, when a modifier key goes down, a dragger will often
// start a new drag gesture without doing a pick. Instead, the previously
// used point will be chosen to start a new gesture.
//
// Use: protected
//
SbVec3f
SoDragger::getWorldStartingPoint()
//
////////////////////////////////////////////////////////////////////////
{
return startingWorldPoint;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Transforms the starting point into local space and returns it.
//
// Use: protected
//
SbVec3f
SoDragger::getLocalStartingPoint()
//
////////////////////////////////////////////////////////////////////////
{
SbVec3f thePoint = getWorldStartingPoint();
getWorldToLocalMatrix().multVecMatrix(thePoint, thePoint);
return thePoint;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This is called to get the position of the locater in
// normalized (0.0 to 1.0) screen space. (0,0) is lower left
// the event.
//
// Use: protected
//
SbVec2f
SoDragger::getNormalizedLocaterPosition()
//
////////////////////////////////////////////////////////////////////////
{
return( getEvent()->getNormalizedPosition( getViewportRegion() ));
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This is called to get the position of the locater in
// non-normalized screen space. (0,0) is lower left
// the event.
//
// Use: protected
//
SbVec2s
SoDragger::getLocaterPosition()
//
////////////////////////////////////////////////////////////////////////
{
return( getEvent()->getPosition( getViewportRegion()) );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns whether the locater has moved enough to assume a
// constraint gesture.
//
// Use: private
//
SbBool
SoDragger::isAdequateConstraintMotion()
//
////////////////////////////////////////////////////////////////////////
{
SbVec2s moved = (getStartLocaterPosition() - getLocaterPosition());
short lengthSquared = moved[0]*moved[0] + moved[1]*moved[1];
return (SbBool) (lengthSquared >= minGesture*minGesture);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This sets the viewport and view volume information
// explicitly to given values.
//
// Use: protected
//
void
SoDragger::setCameraInfo(SoAction *action)
//
////////////////////////////////////////////////////////////////////////
{
// If NULL, use default values
if (action == NULL ) {
viewVolume.ortho(-1, 1, -1, 1, 1, 10);
vpRegion = SbViewportRegion(1,1);
}
// If we've got an action...
else {
// Get the viewport and viewVolume...
SoState *state = action->getState();
viewVolume = SoViewVolumeElement::get( state );
vpRegion = SoViewportRegionElement::get(state);
}
// Here's a chance to set up a good tempPathToThis if for some reason
// we don't already have one.
SoPath *pathToMe = createPathToThis();
if (pathToMe) {
// We've can create a path. Just get rid of the one it gave us.
pathToMe->ref();
pathToMe->unref();
}
else if (action != NULL )
setTempPathToThis( action->getCurPath() );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This sets the handleEventAction
//
// Use: protected
//
void
SoDragger::setHandleEventAction( SoHandleEventAction *newAction )
//
////////////////////////////////////////////////////////////////////////
{
handleEventAction = newAction;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
//
// Use: private
//
void
SoDragger::setTempPathToThis( const SoPath *somethingClose )
//
////////////////////////////////////////////////////////////////////////
{
// Check that we're not doing something unnecessary...
// If paths don't fork until 'this', we can keep the path we've got.
// But even if we've already got an okay path, we should invalidate
// here, since some matrix has probably changed.
if ( tempPathToThis != NULL && somethingClose != NULL ) {
int forkInd = tempPathToThis->findFork( somethingClose );
if ( forkInd == (tempPathToThis->getLength() - 1)) {
cachedPathToThisValid = FALSE;
return;
}
}
// We can not keep our old tempPathToThis. Delete it now.
// ( you delete tempPaths, not ref/unref)
if (tempPathToThis != NULL) {
delete tempPathToThis;
tempPathToThis = NULL;
}
if (tempPathNumKidsHack != NULL) {
delete tempPathNumKidsHack;
tempPathNumKidsHack = NULL;
}
// Figure out tempPathToThis from somethingClose, which may
// not be exactly what we need...
if ( somethingClose != NULL && somethingClose->containsNode(this) ) {
// Create tempPathToThis. Copy from something close.
tempPathToThis
= createTempPathFromFullPath( (const SoFullPath *)somethingClose );
// Then pop off 'til the you get to this.
for (SoNode *n = tempPathToThis->getTail(); n != this; ) {
tempPathToThis->pop();
n = tempPathToThis->getTail();
}
}
else if ( somethingClose != NULL && somethingClose->getLength() > 0 ) {
// Start at the root and search for 'this' underneath.
// Turn on nodekit searching for the duration...
static SoSearchAction *sa = NULL;
if (sa == NULL)
sa = new SoSearchAction;
else
sa->reset();
SbBool oldBool = SoBaseKit::isSearchingChildren();
SoBaseKit::setSearchingChildren( TRUE );
sa->setNode( this );
sa->apply( somethingClose->getHead() );
SoBaseKit::setSearchingChildren( oldBool );
if ( sa->getPath() != NULL )
tempPathToThis
= createTempPathFromFullPath((const SoFullPath *)sa->getPath());
}
// Create the tempPathNumKidsHack. This is an sbPlist that records
// how many children are under each node along the path.
// We use it later to reconstruct the path if the parent/child pairs
// are still okay, but the indices in the path have changed.
if ( tempPathToThis != NULL ) {
SoNode *pathNode;
tempPathNumKidsHack = new SbPList( tempPathToThis->getLength() );
for ( int i = 0; i < tempPathToThis->getLength(); i++ ) {
pathNode = tempPathToThis->getNode(i);
if ( !pathNode || !pathNode->getChildren() )
tempPathNumKidsHack->append( (void *) 0 );
else
tempPathNumKidsHack->append(
(void *) (unsigned long) pathNode->getChildren()->getLength() );
}
}
// In both cases, the cachedPath has changed...
cachedPathToThisValid = FALSE;
}
SoPath *
SoDragger::createPathToThis()
{
if ( isTempPathToThisOk() )
return tempPathToThis->copy();
else
return NULL;
}
SbBool
SoDragger::isTempPathToThisOk()
{
// ??? Hack alert! ???
// This whole method is a hack!
//
if ( tempPathToThis == NULL || tempPathNumKidsHack == NULL)
return FALSE;
SbBool isOkay = TRUE;
SoNode *actualNode = NULL;
SoNode *nodeInPath = NULL;
for(int numFmTail = 0; numFmTail < tempPathToThis->getLength();numFmTail++){
int numFmHead = tempPathToThis->getLength() - numFmTail -1;
nodeInPath = tempPathToThis->getNode( numFmHead );
if (numFmTail == 0) {
if (this != nodeInPath) {
isOkay = FALSE;
break;
}
}
else {
// actual node is still node from last time.
// See if you can find 'nodeInPath' as a parent in the
// auditor list of actualNode.
const SoAuditorList aList = actualNode->getAuditors();
if ( aList.find( nodeInPath, SoNotRec::PARENT ) == -1 ) {
isOkay = FALSE;
break;
}
else {
// Since this is a temp path, it does not get notified
// when children are added or deleted. So we should
// check and adjust the indices in the path if they have
// gotten screwed up...
// Make sure that the
// index is correct for this parent/child pair.
SoNode *parent = nodeInPath;
SoNode *child = actualNode;
SoChildList *children = parent->getChildren();
if (children == NULL) {
isOkay = FALSE;
break;
}
int indexInPath = tempPathToThis->getIndex(numFmHead+1);
int numKidsNow = children->getLength();
#if (_MIPS_SZPTR == 64 || __ia64)
int numKidsBefore = (int) ((long) (*tempPathNumKidsHack)[numFmHead]);
#else
int numKidsBefore = (int) (*tempPathNumKidsHack)[numFmHead];
#endif
// To be correct, the childNode has to be the correct numbered
// child under the parent, and the parent should still
// have the same number of children it did before.
if ( (numKidsNow != numKidsBefore)
|| (numKidsNow <= indexInPath)
|| ((*children)[indexInPath] != child) ) {
// We have a problem. Try to fix it.
// First, figure out where it is most likely to belong.
// Also, update the tempPathNumKidsHack
int bestSpot = indexInPath;
if ( numKidsNow != numKidsBefore ) {
// update the number of kids.
tempPathNumKidsHack->remove(numFmHead);
tempPathNumKidsHack->insert( (void *) (unsigned long) numKidsNow,
numFmHead);
bestSpot = indexInPath + (numKidsNow - numKidsBefore);
}
// Look forward and back from bestSpot and try to find the
// node as a child. See which way we come to the node first.
// (we have to do this because of instancing.)
int early;
int late;
SbBool newSpot = -1;
for ( early = bestSpot, late = bestSpot;
early >= 0 || late < numKidsNow;
early--, late++ ) {
if (early >= 0 && early < numKidsNow &&
(child == (*children)[early])) {
newSpot = early;
break;
}
if (late >= 0 && late < numKidsNow &&
(child == (*children)[late])) {
newSpot = late;
break;
}
}
if ( newSpot == -1 ) {
isOkay = FALSE;
break;
}
if ( newSpot != indexInPath ) {
// We need to alter the path so we increment or
// decrement the index.
// Increments or decrements index of the child, since
// some children may have been added or deleted.
if (newSpot < indexInPath) {
for (int j = newSpot; j < indexInPath; j++ )
tempPathToThis->removeIndex(nodeInPath, 0 );
}
else if (newSpot > indexInPath) {
for (int j = newSpot; j > indexInPath; j-- )
tempPathToThis->insertIndex(nodeInPath, 0 );
}
}
}
}
}
actualNode = nodeInPath;
}
if (isOkay == FALSE) {
if (tempPathToThis != NULL) {
delete tempPathToThis;
tempPathToThis = NULL;
}
if (tempPathNumKidsHack != NULL) {
delete tempPathNumKidsHack;
tempPathNumKidsHack = NULL;
}
}
return isOkay;
}
SoTempPath *
SoDragger::createTempPathFromFullPath( const SoFullPath *fp ) const
{
SoTempPath *answer = new SoTempPath( fp->getLength() );
for ( int i = 0; i < fp->getLength(); i++ )
answer->append( fp->getNode(i) );
return answer;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the pickPath to be the given path.
//
void
SoDragger::setPickPath( SoPath *newPickPath )
//
////////////////////////////////////////////////////////////////////////
{
// Set the pickPath. This should just equal the input path.
// ref the input before unref'ing the old path, in case they're the same
if ( newPickPath != NULL )
newPickPath->ref();
// get rid of old path if it exists
if ( pickPath != NULL ) {
pickPath->unref();
pickPath = NULL;
}
// add new path if it exists
if ( newPickPath != NULL )
pickPath = (SoPath *) newPickPath;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the surrogatePick information to initial values.
//
void
SoDragger::setNoPickedSurrogate()
//
////////////////////////////////////////////////////////////////////////
{
// Set the name to be empty
surrogateNameInPickOwner = "";
// get rid of old paths if they exist.
if ( pathToSurrogatePickOwner != NULL ) {
pathToSurrogatePickOwner->unref();
pathToSurrogatePickOwner = NULL;
}
if ( surrogatePathInPickOwner != NULL ) {
surrogatePathInPickOwner->unref();
surrogatePathInPickOwner = NULL;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
//
SbBool
SoDragger::shouldGrabBasedOnSurrogate( const SoPath *pickPath,
const SoPath *surrogatePath )
//
////////////////////////////////////////////////////////////////////////
{
// The pickPath must contain the surrogatePath
if ( pickPath->containsPath( surrogatePath ) == FALSE )
return FALSE;
const SoFullPath *fullPick = (const SoFullPath *) pickPath;
const SoFullPath *fullSurr = (const SoFullPath *) surrogatePath;
// Find the tail of surrogatePath.
SoNode *surrTail = fullSurr->getTail();
// Go from the tail of pickPath backwards.
// If you find a dragger before you find surrTail, return FALSE.
// Otherwise, return TRUE.
SoNode *pickNode;
for (int i = fullPick->getLength() - 1; i >= 0; i-- ) {
pickNode = fullPick->getNode(i);
if (pickNode == surrTail)
return TRUE;
if (pickNode->isOfType( SoDragger::getClassTypeId() ))
return FALSE;
}
// Should never get here...
return FALSE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the surrogatePick information.
//
void
SoDragger::setPickedSurrogate( SoPath *pathToOwner, SbName &nameUsedByOwner,
SoPath *pathUsedByOwner )
//
////////////////////////////////////////////////////////////////////////
{
// Set the pathToSurrogatePickOwner.
if ( pathToOwner != pathToSurrogatePickOwner ) {
if ( pathToOwner != NULL )
pathToOwner->ref();
if ( pathToSurrogatePickOwner != NULL )
pathToSurrogatePickOwner->unref();
pathToSurrogatePickOwner = pathToOwner;
}
// Set the surrogatePathInPickOwner.
if ( pathUsedByOwner != surrogatePathInPickOwner ) {
if ( pathUsedByOwner != NULL )
pathUsedByOwner->ref();
if ( surrogatePathInPickOwner != NULL )
surrogatePathInPickOwner->unref();
surrogatePathInPickOwner = pathUsedByOwner;
}
// Set the name...
surrogateNameInPickOwner = nameUsedByOwner;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the point in space where dragger is to begin.
// The pickedPoint version would usually be the result of a pick.
// Assumes that 'newPoint' is in world space.
//
void
SoDragger::setStartingPoint( const SoPickedPoint *newPoint )
//
////////////////////////////////////////////////////////////////////////
{
startingWorldPoint = newPoint->getPoint();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the point in space where dragger is to begin.
// The SbVec3f version would usually be a piont saved from the end of
// another gesture. For example, when a modifier key goes down, we might
// save the current position and use it to begin another connected gesture.
// Assumes that 'newPoint' is in world space.
//
void
SoDragger::setStartingPoint( const SbVec3f &newPoint )
//
////////////////////////////////////////////////////////////////////////
{
startingWorldPoint = newPoint;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This implements the SoGetBoundingBoxAction.
//
// Use: protected
//
void
SoDragger::getBoundingBox(SoGetBoundingBoxAction *action)
//
////////////////////////////////////////////////////////////////////////
{
if ( ! ignoreInBbox )
SoBaseKit::getBoundingBox( action );
}
const SbMatrix &
SoDragger::getMotionMatrix()
//
////////////////////////////////////////////////////////////////////////
{
// Try the fast way to access this node first...
SoMatrixTransform *mm = (SoMatrixTransform *) motionMatrix.getValue();
// If that fails, then make a new part...
if (mm == NULL)
mm = (SoMatrixTransform *) getAnyPart("motionMatrix", TRUE);
return ( mm->matrix.getValue() );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the motion matrix to be an explicit matrix. Triggers value
// changed callbacks, but only if (newMatrix != the current motionMatrix)
//
void
SoDragger::setMotionMatrix( const SbMatrix &newMatrix )
//
////////////////////////////////////////////////////////////////////////
{
// Return if no change...
if ( getMotionMatrix() == newMatrix )
return;
// Set motion matrix (the field will be non-null,
// since 'getMotionMatrix()' was just called
((SoMatrixTransform *)motionMatrix.getValue())->matrix = newMatrix;
// We'll need to recalculate the conversion matrices.
cachedMotionMatrixValid = FALSE;
// Invokes the value changed callbacks
valueChanged();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Invokes the valueChangedCallbacks, if they are enabled.
// These are invoked whenever setMotionMatrix() changes the motion matrix.
// If a subclass wishes to invoke the valueChanged callbacks for some
// other reason, they may call valueChanged(). Example: SoSpotLightDragger
// changes its angle field without altering the motionMatrix. So it
// calls valueChanged() to invoke callbacks.
//
void
SoDragger::valueChanged()
//
////////////////////////////////////////////////////////////////////////
{
if (valueChangedCallbacksEnabled == TRUE)
valueChangedCallbacks->invokeCallbacks(this);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Saves the motionMatrix as the startMotionMatrix.
// The startMotionMatrix is used to remember where the dragger was
// at the beginning of dragging.
//
void
SoDragger::saveStartParameters()
//
////////////////////////////////////////////////////////////////////////
{
startMotionMatrix = getMotionMatrix();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This sets the 'activeChildDragger.'
// Called by childStartCB(), childFinishCB(),
// childValueChangedCB(), etc.
// Establishes which dragger is the child, information
// needed by the parent in order to do what it needs to do.
//
void
SoDragger::setActiveChildDragger( SoDragger *newChildDragger )
//
////////////////////////////////////////////////////////////////////////
{
if (newChildDragger)
newChildDragger->ref();
if (activeChildDragger)
activeChildDragger->unref();
activeChildDragger = newChildDragger;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This function is invoked by child draggers
// when they change their value.
//
void
SoDragger::transferMotion( SoDragger *childDragger)
//
////////////////////////////////////////////////////////////////////////
{
// Get the motion matrix from the child
SbMatrix childMotion = childDragger->getMotionMatrix();
// There's a lot we don't need to bother with if the childMotion is
// identity...
SbBool childIdent = ( childMotion == SbMatrix::identity() );
// Return if childMotion is identity and our motionMatrix already
// matches our saved startMatrix.
if ( childIdent && (getMotionMatrix() == getStartMotionMatrix()))
return;
if ( !childIdent ) {
// First, set the childDragger matrix to identity.
childDragger->setMotionMatrix( SbMatrix::identity() );
// Convert the childMotion from child LOCAL space to world space.
childDragger->transformMatrixLocalToWorld(childMotion,childMotion);
// Convert the childMotion from world space to our LOCAL space.
transformMatrixWorldToLocal(childMotion,childMotion);
}
// Append this transformed child motion to our saved start matrix.
SbMatrix newMotion = getStartMotionMatrix();
if ( !childIdent )
newMotion.multLeft( childMotion );
setMotionMatrix( newMotion );
// Changing the parent matrix invalidates the matrix cache of the
// childDragger
childDragger->cachedPathToThisValid = FALSE;
}
////////////////////////////////////////////////////////////////////////
//
// Use: private
//
void
SoDragger::validateMatrices()
//
////////////////////////////////////////////////////////////////////////
{
// If both aspects of the matrices are still okay, then we can
// continue to use the cached values.
if ( cachedPathToThisValid && cachedMotionMatrixValid )
return;
// If the tempPathToThis is no longer valid, then we've got to run a
// getMatrix action to find the preMotionToWorld and
// worldToPreMotion matrices.
if ( cachedPathToThisValid == FALSE ) {
// Do a get matrix action from world space to this node.
SoPath *pathToMe = createPathToThis();
if ( pathToMe != NULL ) {
pathToMe->ref();
static SoGetMatrixAction *ma = NULL;
if (ma == NULL)
ma = new SoGetMatrixAction( getViewportRegion() );
else
ma->setViewportRegion( getViewportRegion() );
ma->apply( pathToMe );
preMotionToWorldMatrix = ma->getMatrix();
worldToPreMotionMatrix = ma->getInverse();
pathToMe->unref();
}
else {
preMotionToWorldMatrix.makeIdentity();
worldToPreMotionMatrix.makeIdentity();
}
}
// If the cachedMotionMatrix is no longer valid, then figure 'em out...
if ( cachedMotionMatrixValid == FALSE ) {
cachedMotionMatrix = getMotionMatrix();
}
// Last, we append the motion matrix to the preMotion matrix to get
// the postMotion matrix.
postMotionToWorldMatrix = preMotionToWorldMatrix;
postMotionToWorldMatrix.multLeft( cachedMotionMatrix );
worldToPostMotionMatrix = worldToPreMotionMatrix;
worldToPostMotionMatrix.multRight( cachedMotionMatrix.inverse() );
cachedMotionMatrixValid = TRUE;
cachedPathToThisValid = TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Use: protected
//
SbMatrix
SoDragger::getLocalToWorldMatrix()
//
////////////////////////////////////////////////////////////////////////
{
// Make sure everything is kosher
validateMatrices();
return postMotionToWorldMatrix;
}
////////////////////////////////////////////////////////////////////////
//
// Use: protected
//
SbMatrix
SoDragger::getWorldToLocalMatrix()
//
////////////////////////////////////////////////////////////////////////
{
// Make sure everything is kosher
validateMatrices();
return worldToPostMotionMatrix;
}
////////////////////////////////////////////////////////////////////////
//
// Use: protected
//
void
SoDragger::transformMatrixLocalToWorld( const SbMatrix &fromMatrix,
SbMatrix &toMatrix)
//
////////////////////////////////////////////////////////////////////////
{
toMatrix = fromMatrix;
SbMatrix forward = getLocalToWorldMatrix();
SbMatrix backward = getWorldToLocalMatrix();
toMatrix.multRight( forward );
toMatrix.multLeft( backward );
}
////////////////////////////////////////////////////////////////////////
//
// Use: protected
//
void
SoDragger::transformMatrixWorldToLocal( const SbMatrix &fromMatrix,
SbMatrix &toMatrix)
//
////////////////////////////////////////////////////////////////////////
{
toMatrix = fromMatrix;
SbMatrix forward = getWorldToLocalMatrix();
SbMatrix backward = getLocalToWorldMatrix();
toMatrix.multRight( forward );
toMatrix.multLeft( backward );
}
////////////////////////////////////////////////////////////////////////
//
// Use: EXTENDER public
//
// Description:
// Get the matrix which converts from the space of one part into
// local space. Good to use if transforms occur between 'motionMatrix'
// and the space you want to work in.
// Note: This routine will try not to create parts that don't exist.
// Instead it finds the existing part that precedes it in traversal.
// But this only works if the partName is in this nodekit's catalog.
// If the part is nested within another kit below this one or
// sitting inside a list part, the part will be created when it
// doesn't exist.
//
void
SoDragger::getPartToLocalMatrix( const SbName &partName,
SbMatrix &partToLocalMatrix, SbMatrix &localToPartMatrix)
//
////////////////////////////////////////////////////////////////////////
{
// We need to temporarily ref ourself, since we build paths
// and stuff...
ref();
SoPath *pathToMe = createPathToThis();
if (pathToMe)
pathToMe->ref();
SoPath *pathToPart;
// We want to figure this out without creating any parts unnecessarily.
// So, instead of forcing creation, do a check.
pathToPart = createPathToAnyPart(partName,FALSE,FALSE,FALSE,pathToMe);
// If we didn't find a path that already exists:
if (pathToPart == NULL) {
const SoNodekitCatalog *cat = getNodekitCatalog();
int pNum = cat->getPartNumber(partName);
if ( pNum != SO_CATALOG_NAME_NOT_FOUND ) {
// If it fails and the part is in this catalog, then traverse
// backwards until we can find the last part before partName
// and after "motionMatrix" If we hit "motionMatrix", or
// "this", then just set path to NULL
int thisPnum = cat->getPartNumber("this");
int motMatPnum = cat->getPartNumber("motionMatrix");
while ((pathToPart == NULL) &&
(pNum != thisPnum) && (pNum != motMatPnum))
{
// Find left sibling or parent. Can check 'em together
// since left sibling never precedes parent.
for (int i = pNum-1; i >= 0; i-- ) {
if ((cat->getRightSiblingPartNumber(i) == pNum) ||
(cat->getParentPartNumber(pNum) == i)) {
pNum = i;
break;
}
}
if ( pNum != thisPnum && pNum != motMatPnum ) {
pathToPart = createPathToAnyPart(cat->getName(pNum),
FALSE, FALSE, FALSE, pathToMe);
}
}
}
else {
// If the part doesn't exist yet and the partName
// is not in this catalog, we're sort of stuck. Force
// it and create the part anyway. (2nd arg == TRUE)
pathToPart = createPathToAnyPart(partName,TRUE,
FALSE,FALSE,pathToMe);
}
}
// We don't need this path anymore.
if (pathToMe)
pathToMe->unref();
if ( pathToPart == NULL ) {
partToLocalMatrix = localToPartMatrix = SbMatrix::identity();
// Undo temporary ref on ourself.
unrefNoDelete();
return;
}
pathToPart->ref();
static SoGetMatrixAction *ma = NULL;
if (ma == NULL)
ma = new SoGetMatrixAction( getViewportRegion() );
else
ma->setViewportRegion( getViewportRegion() );
ma->apply( pathToPart );
SbMatrix partToWorld = ma->getMatrix();
SbMatrix worldToPart = ma->getInverse();
pathToPart->unref();
partToLocalMatrix = partToWorld;
partToLocalMatrix.multRight( getWorldToLocalMatrix() );
localToPartMatrix = getLocalToWorldMatrix();
localToPartMatrix.multRight( worldToPart);
// Undo temporary ref on ourself.
unrefNoDelete();
}
void
SoDragger::transformMatrixToLocalSpace( const SbMatrix &fromMatrix,
SbMatrix &toMatrix, const SbName &fromSpacePartName)
{
SbMatrix fromToLocalM, localToFromM;
getPartToLocalMatrix( fromSpacePartName, fromToLocalM, localToFromM);
toMatrix = fromMatrix;
toMatrix.multRight( fromToLocalM );
toMatrix.multLeft( localToFromM );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Simple functions for adding to and removing from
// the different callback lists.
//
////////////////////////////////////////////////////////////////////////
/////////
// START
/////////
void SoDragger::addStartCallback( SoDraggerCB *f, void *d )
{ startCallbacks->addCallback( (SoCallbackListCB *)f, d ); }
void SoDragger::removeStartCallback( SoDraggerCB *f, void *d )
{ startCallbacks->removeCallback( (SoCallbackListCB *)f, d ); }
/////////
// MOTION
/////////
void SoDragger::addMotionCallback( SoDraggerCB *f, void *d )
{ motionCallbacks->addCallback( (SoCallbackListCB *)f, d ); }
void SoDragger::removeMotionCallback( SoDraggerCB *f, void *d )
{ motionCallbacks->removeCallback( (SoCallbackListCB *)f, d ); }
/////////
// DONE
/////////
void SoDragger::addFinishCallback( SoDraggerCB *f, void *d )
{ finishCallbacks->addCallback( (SoCallbackListCB *)f, d ); }
void SoDragger::removeFinishCallback( SoDraggerCB *f, void *d )
{ finishCallbacks->removeCallback( (SoCallbackListCB *)f, d ); }
/////////
// VALUECHANGED
/////////
void SoDragger::addValueChangedCallback( SoDraggerCB *f, void *d )
{ valueChangedCallbacks->addCallback( (SoCallbackListCB *)f, d ); }
void SoDragger::removeValueChangedCallback( SoDraggerCB *f, void *d )
{ valueChangedCallbacks->removeCallback( (SoCallbackListCB *)f, d ); }
SbBool SoDragger::enableValueChangedCallbacks( SbBool newVal )
{
SbBool oldVal = valueChangedCallbacksEnabled;
valueChangedCallbacksEnabled = newVal;
return oldVal;
}
/////////
// OTHEREVENT
/////////
void SoDragger::addOtherEventCallback( SoDraggerCB *f, void *d )
{ otherEventCallbacks->addCallback( (SoCallbackListCB *)f, d ); }
void SoDragger::removeOtherEventCallback( SoDraggerCB *f, void *d )
{ otherEventCallbacks->removeCallback( (SoCallbackListCB *)f, d ); }
void SoDragger::registerChildDragger(SoDragger *child)
{
// This calls transferMotion, followed by the callbacks for this node.
child->addValueChangedCallback(
SoDragger::childTransferMotionAndValueChangedCB, this );
child->addStartCallback(SoDragger::childStartCB, this);
child->addMotionCallback(SoDragger::childMotionCB, this);
child->addFinishCallback(SoDragger::childFinishCB, this);
child->addOtherEventCallback(SoDragger::childOtherEventCB, this);
}
void SoDragger::unregisterChildDragger(SoDragger *child)
{
child->removeValueChangedCallback(
SoDragger::childTransferMotionAndValueChangedCB, this );
child->removeStartCallback(SoDragger::childStartCB, this);
child->removeMotionCallback(SoDragger::childMotionCB, this);
child->removeFinishCallback(SoDragger::childFinishCB, this);
child->removeOtherEventCallback(SoDragger::childOtherEventCB, this);
}
void SoDragger::registerChildDraggerMovingIndependently(SoDragger *child)
{
child->addValueChangedCallback( SoDragger::childValueChangedCB, this );
child->addStartCallback(SoDragger::childStartCB, this);
child->addMotionCallback(SoDragger::childMotionCB, this);
child->addFinishCallback(SoDragger::childFinishCB, this);
child->addOtherEventCallback(SoDragger::childOtherEventCB, this);
}
void SoDragger::unregisterChildDraggerMovingIndependently(SoDragger *child)
{
child->removeValueChangedCallback( SoDragger::childValueChangedCB, this );
child->removeStartCallback(SoDragger::childStartCB, this);
child->removeMotionCallback(SoDragger::childMotionCB, this);
child->removeFinishCallback(SoDragger::childFinishCB, this);
child->removeOtherEventCallback(SoDragger::childOtherEventCB, this);
}
void SoDragger::childTransferMotionAndValueChangedCB(void *parentAsVoid,
SoDragger *childDragger)
{
SoDragger *parent = (SoDragger *) parentAsVoid;
SoDragger *savedChild = parent->getActiveChildDragger();
if (savedChild) savedChild->ref();
parent->setActiveChildDragger( childDragger );
// Save these variables to we can put 'em back when we're done.
SoHandleEventAction *oldHa = parent->getHandleEventAction();
SbViewVolume oldVV = parent->getViewVolume();
SbViewportRegion oldVPR = parent->getViewportRegion();
parent->setHandleEventAction(childDragger->getHandleEventAction());
parent->setViewVolume(childDragger->getViewVolume());
parent->setViewportRegion(childDragger->getViewportRegion());
SoPath *pathToKid = childDragger->createPathToThis();
if (pathToKid) pathToKid->ref();
parent->setTempPathToThis( pathToKid );
if (pathToKid) pathToKid->unref();
// Before calling the other valueChanged callbacks, transfer the
// motion of the childDragger into our own motion matrix.
// We do not want to trigger any of our other valueChanged callbacks
// while this is being done...
SbBool saveEnabled = parent->enableValueChangedCallbacks( FALSE );
parent->transferMotion( childDragger );
parent->enableValueChangedCallbacks( saveEnabled );
parent->valueChanged();
parent->setActiveChildDragger( savedChild );
// Restore saved values of our variables
parent->setHandleEventAction(oldHa);
parent->setViewVolume(oldVV);
parent->setViewportRegion(oldVPR);
if (savedChild) savedChild->unref();
}
void SoDragger::childValueChangedCB(void *parentAsVoid, SoDragger *childDragger)
{
SoDragger *parent = (SoDragger *) parentAsVoid;
SoDragger *savedChild = parent->getActiveChildDragger();
if (savedChild) savedChild->ref();
parent->setActiveChildDragger( childDragger );
// Save these variables to we can put 'em back when we're done.
SoHandleEventAction *oldHa = parent->getHandleEventAction();
SbViewVolume oldVV = parent->getViewVolume();
SbViewportRegion oldVPR = parent->getViewportRegion();
parent->setHandleEventAction(childDragger->getHandleEventAction());
parent->setViewVolume(childDragger->getViewVolume());
parent->setViewportRegion(childDragger->getViewportRegion());
SoPath *pathToKid = childDragger->createPathToThis();
if (pathToKid) pathToKid->ref();
parent->setTempPathToThis( pathToKid );
if (pathToKid) pathToKid->unref();
parent->valueChanged();
parent->setActiveChildDragger( savedChild );
// Restore saved values of our variables
parent->setHandleEventAction(oldHa);
parent->setViewVolume(oldVV);
parent->setViewportRegion(oldVPR);
if (savedChild) savedChild->unref();
}
void SoDragger::childStartCB(void *parentAsVoid, SoDragger *childDragger )
{
SoDragger *parent = (SoDragger *) parentAsVoid;
SoDragger *savedChild = parent->getActiveChildDragger();
if (savedChild) savedChild->ref();
parent->setActiveChildDragger( childDragger );
parent->saveStartParameters();
// Save these variables to we can put 'em back when we're done.
SoHandleEventAction *oldHa = parent->getHandleEventAction();
SbViewVolume oldVV = parent->getViewVolume();
SbViewportRegion oldVPR = parent->getViewportRegion();
parent->setHandleEventAction(childDragger->getHandleEventAction());
parent->setViewVolume(childDragger->getViewVolume());
parent->setViewportRegion(childDragger->getViewportRegion());
SoPath *pathToKid = childDragger->createPathToThis();
if (pathToKid) pathToKid->ref();
parent->setTempPathToThis( pathToKid );
if (pathToKid) pathToKid->unref();
parent->setStartingPoint( childDragger->getWorldStartingPoint() );
// While the child is manipulating, we should not bother caching here.
parent->renderCaching = OFF;
parent->startCallbacks->invokeCallbacks(parent);
parent->setActiveChildDragger( savedChild );
// Restore saved values of our variables
parent->setHandleEventAction(oldHa);
parent->setViewVolume(oldVV);
parent->setViewportRegion(oldVPR);
if (savedChild) savedChild->unref();
}
void SoDragger::childMotionCB(void *parentAsVoid, SoDragger *childDragger )
{
SoDragger *parent = (SoDragger *) parentAsVoid;
SoDragger *savedChild = parent->getActiveChildDragger();
if (savedChild) savedChild->ref();
parent->setActiveChildDragger( childDragger );
// Save these variables to we can put 'em back when we're done.
SoHandleEventAction *oldHa = parent->getHandleEventAction();
SbViewVolume oldVV = parent->getViewVolume();
SbViewportRegion oldVPR = parent->getViewportRegion();
parent->setHandleEventAction(childDragger->getHandleEventAction());
parent->setViewVolume(childDragger->getViewVolume());
parent->setViewportRegion(childDragger->getViewportRegion());
SoPath *pathToKid = childDragger->createPathToThis();
if (pathToKid) pathToKid->ref();
parent->setTempPathToThis( pathToKid );
if (pathToKid) pathToKid->unref();
parent->motionCallbacks->invokeCallbacks(parent);
parent->setActiveChildDragger( savedChild );
// Restore saved values of our variables
parent->setHandleEventAction(oldHa);
parent->setViewVolume(oldVV);
parent->setViewportRegion(oldVPR);
if (savedChild) savedChild->unref();
}
void SoDragger::childFinishCB(void *parentAsVoid, SoDragger *childDragger )
{
SoDragger *parent = (SoDragger *) parentAsVoid;
SoDragger *savedChild = parent->getActiveChildDragger();
if (savedChild) savedChild->ref();
parent->setActiveChildDragger( childDragger );
// Save these variables to we can put 'em back when we're done.
SoHandleEventAction *oldHa = parent->getHandleEventAction();
SbViewVolume oldVV = parent->getViewVolume();
SbViewportRegion oldVPR = parent->getViewportRegion();
parent->setHandleEventAction(childDragger->getHandleEventAction());
parent->setViewVolume(childDragger->getViewVolume());
parent->setViewportRegion(childDragger->getViewportRegion());
SoPath *pathToKid = childDragger->createPathToThis();
if (pathToKid) pathToKid->ref();
parent->setTempPathToThis( pathToKid );
if (pathToKid) pathToKid->unref();
// When child is finished manipulating, we resume caching.
parent->renderCaching = AUTO;
parent->finishCallbacks->invokeCallbacks(parent);
parent->setActiveChildDragger( savedChild );
// Restore saved values of our variables
parent->setHandleEventAction(oldHa);
parent->setViewVolume(oldVV);
parent->setViewportRegion(oldVPR);
if (savedChild) savedChild->unref();
}
void SoDragger::childOtherEventCB(void *parentAsVoid, SoDragger *childDragger )
{
SoDragger *parent = (SoDragger *) parentAsVoid;
SoDragger *savedChild = parent->getActiveChildDragger();
if (savedChild) savedChild->ref();
parent->setActiveChildDragger( childDragger );
// Save these variables to we can put 'em back when we're done.
SoHandleEventAction *oldHa = parent->getHandleEventAction();
SbViewVolume oldVV = parent->getViewVolume();
SbViewportRegion oldVPR = parent->getViewportRegion();
parent->setHandleEventAction(childDragger->getHandleEventAction());
parent->setViewVolume(childDragger->getViewVolume());
parent->setViewportRegion(childDragger->getViewportRegion());
SoPath *pathToKid = childDragger->createPathToThis();
if (pathToKid) pathToKid->ref();
parent->setTempPathToThis( pathToKid );
if (pathToKid) pathToKid->unref();
parent->otherEventCallbacks->invokeCallbacks(parent);
parent->setActiveChildDragger( savedChild );
// Restore saved values of our variables
parent->setHandleEventAction(oldHa);
parent->setViewVolume(oldVV);
parent->setViewportRegion(oldVPR);
if (savedChild) savedChild->unref();
}
void
SoDragger::workFieldsIntoTransform( SbMatrix &mtx )
{
SbVec3f trans, *translationPtr = NULL;
SbRotation rot, *rotationPtr = NULL;
SbVec3f scale, *scaleFactorPtr = NULL;
SbRotation orient, *scaleOrientationPtr = NULL;
SbVec3f center, *centerPtr = NULL;
SoField *f;
// Assign values from any fields you might find...
if ( (f = getField( "translation" )) != NULL ) {
trans = ((SoSFVec3f *)f)->getValue();
translationPtr = &trans;
}
if ( (f = getField( "rotation" )) != NULL ) {
rot = ((SoSFRotation *)f)->getValue();
rotationPtr = &rot;
}
if ( (f = getField( "scaleFactor" )) != NULL ) {
scale = ((SoSFVec3f *)f)->getValue();
scaleFactorPtr = &scale;
}
if ( (f = getField( "scaleOrientation" )) != NULL ) {
orient = ((SoSFRotation *)f)->getValue();
scaleOrientationPtr = &orient;
}
if ( (f = getField( "center" )) != NULL ) {
center = ((SoSFVec3f *)f)->getValue();
centerPtr = ¢er;
}
workValuesIntoTransform( mtx, translationPtr,
rotationPtr, scaleFactorPtr, scaleOrientationPtr, centerPtr );
}
void
SoDragger::workValuesIntoTransform( SbMatrix &mtx,
const SbVec3f *translationPtr,
const SbRotation *rotationPtr,
const SbVec3f *scaleFactorPtr,
const SbRotation *scaleOrientationPtr,
const SbVec3f *centerPtr )
{
SbVec3f trans, scale;
SbRotation rot, scaleOrient;
SbVec3f center(0,0,0);
// To begin with, get the values currently in the matrix. If we were
// given a center, use it for the calculations.
if ( centerPtr != NULL )
center = *centerPtr;
SoDragger::getTransformFast( mtx, trans,rot,scale,scaleOrient,center);
// Now, replace any values which should be dictated by our input.
// Don't need to do center again, since it should remain unchanged.
if ( translationPtr != NULL )
trans = *translationPtr;
if ( rotationPtr != NULL )
rot = *rotationPtr;
if ( scaleFactorPtr != NULL )
scale = *scaleFactorPtr;
if ( scaleOrientationPtr != NULL )
scaleOrient = *scaleOrientationPtr;
// Finally, construct a new transform with these values.
mtx.setTransform( trans, rot, scale, scaleOrient, center );
}
void
SoDragger::getTransformFast( SbMatrix &mtx, SbVec3f &translation,
SbRotation &rotation, SbVec3f &scaleFactor,
SbRotation &scaleOrientation,
const SbVec3f ¢er )
{
if (center != SbVec3f(0,0,0)) {
// to get fields for a non-0 center, we
// need to decompose a new matrix "m" such
// that [-center][m][center] = [this]
// i.e., [m] = [center][this][-center]
// (this trick stolen from Showcase code)
SbMatrix m,c;
m.setTranslate(-center);
m.multLeft(mtx);
c.setTranslate(center);
m.multLeft(c);
SoDragger::getTransformFast( m, translation, rotation, scaleFactor,
scaleOrientation);
}
else
SoDragger::getTransformFast( mtx, translation, rotation, scaleFactor,
scaleOrientation);
}
void
SoDragger::getTransformFast( SbMatrix &mtx, SbVec3f &translation,
SbRotation &rotation, SbVec3f &scaleFactor,
SbRotation &scaleOrientation)
{
SbBool canDoFast = TRUE;
// If the last column is (0,0,0,1), then we don't have to worry
// about projection matrix. If not, we need to call SbMatrix::factor
if ( mtx[0][3] != 0 || mtx[1][3] != 0 || mtx[2][3] != 0 || mtx[3][3] != 1)
canDoFast = FALSE;
// You get maxXVec, matYVec, and matZVec if you send the xVec,yVec,zVec,
// through the matrix.
SbVec3f xVec(1,0,0), yVec(0,1,0), zVec(0,0,1);
SbVec3f matXVec( mtx[0][0], mtx[0][1], mtx[0][2] );
SbVec3f matYVec( mtx[1][0], mtx[1][1], mtx[1][2] );
SbVec3f matZVec( mtx[2][0], mtx[2][1], mtx[2][2] );
// If they are orthogonal, that means the
// scaleOrientation is identity() and we are free to factor the matrix.
// Only need to test two sets, since 3rd answer is implicit.
#define TINY 0.00001
if ( fabs( matXVec.dot( matYVec )) > TINY )
canDoFast = FALSE;
else if ( fabs( matYVec.dot( matZVec )) > TINY )
canDoFast = FALSE;
#undef TINY
if ( canDoFast == TRUE ) {
scaleOrientation.setValue(0,0,0,1);
// Translation is just the first three entries in bottom row.
translation.setValue( mtx[3][0], mtx[3][1], mtx[3][2] );
// scaleFactor is scale of the three transformed axes.
// Kill two birds with one stone and normalize to get the sizes...
scaleFactor.setValue( matXVec.normalize(),
matYVec.normalize(),
matZVec.normalize());
rotation = SbMatrix( matXVec[0], matXVec[1], matXVec[2], 0,
matYVec[0], matYVec[1], matYVec[2], 0,
matZVec[0], matZVec[1], matZVec[2], 0,
0, 0, 0, 1 );
}
else {
// If 'canDoFast' == FALSE, send the info to SbMatrix::factor
#ifdef DEBUG
#if 0
SoDebugError::post("SoDragger::getTransformFast",
"This is a tricky matrix. Giving it to SbMatrix::factor");
#endif
#endif
SbMatrix proj, rotMatrix, scaleOrientMatrix;
mtx.factor(scaleOrientMatrix, scaleFactor, rotMatrix, translation,proj);
rotation = rotMatrix;
// have to transpose because factor gives transpose of correct answer
scaleOrientation = scaleOrientMatrix.transpose();
}
}
SbMatrix
SoDragger::appendTranslation( const SbMatrix &mtx,
const SbVec3f &translation,
const SbMatrix *conversion )
{
SbBool isCnvIdent = (conversion == NULL
|| (*conversion) == SbMatrix::identity());
SbBool isMtxIdent = (mtx == SbMatrix::identity());
// Get motion into local Space.
// Local space for translation is at the beginning of the matrix.
// Convert using multDirMatrix, not multVecMatrix, since this is motion.
SbVec3f lclMotion = translation;
if ( !isCnvIdent )
conversion->multDirMatrix( lclMotion, lclMotion );
if ( !isMtxIdent )
mtx.multDirMatrix( lclMotion, lclMotion );
SbVec3f startTranslate = mtx[3];
SbVec3f newTranslate = startTranslate + lclMotion;
SbMatrix answer = mtx;
answer[3][0] = newTranslate[0];
answer[3][1] = newTranslate[1];
answer[3][2] = newTranslate[2];
return answer;
}
SbMatrix
SoDragger::appendScale( const SbMatrix &mtx,
const SbVec3f &scale, const SbVec3f &scaleCenter,
const SbMatrix *conversion )
{
SbBool isCnvIdent = (conversion == NULL
|| (*conversion) == SbMatrix::identity());
SbBool isMtxIdent = (mtx == SbMatrix::identity());
SbMatrix convInverse, mtxInverse;
if ( !isCnvIdent )
convInverse = conversion->inverse();
if ( !isMtxIdent )
mtxInverse = mtx.inverse();
// Calculate 'matrixWithScale.'
// [matrixWithScale] = [convInverse][scaleMtx][conversion][mtx]
// Create a scaling matrix;
SbMatrix scaleMtx;
scaleMtx.setScale( scale );
// convert it to space at end of matrix,
// [scaleMtx] = [convInverse][scaleMtx][conversion]
if ( !isCnvIdent ) {
scaleMtx.multRight( (*conversion) );
scaleMtx.multLeft( convInverse );
}
// Append this scaling to mtx.
// [mtxWithScale] = [scaleMtx][mtx]
SbMatrix mtxWithScale;
if ( !isMtxIdent ) {
mtxWithScale = mtx;
mtxWithScale.multLeft( scaleMtx );
}
else
mtxWithScale = scaleMtx;
// Extract the new values from the merged matrix.
SbVec3f mrgTrans, mrgScale;
SbRotation mrgRot, mrgScaleOrient;
getTransformFast(mtxWithScale,mrgTrans,mrgRot, mrgScale,mrgScaleOrient);
// Constrain the scaling to be greater than getMinScale().
SbVec3f okayMrgScale = mrgScale;
for (int i = 0; i < 3; i++ ) {
if (okayMrgScale[i] <= getMinScale() )
okayMrgScale[i] = getMinScale();
}
SbVec3f okayScale;
if ( okayMrgScale == mrgScale )
okayScale = scale;
else {
// If we needed to constrain, figure out 'okayScale.'
// First, construct 'okayMtxWithScale.'
// This is a version of 'matrixWithScale' where we replace
// 'mrgScale' with 'okayMrgScale.'
SbMatrix okayMtxWithScale;
okayMtxWithScale.setTransform( mrgTrans, mrgRot,
okayMrgScale, mrgScaleOrient );
// Using the same relationship as earlier:
// [okayMtxWithScale] = [convInverse][okayScaleMtx][conversion][mtx]
// Solve for 'okayScaleMtx' to find the scale matrix that gives
// us the desired result.
SbMatrix okayScaleMtx = okayMtxWithScale;
if ( !isMtxIdent )
okayScaleMtx.multRight( mtxInverse );
if ( !isCnvIdent ) {
okayScaleMtx.multRight( convInverse );
okayScaleMtx.multLeft( (*conversion) );
}
// Get the okayScale from its matrix.
okayScale[0] = okayScaleMtx[0][0];
okayScale[1] = okayScaleMtx[1][1];
okayScale[2] = okayScaleMtx[2][2];
}
// Now we've got a scale (okayScale) and scaleCenter we know we can use.
// Create the right matrix and append it to get our answer.
// [answer] = [convInvserse][scaleAboutCenter][conversion][mtx]
// where: [scaleAboutCenter] = [centerInverse][okayScale][center]
SbMatrix scaleAboutCenter;
scaleAboutCenter.setScale( okayScale );
if ( scaleCenter != SbVec3f(0,0,0) ) {
SbMatrix tm;
tm.setTranslate( scaleCenter );
scaleAboutCenter.multRight( tm );
tm.setTranslate( -scaleCenter );
scaleAboutCenter.multLeft( tm );
}
SbMatrix answer = scaleAboutCenter;
if ( !isCnvIdent ) {
answer.multLeft( convInverse );
answer.multRight( (*conversion) );
}
if ( !isMtxIdent )
answer.multRight( mtx );
return answer;
}
SbMatrix
SoDragger::appendRotation( const SbMatrix &mtx,
const SbRotation &rot, const SbVec3f &rotCenter,
const SbMatrix *conversion )
{
SbBool isCnvIdent = (conversion == NULL
|| (*conversion) == SbMatrix::identity());
SbBool isMtxIdent = (mtx == SbMatrix::identity());
SbMatrix convInverse, mtxInverse;
if ( !isCnvIdent )
convInverse = conversion->inverse();
if ( !isMtxIdent )
mtxInverse = mtx.inverse();
// Create a matrix for rotating about the rotCenter and append it to our
// mtx.
// [answer] = [convInvserse][rotateAboutCenter][conversion][mtx]
// where: [rotateAboutCenter] = [centerInverse][rotMat][center]
SbMatrix rotateAboutCenter;
rotateAboutCenter.setRotate( rot );
if ( rotCenter != SbVec3f(0,0,0) ) {
SbMatrix tm;
tm.setTranslate( rotCenter );
rotateAboutCenter.multRight( tm );
tm.setTranslate( -rotCenter );
rotateAboutCenter.multLeft( tm );
}
SbMatrix answer = rotateAboutCenter;
if ( !isCnvIdent ) {
answer.multLeft( convInverse );
answer.multRight( (*conversion) );
}
if ( !isMtxIdent )
answer.multRight( mtx );
return answer;
}
// Called by the SoBaseKit::write() method.
//
// Draggers don't want to write out fields if they have default vals.
//
// sets isActive to default if default and not connected.
//
// Looks for fields named:
// 'rotation' where value is within TINY of SbRotation::identity()
// 'translation' where value is within TINY of (0,0,0)
// 'center' where value is within TINY of (0,0,0)
// 'scaleFactor' where value is within TINY of (1,1,1)
// and sets them to default if they are not connected from a field.
// (note that most draggers are missing at least some of these, but thats okay)
//
// Then calls the method for SoInteractionKit.
//
// NOTE: Parts which are set to default may still wind up writing to file
// if, for example, they lie on a path.
void
SoDragger::setDefaultOnNonWritingFields()
{
// We don't write out isActive if it has default value.
if ( ! (isActive.isConnected() && isActive.isConnectionEnabled())
&& isActive.getValue() == FALSE )
isActive.setDefault(TRUE);
// Since so many draggers have fields named 'rotation',
// 'translation', 'scaleFactor', and 'center', we'll check for them
// in the base class and set them to default if we can...
#define TINY 0.00001
SoField *f;
if ( (f = getField( "rotation" )) != NULL ) {
if ( !(f->isConnected() && f->isConnectionEnabled())
&& (((SoSFRotation *)f)->getValue()).equals(
SbRotation::identity(),TINY))
f->setDefault(TRUE);
}
if ( (f = getField( "translation" )) != NULL ) {
if ( !(f->isConnected() && f->isConnectionEnabled())
&& (((SoSFVec3f *)f)->getValue()).equals(SbVec3f(0,0,0),TINY))
f->setDefault(TRUE);
}
if ( (f = getField( "scaleFactor" )) != NULL ) {
if ( !(f->isConnected() && f->isConnectionEnabled())
&& (((SoSFVec3f *)f)->getValue()).equals(SbVec3f(1,1,1),TINY))
f->setDefault(TRUE);
}
if ( (f = getField( "center" )) != NULL ) {
if ( !(f->isConnected() && f->isConnectionEnabled())
&& (((SoSFVec3f *)f)->getValue()).equals(SbVec3f(0,0,0),TINY))
f->setDefault(TRUE);
}
#undef TINY
// This node may change after construction, but we still
// don't want to write it out.
motionMatrix.setDefault(TRUE);
// Call the base class...
SoInteractionKit::setDefaultOnNonWritingFields();
}