/*
*
* 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.1.1.1 $
|
| Classes:
| SoDragPointDragger
|
| Author(s): Paul Isaacs, Howard Look
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <stdio.h>
#include <limits.h>
#include <Inventor/SoDB.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/sensors/SoFieldSensor.h>
#include <Inventor/SbLinear.h>
#include <Inventor/SoPath.h>
#include <Inventor/SoPickedPoint.h>
#include <Inventor/events/SoEvent.h>
#include <Inventor/events/SoKeyboardEvent.h>
#include <Inventor/actions/SoHandleEventAction.h>
#include <Inventor/actions/SoGetBoundingBoxAction.h>
#include <Inventor/draggers/SoDragPointDragger.h>
#include <Inventor/draggers/SoTranslate1Dragger.h>
#include <Inventor/draggers/SoTranslate2Dragger.h>
#include <Inventor/nodes/SoNode.h>
#include <Inventor/nodes/SoRotation.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/projectors/SbLineProjector.h>
#include <Inventor/projectors/SbPlaneProjector.h>
#include "geom/SoDragPointDraggerGeom.h"
SO_KIT_SOURCE(SoDragPointDragger);
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor
//
SoDragPointDragger::SoDragPointDragger()
//
////////////////////////////////////////////////////////////////////////
{
SO_KIT_CONSTRUCTOR(SoDragPointDragger);
isBuiltIn = TRUE;
SO_KIT_ADD_CATALOG_ENTRY(noRotSep, SoSeparator, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xTranslatorSwitch, SoSwitch, FALSE,
noRotSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xTranslator, SoTranslate1Dragger, TRUE,
xTranslatorSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xyTranslatorSwitch, SoSwitch, FALSE,
noRotSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xyTranslator, SoTranslate2Dragger, TRUE,
xyTranslatorSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(rotXSep, SoSeparator, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(rotX, SoRotation, TRUE,
rotXSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xzTranslatorSwitch, SoSwitch, FALSE,
rotXSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xzTranslator, SoTranslate2Dragger, TRUE,
xzTranslatorSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(rotYSep, SoSeparator, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(rotY, SoRotation, TRUE,
rotYSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(zTranslatorSwitch, SoSwitch, FALSE,
rotYSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(zTranslator, SoTranslate1Dragger, TRUE,
zTranslatorSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yzTranslatorSwitch, SoSwitch, FALSE,
rotYSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(yzTranslator, SoTranslate2Dragger, TRUE,
yzTranslatorSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(rotZSep, SoSeparator, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(rotZ, SoRotation, TRUE,
rotZSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(yTranslatorSwitch, SoSwitch, FALSE,
rotZSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(yTranslator, SoTranslate1Dragger, TRUE,
yTranslatorSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xFeedbackSwitch, SoSwitch, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xFeedbackSep, SoSeparator, FALSE,
xFeedbackSwitch, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xFeedbackTranslation, SoTranslation, FALSE,
xFeedbackSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(xFeedback, SoSeparator, TRUE,
xFeedbackSep, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yFeedbackSwitch, SoSwitch, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(yFeedbackSep, SoSeparator, FALSE,
yFeedbackSwitch, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(yFeedbackTranslation, SoTranslation, FALSE,
yFeedbackSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(yFeedback, SoSeparator, TRUE,
yFeedbackSep, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zFeedbackSwitch, SoSwitch, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(zFeedbackSep, SoSeparator, FALSE,
zFeedbackSwitch, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(zFeedbackTranslation, SoTranslation, FALSE,
zFeedbackSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(zFeedback, SoSeparator, TRUE,
zFeedbackSep, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(planeFeedbackSep, SoSeparator, FALSE,
topSeparator, geomSeparator,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(planeFeedbackTranslation, SoTranslation, FALSE,
planeFeedbackSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(planeFeedbackSwitch, SoSwitch, FALSE,
planeFeedbackSep, ,FALSE);
SO_KIT_ADD_CATALOG_ENTRY(yzFeedback, SoSeparator, TRUE,
planeFeedbackSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xzFeedback, SoSeparator, TRUE,
planeFeedbackSwitch, ,TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xyFeedback, SoSeparator, TRUE,
planeFeedbackSwitch, ,TRUE);
// read geometry for shared parts
if (SO_KIT_IS_FIRST_INSTANCE())
readDefaultParts("dragPointDragger.iv",geomBuffer,sizeof(geomBuffer) );
// The field that reflects where the dragger has been translated to
SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0));
SO_KIT_INIT_INSTANCE();
// Cached values to make updating the feedback geometry more efficient
oldXAxisNode = NULL;
oldYAxisNode = NULL;
oldZAxisNode = NULL;
//******************
// Set up the parts.
//******************
// Set up the rotations to orient the draggers correctly.
SoRotation *myRotX = new SoRotation;
SoRotation *myRotY = new SoRotation;
SoRotation *myRotZ = new SoRotation;
myRotX->rotation = SbRotation( SbVec3f(1,0,0), 1.57079 );
myRotY->rotation = SbRotation( SbVec3f(0,1,0), 1.57079 );
myRotZ->rotation = SbRotation( SbVec3f(0,0,1), 1.57079 );
setAnyPartAsDefault("rotX", myRotX );
setAnyPartAsDefault("rotY", myRotY );
setAnyPartAsDefault("rotZ", myRotZ );
// CREATE THE CHILD DRAGGERS.
// Create the translate1Draggers...
SoTranslate1Dragger *myXTrans, *myYTrans, *myZTrans;
myXTrans = SO_GET_ANY_PART(this,"xTranslator",SoTranslate1Dragger);
myYTrans = SO_GET_ANY_PART(this,"yTranslator",SoTranslate1Dragger);
myZTrans = SO_GET_ANY_PART(this,"zTranslator",SoTranslate1Dragger);
// Create the translate2Draggers...
SoTranslate2Dragger *myYZTrans, *myXZTrans, *myXYTrans;
myYZTrans = SO_GET_ANY_PART(this,"yzTranslator",SoTranslate2Dragger);
myXZTrans = SO_GET_ANY_PART(this,"xzTranslator",SoTranslate2Dragger);
myXYTrans = SO_GET_ANY_PART(this,"xyTranslator",SoTranslate2Dragger);
//******************
// The feedback parts jump around as the limit box changes. That is, they
// stay fixed in space while the dragger moves around.
// However, they jump to a new location when the dragger nears the edge.
// These parts a separate translation node, since they move differently
// than the dragger itself.
//******************
//******************
// The feedback parts jump around as the limit box changes. That is, they
// stay fixed in space while the dragger moves around.
// However, they jump to a new location when the dragger nears the edge.
// These parts a separate translation node, since they move differently
// than the dragger itself.
//
// Only one plane or one axis is shown at a time, depending on which
// translator has initiated the dragging.
//******************
setPartAsDefault("xFeedback", "dragPointXFeedback");
setPartAsDefault("yFeedback", "dragPointYFeedback");
setPartAsDefault("zFeedback", "dragPointZFeedback");
setPartAsDefault("yzFeedback", "dragPointYZFeedback");
setPartAsDefault("xzFeedback", "dragPointXZFeedback");
setPartAsDefault("xyFeedback", "dragPointXYFeedback");
//********************
// initialize state, limitbox, gesture variables
//********************
// To begin with, only the yTranslator and xzTranslators are turned on.
// You can switch between pairs of line/plane draggers by hitting the
// CONTROL key
setSwitchValue(xTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yTranslatorSwitch.getValue(), 0 );
setSwitchValue(zTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yzTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(xzTranslatorSwitch.getValue(), 0 );
setSwitchValue(xyTranslatorSwitch.getValue(), SO_SWITCH_NONE );
// ??? Would be cool to be able to choose a free
// axis, rotate it around to whatever direction, maybe even
// have it snap to things in the scene, then constrain
// dragging to that line.
// Start off inactive
currentDragger = NULL;
// The state of the modifier keys
shftDown = FALSE;
// Need to initialize since checkBoxLimits will look at it...
startLocalHitPt.setValue(0,0,0);
// The jump axes will jump when the edit point gets within
// 10% of their ends
jumpLimit = .1;
// makes the offsetWorkLimit box
limitBox.makeEmpty();
updateLimitBoxAndFeedback();
// These will be called by the child draggers after they call
// their own callbacks...
addStartCallback( &SoDragPointDragger::startCB );
addMotionCallback( &SoDragPointDragger::motionCB );
addFinishCallback( &SoDragPointDragger::finishCB );
// When modify keys are released, we need to turn off any constraints.
addOtherEventCallback( &SoDragPointDragger::metaKeyChangeCB );
// Updates the translation field when the motionMatrix is set.
addValueChangedCallback( &SoDragPointDragger::valueChangedCB );
// Updates the motionMatrix when the translation field is set.
fieldSensor = new SoFieldSensor( &SoDragPointDragger::fieldSensorCB, this);
fieldSensor->setPriority( 0 );
setUpConnections( TRUE, TRUE );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor
//
// Use: public
//
SoDragPointDragger::~SoDragPointDragger()
//
////////////////////////////////////////////////////////////////////////
{
if (fieldSensor)
delete fieldSensor;
}
// detach/attach any sensors, callbacks, and/or field connections.
// Called by: start/end of SoBaseKit::readInstance
// and on new copy by: start/end of SoBaseKit::copy.
// Classes that redefine must call setUpConnections(TRUE,TRUE)
// at end of constructor.
// Returns the state of the node when this was called.
SbBool
SoDragPointDragger::setUpConnections( SbBool onOff, SbBool doItAlways )
{
if ( !doItAlways && connectionsSetUp == onOff)
return onOff;
if ( onOff ) {
// We connect AFTER base class.
SoDragger::setUpConnections( onOff, FALSE );
// SET Parts and callbacks on CHLD DRAGGERS
// Create the translate1Draggers...
SoSeparator *dummySep = new SoSeparator;
dummySep->ref();
SoDragger *xD, *yD, *zD;
xD = (SoDragger *) getAnyPart("xTranslator",FALSE);
yD = (SoDragger *) getAnyPart("yTranslator",FALSE);
zD = (SoDragger *) getAnyPart("zTranslator",FALSE);
if (xD) {
xD->setPartAsDefault("translator","dragPointXTranslatorTranslator");
xD->setPartAsDefault("translatorActive",
"dragPointXTranslatorTranslatorActive");
xD->setPartAsDefault("feedback", dummySep );
xD->setPartAsDefault("feedbackActive", dummySep );
registerChildDragger( xD );
}
if (yD) {
yD->setPartAsDefault("translator","dragPointYTranslatorTranslator");
yD->setPartAsDefault("translatorActive",
"dragPointYTranslatorTranslatorActive");
yD->setPartAsDefault("feedback", dummySep );
yD->setPartAsDefault("feedbackActive", dummySep );
registerChildDragger( yD );
}
if (zD) {
zD->setPartAsDefault("translator","dragPointZTranslatorTranslator");
zD->setPartAsDefault("translatorActive",
"dragPointZTranslatorTranslatorActive");
zD->setPartAsDefault("feedback", dummySep );
zD->setPartAsDefault("feedbackActive", dummySep );
registerChildDragger( zD );
}
// Create the translate2Draggers...
SoDragger *yzD, *xzD, *xyD;
yzD = (SoDragger *) getAnyPart("yzTranslator",FALSE);
xzD = (SoDragger *) getAnyPart("xzTranslator",FALSE);
xyD = (SoDragger *) getAnyPart("xyTranslator",FALSE);
if (yzD) {
yzD->setPartAsDefault("translator",
"dragPointYZTranslatorTranslator");
yzD->setPartAsDefault("translatorActive",
"dragPointYZTranslatorTranslatorActive");
yzD->setPartAsDefault("feedback", dummySep );
yzD->setPartAsDefault("feedbackActive", dummySep );
yzD->setPartAsDefault("xAxisFeedback", dummySep );
yzD->setPartAsDefault("yAxisFeedback", dummySep );
registerChildDragger( yzD );
}
if (xzD) {
xzD->setPartAsDefault("translator",
"dragPointXZTranslatorTranslator");
xzD->setPartAsDefault("translatorActive",
"dragPointXZTranslatorTranslatorActive");
xzD->setPartAsDefault("feedback", dummySep );
xzD->setPartAsDefault("feedbackActive", dummySep );
xzD->setPartAsDefault("xAxisFeedback", dummySep );
xzD->setPartAsDefault("yAxisFeedback", dummySep );
registerChildDragger( xzD );
}
if (xyD) {
xyD->setPartAsDefault("translator",
"dragPointXYTranslatorTranslator");
xyD->setPartAsDefault("translatorActive",
"dragPointXYTranslatorTranslatorActive");
xyD->setPartAsDefault("feedback", dummySep );
xyD->setPartAsDefault("feedbackActive", dummySep );
xyD->setPartAsDefault("xAxisFeedback", dummySep );
xyD->setPartAsDefault("yAxisFeedback", dummySep );
registerChildDragger( xyD );
}
dummySep->unref();
// Call the sensor CBs to make things are up-to-date.
fieldSensorCB( this, NULL );
// Connect the field sensors
if (fieldSensor->getAttachedField() != &translation)
fieldSensor->attach( &translation );
}
else {
// We disconnect BEFORE base class.
// Remove callbacks from CHLD DRAGGERS
SoDragger *xD, *yD, *zD;
xD = (SoDragger *) getAnyPart("xTranslator",FALSE);
yD = (SoDragger *) getAnyPart("yTranslator",FALSE);
zD = (SoDragger *) getAnyPart("zTranslator",FALSE);
if (xD)
unregisterChildDragger( xD );
if (yD)
unregisterChildDragger( yD );
if (zD)
unregisterChildDragger( zD );
SoDragger *yzD, *xzD, *xyD;
yzD = (SoDragger *) getAnyPart("yzTranslator",FALSE);
xzD = (SoDragger *) getAnyPart("xzTranslator",FALSE);
xyD = (SoDragger *) getAnyPart("xyTranslator",FALSE);
if (yzD)
unregisterChildDragger( yzD );
if (xzD)
unregisterChildDragger( xzD );
if (xyD)
unregisterChildDragger( xyD );
// Disconnect the field sensors.
if (fieldSensor->getAttachedField())
fieldSensor->detach();
SoDragger::setUpConnections( onOff, FALSE );
}
return !(connectionsSetUp = onOff);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Called by a child dragger after dragStart.
//
// Figures out which dragger is active, then updates the feedback
// accordingly
//
// Use: protected
//
void
SoDragPointDragger::dragStart()
//
////////////////////////////////////////////////////////////////////////
{
currentDragger = getActiveChildDragger();
// If there is no active child dragger, then we should just return.
if ( currentDragger == NULL)
return;
shftDown = getEvent()->wasShiftDown();
// Save the starting point as expressed in local space.
// We need to do this so that our feedback will work nicely.
startLocalHitPt = getLocalStartingPoint();
updateLimitBoxAndFeedback();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Called by a child dragger after dragging.
// Update the feedback to sit in the right place.
//
// Use: protected
//
void
SoDragPointDragger::drag()
//
////////////////////////////////////////////////////////////////////////
{
updateLimitBoxAndFeedback();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Called by a child dragger after dragFinish.
//
// Use: protected
//
void
SoDragPointDragger::dragFinish()
//
////////////////////////////////////////////////////////////////////////
{
currentDragger = NULL;
updateLimitBoxAndFeedback();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the feedback geometry depending on which dragger is active.
// A 1-D translator results in that line's feedback turning on.
// A 2-D translator results in that plane's feedback turning on.
//
// Use: private
//
void
SoDragPointDragger::setFeedbackGeometry()
//
////////////////////////////////////////////////////////////////////////
{
// First, figure out if we need to know a constrained translation
// direction. This happens if a plane dragger is in use, but is
// being constrained.
int translateDir = -1;
#define TINY .0001
if ( shftDown ) {
SbVec3f current = getMotionMatrix()[3];
SbVec3f start = getStartMotionMatrix()[3];
SbVec3f motion = current - start;
if ( fabs( motion[0]) > fabs( motion[1])
&& fabs( motion[0]) > fabs( motion[2]))
translateDir = 0;
else if ( fabs( motion[1]) > fabs( motion[2]))
translateDir = 1;
else if ( fabs( motion[2] ) > TINY )
translateDir = 2;
}
#undef TINY
// Turn on appropriate parts, based on state.
if (currentDragger == NULL) {
setSwitchValue(xFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(zFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(planeFeedbackSwitch.getValue(), SO_SWITCH_NONE );
}
else if (currentDragger == xTranslator.getValue() ) {
setSwitchValue(xFeedbackSwitch.getValue(), 0 );
setSwitchValue(yFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(zFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(planeFeedbackSwitch.getValue(), SO_SWITCH_NONE );
}
else if (currentDragger == yTranslator.getValue() ) {
setSwitchValue(xFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yFeedbackSwitch.getValue(), 0 );
setSwitchValue(zFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(planeFeedbackSwitch.getValue(), SO_SWITCH_NONE );
}
else if (currentDragger == zTranslator.getValue() ) {
setSwitchValue(xFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(zFeedbackSwitch.getValue(), 0 );
setSwitchValue(planeFeedbackSwitch.getValue(), SO_SWITCH_NONE );
}
else if (currentDragger == yzTranslator.getValue() ) {
setSwitchValue(xFeedbackSwitch.getValue(), SO_SWITCH_NONE );
if ( translateDir == -1 || translateDir == 1 )
setSwitchValue(yFeedbackSwitch.getValue(), 0 );
else
setSwitchValue(yFeedbackSwitch.getValue(), SO_SWITCH_NONE );
if ( translateDir == -1 || translateDir == 2 )
setSwitchValue(zFeedbackSwitch.getValue(), 0 );
else
setSwitchValue(zFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(planeFeedbackSwitch.getValue(), 0 );
}
else if (currentDragger == xzTranslator.getValue() ) {
if ( translateDir == -1 || translateDir == 0 )
setSwitchValue(xFeedbackSwitch.getValue(), 0 );
else
setSwitchValue(xFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yFeedbackSwitch.getValue(), SO_SWITCH_NONE );
if ( translateDir == -1 || translateDir == 2 )
setSwitchValue(zFeedbackSwitch.getValue(), 0 );
else
setSwitchValue(zFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(planeFeedbackSwitch.getValue(), 1 );
}
else if (currentDragger == xyTranslator.getValue() ) {
if ( translateDir == -1 || translateDir == 0 )
setSwitchValue(xFeedbackSwitch.getValue(), 0 );
else
setSwitchValue(xFeedbackSwitch.getValue(), SO_SWITCH_NONE );
if ( translateDir == -1 || translateDir == 1 )
setSwitchValue(yFeedbackSwitch.getValue(), 0 );
else
setSwitchValue(yFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(zFeedbackSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(planeFeedbackSwitch.getValue(), 2 );
}
checkBoxLimits();
//**************************************
// Set the transforms for the feedback axes and/or planes.
//**************************************
// First, find the center and scale for each part, in LOCAL space.
// The limit box is defined in a space aligned and scaled as
// LOCAL space, but with it's center remaining fixed in WORLD
// space.
// We need to transform the center of the limitBox
// from WORLD space to LOCAL space to
// find the location of the center of the limit box feedback.
SbMatrix worldToLocal = getWorldToLocalMatrix();
SbVec3f limitBoxCenterInLocal = limitBox.getCenter();
worldToLocal.multVecMatrix( limitBoxCenterInLocal,
limitBoxCenterInLocal );
SbVec3f xAxisSpot = limitBoxCenterInLocal;
SbVec3f yAxisSpot = limitBoxCenterInLocal;
SbVec3f zAxisSpot = limitBoxCenterInLocal;
SbVec3f planeSpot = limitBoxCenterInLocal;
//With center matching mouse location...
// The point under the mouse is stored as 'startLocalHitPt', which is
// expressed in LOCAL space.
xAxisSpot[1] = startLocalHitPt[1];
xAxisSpot[2] = startLocalHitPt[2];
yAxisSpot[0] = startLocalHitPt[0];
yAxisSpot[2] = startLocalHitPt[2];
zAxisSpot[0] = startLocalHitPt[0];
zAxisSpot[1] = startLocalHitPt[1];
if ( currentDragger == yzTranslator.getValue()) {
planeSpot[0] = startLocalHitPt[0];
}
else if ( currentDragger == xzTranslator.getValue()) {
planeSpot[1] = startLocalHitPt[1];
}
else if ( currentDragger == xyTranslator.getValue()) {
planeSpot[2] = startLocalHitPt[2];
}
SoTranslation *transNode;
// Set position of each axis feedback
if ( xFeedbackTranslation.getValue() == NULL )
setAnyPart( "xFeedbackTranslation", new SoTranslation );
transNode = (SoTranslation *) xFeedbackTranslation.getValue();
transNode->translation = xAxisSpot;
if ( yFeedbackTranslation.getValue() == NULL )
setAnyPart( "yFeedbackTranslation", new SoTranslation );
transNode = (SoTranslation *) yFeedbackTranslation.getValue();
transNode->translation = yAxisSpot;
if ( zFeedbackTranslation.getValue() == NULL )
setAnyPart( "zFeedbackTranslation", new SoTranslation );
transNode = (SoTranslation *) zFeedbackTranslation.getValue();
transNode->translation = zAxisSpot;
// Set position of plane feedback
if ( planeFeedbackTranslation.getValue() == NULL )
setAnyPart( "planeFeedbackTranslation", new SoTranslation );
transNode = (SoTranslation *) planeFeedbackTranslation.getValue();
transNode->translation = planeSpot;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
//
// Sets switches to show one line dragger and one plane dragger.
// It cycles through 3 different configurations:
// xline/yzPlane, yline/xzplane, zline/xyplane
//
////////////////////////////////////////////////////////////////////////
void
SoDragPointDragger::showNextDraggerSet()
{
SoSwitch *sw;
// Case 1. Currently, xline is displayed. Switch to y.
sw = (SoSwitch *) xTranslatorSwitch.getValue();
if ( sw && sw->whichChild.getValue() != SO_SWITCH_NONE ) {
setSwitchValue(xTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yTranslatorSwitch.getValue(), 0 );
setSwitchValue(zTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yzTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(xzTranslatorSwitch.getValue(), 0 );
setSwitchValue(xyTranslatorSwitch.getValue(), SO_SWITCH_NONE );
return;
}
// Case 2. Currently, yline is displayed. Switch to z.
sw = (SoSwitch *) yTranslatorSwitch.getValue();
if ( sw && sw->whichChild.getValue() != SO_SWITCH_NONE ) {
setSwitchValue(xTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(zTranslatorSwitch.getValue(), 0 );
setSwitchValue(yzTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(xzTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(xyTranslatorSwitch.getValue(), 0 );
return;
}
// Case 3. Currently, zline is displayed. Switch to x.
sw = (SoSwitch *) zTranslatorSwitch.getValue();
if ( sw && sw->whichChild.getValue() != SO_SWITCH_NONE ) {
setSwitchValue(xTranslatorSwitch.getValue(), 0 );
setSwitchValue(yTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(zTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(yzTranslatorSwitch.getValue(), 0 );
setSwitchValue(xzTranslatorSwitch.getValue(), SO_SWITCH_NONE );
setSwitchValue(xyTranslatorSwitch.getValue(), SO_SWITCH_NONE );
return;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This routine sets the limitBox.
// The limit box is defined in a space aligned and scaled as
// LOCAL space, but with it's center remaining fixed in WORLD
// space.
//
// Use: static private
//
void
SoDragPointDragger::updateLimitBoxAndFeedback()
//
////////////////////////////////////////////////////////////////////////
{
// This gets called in the constructor, while the ref count is still 0.
// Since there will be some ref'ing and unref'ing done inside here,
// add a temporary ref and undo it at the end.
ref();
if ( xFeedback.getValue() != oldXAxisNode ||
yFeedback.getValue() != oldYAxisNode ||
zFeedback.getValue() != oldZAxisNode ) {
oldXAxisNode = SO_GET_ANY_PART(this,"xFeedback",SoSeparator);
oldYAxisNode = SO_GET_ANY_PART(this,"yFeedback",SoSeparator);
oldZAxisNode = SO_GET_ANY_PART(this,"zFeedback",SoSeparator);
// Get the bounds of the axis parts.
static SoGetBoundingBoxAction *bba = NULL;
if (bba == NULL)
bba = new SoGetBoundingBoxAction(getViewportRegion());
else
bba->setViewportRegion(getViewportRegion());
float xMin, yMin, zMin, xMax, yMax, zMax;
SbVec3f min, max;
bba->apply(xFeedback.getValue());
bba->getBoundingBox().getBounds( xMin, yMin, zMin, xMax, yMax, zMax );
min[0] = xMin;
max[0] = xMax;
bba->apply(yFeedback.getValue());
bba->getBoundingBox().getBounds( xMin, yMin, zMin, xMax, yMax, zMax );
min[1] = yMin;
max[1] = yMax;
bba->apply(zFeedback.getValue());
bba->getBoundingBox().getBounds( xMin, yMin, zMin, xMax, yMax, zMax );
min[2] = zMin;
max[2] = zMax;
// The limit box is defined in a space aligned and scaled as
// LOCAL space, but with it's center remaining fixed in WORLD
// space.
SbVec3f newDiag = (max - min) / 2.0;
// Give a default size of 1, in case no axis parts exist.
for (int i = 0; i < 3; i++) {
if (newDiag[i] <= getMinScale())
newDiag[i] = 1.0;
}
SbVec3f oldDiag = limitBox.getMax() -
limitBox.getCenter();
// If the size of the boundingBox has changed...
if ( newDiag != oldDiag ) {
// curEdit point needs to be the current origin expressed in world
// space (i.e., this bizarro space).
SbMatrix localToWorld = getLocalToWorldMatrix();
SbVec3f zeroPt(0,0,0);
localToWorld.multVecMatrix( zeroPt, zeroPt );
limitBox.setBounds(zeroPt - newDiag, zeroPt + newDiag );
}
}
setFeedbackGeometry();
// undo the temporary ref.
unrefNoDelete();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Checks the limit box and extends it if necessary
//
// Use: private
void
SoDragPointDragger::checkBoxLimits()
//
////////////////////////////////////////////////////////////////////////
{
// The limit box is defined in a space aligned and scaled as LOCAL
// space, but with it's center remaining fixed in WORLD space.
// We need to do this work in LOCAL space, so to begin with,
// get the location of the center of this box in LOCAL space.
SbMatrix worldToLocal = getWorldToLocalMatrix();
SbVec3f limitBoxCenterInLocal = limitBox.getCenter();
worldToLocal.multVecMatrix( limitBoxCenterInLocal,
limitBoxCenterInLocal );
// Now, if our current position is out of range, we need to move
// the limit box center accordingly. We continue to do our work
// in LOCAL space.
SbBool changed = FALSE;
SbVec3f boxSize = limitBox.getMax() - limitBox.getMin();
for (int i = 0; i < 3; i++) {
float length = boxSize[i];
float halfLength = length * 0.5;
// Check the location of startLocalHitPt against boundaries of the limit
// box (keeping in mind the jump limit as a % of the total length).
// Have we gone too far in the positive direction?
float high = limitBoxCenterInLocal[i] + halfLength;
while ( (high - startLocalHitPt[i]) / length < jumpLimit ) {
limitBoxCenterInLocal[i] += halfLength;
high += halfLength;
changed = TRUE;
}
// Have we gone too far in the negative direction?
float low = limitBoxCenterInLocal[i] - halfLength;
while (( startLocalHitPt[i] - low ) / length < jumpLimit ) {
limitBoxCenterInLocal[i] -= halfLength;
low -= halfLength;
changed = TRUE;
}
}
if (changed == TRUE ) {
// First, convert the changed limitBoxCenterInLocal
// into WORLD space...
SbMatrix localToWorld = getLocalToWorldMatrix();
SbVec3f newCenter;
localToWorld.multVecMatrix(limitBoxCenterInLocal,newCenter);
// Next, set the bounds of the limit box to have the same size
// as before, but centered about this new point.
SbVec3f diag = limitBox.getMax() -
limitBox.getCenter();
limitBox.setBounds( newCenter - diag, newCenter + diag );
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Check to see if the Shift or Control key changed.
void
SoDragPointDragger::metaKeyChangeCB( void *, SoDragger *inDragger )
//
////////////////////////////////////////////////////////////////////////
{
SoDragPointDragger *dp = (SoDragPointDragger *) inDragger;
SoHandleEventAction *ha = dp->getHandleEventAction();
// We care if the shift key is down for drawing feedback.
const SoEvent *event = dp->getEvent();
dp->shftDown = event->wasShiftDown();
// Don't check for grabber yet.
// CONTROL key press overrides if the grabber is a childDragger.
// [1] We only respond to CONTROL key press events.
if ( !SO_KEY_PRESS_EVENT(event, LEFT_CONTROL) &&
!SO_KEY_PRESS_EVENT(event, RIGHT_CONTROL))
return;
//[2] Should we return?
// Stay here if there's no grabber and the mouse is over us,
// or over a surrogate part.
// Stay here if we are the grabber.
// Stay here if our currentDragger is grabbing events.
// Return if anyone else is grabbing.
SbBool takeIt = FALSE;
if ( ha->getGrabber() == NULL ) {
// grabber is NULL...
const SoPickedPoint *p = ha->getPickedPoint();
if ( p && p->getPath()) {
// Are we on the pickPath?
// Or is the path a surrogate path for us or any draggers
// contained within us?
if ( p->getPath()->containsNode(dp) ||
dp->isPathSurrogateInMySubgraph(p->getPath()) )
takeIt = TRUE;
}
}
else if ( ha->getGrabber() == dp )
takeIt = TRUE;
else if ( ha->getGrabber() == dp->currentDragger )
takeIt = TRUE;
else
takeIt = FALSE;
if ( !takeIt )
return;
//[3] Now, switch the set of visible draggers...
dp->showNextDraggerSet();
//[4] If a child is grabbing, release events and hand it all over to
// the next one...
SoDragger *oldDragger = dp->currentDragger;
if (oldDragger) {
// Ref the oldDragger.
oldDragger->ref();
// Release the grabber. This will call grabEventsCleanUp() if
// the grabber is a dragger.
ha->releaseGrabber();
// If there was an oldDragger,
// [a] select a new dragger to grab events.
// [b] Set up a plane or line projector oriented like newDragger.
// [c] Find out where current mouse position intersects that new
// plane or line. The new gesture will continue from there.
SoDragger *newDragger;
SbVec3f projPt;
SbLineProjector lp;
SbPlaneProjector pp;
lp.setViewVolume( dp->getViewVolume() );
pp.setViewVolume( dp->getViewVolume() );
lp.setWorkingSpace( dp->getLocalToWorldMatrix() );
pp.setWorkingSpace( dp->getLocalToWorldMatrix() );
if ( oldDragger == dp->xTranslator.getValue() ) {
newDragger = (SoDragger *) dp->yTranslator.getValue();
lp.setLine( SbLine( SbVec3f(0,0,0), SbVec3f(0,1,0)));
projPt = lp.project(dp->getNormalizedLocaterPosition());
}
else if ( oldDragger == dp->yTranslator.getValue() ) {
newDragger = (SoDragger *) dp->zTranslator.getValue();
lp.setLine( SbLine( SbVec3f(0,0,0), SbVec3f(0,0,1)));
projPt = lp.project(dp->getNormalizedLocaterPosition());
}
else if ( oldDragger == dp->zTranslator.getValue() ) {
newDragger = (SoDragger *) dp->xTranslator.getValue();
lp.setLine( SbLine( SbVec3f(0,0,0), SbVec3f(1,0,0)));
projPt = lp.project(dp->getNormalizedLocaterPosition());
}
else if ( oldDragger == dp->yzTranslator.getValue() ) {
newDragger = (SoDragger *) dp->xzTranslator.getValue();
pp.setPlane( SbPlane( SbVec3f(0,1,0), SbVec3f(0,0,0)));
projPt = pp.project(dp->getNormalizedLocaterPosition());
}
else if ( oldDragger == dp->xzTranslator.getValue() ) {
newDragger = (SoDragger *) dp->xyTranslator.getValue();
pp.setPlane( SbPlane( SbVec3f(0,0,1), SbVec3f(0,0,0)));
projPt = pp.project(dp->getNormalizedLocaterPosition());
}
else if ( oldDragger == dp->xyTranslator.getValue() ) {
newDragger = (SoDragger *) dp->yzTranslator.getValue();
pp.setPlane( SbPlane( SbVec3f(1,0,0), SbVec3f(0,0,0)));
projPt = pp.project(dp->getNormalizedLocaterPosition());
}
// unref oldDragger. We don't need it any more.
oldDragger->unref();
// Give newDragger our handleEvent action.
newDragger->setHandleEventAction(ha);
// Cast the projPt into world space for the new starting point.
dp->getLocalToWorldMatrix().multVecMatrix(projPt,projPt);
newDragger->setStartingPoint( projPt );
// Give the newDragger a path to itself
SoPath *pathToDragger;
// We must ref() & unref() dpThisPath to dispose of it.
SoPath *dpThisPath = dp->createPathToThis();
if (dpThisPath) dpThisPath->ref();
pathToDragger = dp->createPathToPart(
dp->getPartString(newDragger), TRUE, dpThisPath );
if (dpThisPath) dpThisPath->unref();
if (pathToDragger)
pathToDragger->ref();
newDragger->setTempPathToThis( pathToDragger );
if (pathToDragger)
pathToDragger->unref();
// Give the newDragger our viewing information.
newDragger->setViewVolume(dp->getViewVolume());
newDragger->setViewportRegion(dp->getViewportRegion());
// Set the grabber. This will call starting callbacks on the new
// grabber, as well as it's registered parent, moi!
ha->setGrabber( newDragger );
}
//[5] set handled
ha->setHandled();
}
////////////////////////////////////////////////////////////////////
// Stubs for callbacks
////////////////////////////////////////////////////////////////////
void
SoDragPointDragger::startCB( void *, SoDragger *inDragger )
{
SoDragPointDragger *dp = (SoDragPointDragger *) inDragger;
dp->dragStart();
}
void
SoDragPointDragger::motionCB( void *, SoDragger *inDragger )
{
SoDragPointDragger *dp = (SoDragPointDragger *) inDragger;
dp->drag();
}
void
SoDragPointDragger::finishCB( void *, SoDragger *inDragger )
{
SoDragPointDragger *dp = (SoDragPointDragger *) inDragger;
dp->dragFinish();
}
void
SoDragPointDragger::valueChangedCB( void *, SoDragger *inDragger )
{
SoDragPointDragger *m = (SoDragPointDragger *) inDragger;
SbMatrix motMat = m->getMotionMatrix();
SbVec3f trans, scale;
SbRotation rot, scaleOrient;
getTransformFast( motMat, trans, rot, scale, scaleOrient );
// Disconnect the field sensor
m->fieldSensor->detach();
if ( m->translation.getValue() != trans )
m->translation = trans;
// Reconnect the field sensor
m->fieldSensor->attach( &(m->translation) );
}
void
SoDragPointDragger::fieldSensorCB( void *inDragger, SoSensor *)
{
SoDragPointDragger *dragger = (SoDragPointDragger *) inDragger;
// Incorporate the new field value into the matrix...
SbMatrix motMat = dragger->getMotionMatrix();
dragger->workFieldsIntoTransform( motMat );
dragger->setMotionMatrix( motMat );
}
void
SoDragPointDragger::setDefaultOnNonWritingFields()
{
// These nodes may change after construction, but we still
// don't want to write them out.
xFeedbackTranslation.setDefault(TRUE);
yFeedbackTranslation.setDefault(TRUE);
zFeedbackTranslation.setDefault(TRUE);
planeFeedbackTranslation.setDefault(TRUE);
// Try not to write out the sub-draggers.
xTranslator.setDefault(TRUE);
yTranslator.setDefault(TRUE);
zTranslator.setDefault(TRUE);
xyTranslator.setDefault(TRUE);
xzTranslator.setDefault(TRUE);
yzTranslator.setDefault(TRUE);
// Call the base class...
SoDragger::setDefaultOnNonWritingFields();
}