/*
*
* 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/
*
*/
/*
| Classes:
| WorldInfo
|
| Author(s): Paul Isaacs
|
*/
#include <stdio.h>
#include <Inventor/SbLinear.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoLists.h>
#include <Inventor/SbViewportRegion.h>
#include <Inventor/SoPickedPoint.h>
#include <Inventor/actions/SoGetBoundingBoxAction.h>
#include <Inventor/actions/SoSearchAction.h>
#include <Inventor/manips/SoHandleBoxManip.h>
#include <Inventor/nodes/SoSelection.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/errors/SoDebugError.h>
#include "WorldInfo.h"
#include "GeneralizedCylinder.h"
WorldInfo::WorldInfo()
{
// Make worldRoot
worldRoot = new SoSeparator;
worldRoot->ref();
// Add selection node.
selector = new SoSelection;
worldRoot->addChild(selector);
selector->addSelectionCallback( &WorldInfo::selectionCB, this );
selector->addDeselectionCallback( &WorldInfo::deselectionCB, this );
selector->setPickFilterCallback( &WorldInfo::pickFilterCB, this );
// Put sceneRoot under selector
sceneRoot = new SoSeparator;
selector->addChild( sceneRoot );
// Create the deletedNoodles list. We store them so they can be un-deleted.
deletedNoodles = new SoNodeList;
// This piece will be dragged in z whenever the activePlane moves.
currentNoodle = NULL;
// Initialize other variables.
fileName = NULL;
manipType = SoTransform::getClassTypeId();
}
WorldInfo::~WorldInfo()
{
if ( worldRoot ) worldRoot->unref();
if ( deletedNoodles ) delete deletedNoodles;
}
/////////////////////////////////////////////////////////////////////
// Set the type of manipulator being used to move selected noodles.
// Then install one in each selected noodle.
//
void
WorldInfo::setManipType( SoType newType )
{
if ( newType == manipType)
return;
if ( ! newType.isDerivedFrom( SoTransform::getClassTypeId() ))
return;
manipType = newType;
// Get all selections; replace their transforms with the new type.
const SoPathList *pl = selector->getList();
for (int i = 0; i < selector->getNumSelected(); i++ ) {
SoNode *t = (*pl)[i]->getTail();
if (t->isOfType(GeneralizedCylinder::getClassTypeId()))
((GeneralizedCylinder *)t)->changeTransformType(newType);
}
}
/////////////////////////////////////////////////////////////////////
// Returns a copy of the scene with all noodles replaced by subgraphs
// of standard inventor nodes.
SoSeparator *
WorldInfo::getVanillaSceneCopy()
{
if (sceneRoot == NULL)
return NULL;
SoSeparator *myCopy = (SoSeparator *) sceneRoot->copy();
myCopy->ref();
// Replace every GeneralizedCylinder in the copy with just the
// data that draws, removing the nodekit.
SoSearchAction *sa = new SoSearchAction;
sa->setType( GeneralizedCylinder::getClassTypeId() );
sa->setInterest( SoSearchAction::ALL );
sa->apply( myCopy );
SoPathList *genCylPaths = &(sa->getPaths());
SoFullPath *myPath;
SoNode *myParent;
GeneralizedCylinder *gc;
SoGroup *parentGroup;
for ( int i = 0; i < genCylPaths->getLength(); i++ ) {
myPath = (SoFullPath *) (*genCylPaths)[i];
myParent = myPath->getNode( myPath->getLength() - 2 );
gc = (GeneralizedCylinder *) myPath->getTail();
if ( myParent->isOfType( SoGroup::getClassTypeId() ) == FALSE ) {
fprintf(stderr,"Error converting scene to vanilla. Found a \n");
fprintf(stderr,"non-group parent of the generalized cyliinder\n");
break;
}
parentGroup = (SoGroup *) myParent;
SoSeparator *vanillaObject = gc->makeVanillaVersion();
parentGroup->replaceChild( gc, vanillaObject );
}
// Delete the search action BEFORE unrefNodelete. This takes
// count down to 1.
delete sa;
// Take count from 1 to 0
myCopy->unrefNoDelete();
return myCopy;
}
void
WorldInfo::setScene( SoSeparator *newScene )
{
if (newScene == sceneRoot)
return;
// Deselect everything.
selector->deselectAll();
// Always have at least an empty separator in the scene.
if (newScene == NULL)
newScene = new SoSeparator;
if (sceneRoot == NULL) {
#ifdef DEBUG
SoDebugError::post("WorldInfo::setScene", "old sceneRoot is NULL");
#endif
selector->insertChild( newScene, 0 );
}
else
selector->replaceChild( sceneRoot, newScene );
sceneRoot = newScene;
// Now, select the first noodle in the scene
setFirstNoodleCurrent();
}
SbBool
WorldInfo::isSceneEmpty()
{
if (sceneRoot == NULL) {
#ifdef DEBUG
SoDebugError::post("WorldInfo::setScene", "old sceneRoot is NULL");
#endif
return TRUE;
}
else
return ( sceneRoot->getNumChildren() == 0);
}
void
WorldInfo::setFileName( char *newFileName )
{
if (newFileName == NULL)
fileName = NULL;
else
fileName = strdup( newFileName );
}
void
WorldInfo::addNoodle( GeneralizedCylinder *newNoodle )
{
if (newNoodle == NULL)
return;
sceneRoot->addChild( newNoodle );
}
void
WorldInfo::deleteNoodle( GeneralizedCylinder *victim )
{
// See if victim is in the selection list...
// We'd prefer to delete based on selection path, if possible, since we
// can then deselect.
SoFullPath *victimPath = NULL;
for (int i = 0; i < selector->getNumSelected(); i++ ) {
if ( ((SoNodeKitPath *)selector->getPath(i))->getTail() == victim ) {
victimPath = (SoFullPath *) selector->getPath(i);
victimPath->ref();
break;
}
}
if (victimPath) {
// deselect old one if it is selected...
selector->deselect(victimPath);
}
else {
// If it wasn't a selection, get a path to it...
SoSearchAction sa;
sa.setNode( victim );
sa.setInterest( SoSearchAction::FIRST );
SbBool wasSearchingKits = SoBaseKit::isSearchingChildren();
SoBaseKit::setSearchingChildren(TRUE);
sa.apply( selector );
SoBaseKit::setSearchingChildren(wasSearchingKits);
victimPath = (SoFullPath *) sa.getPath();
if (victimPath)
victimPath->ref();
}
if ( ! victimPath )
return;
// Remove piece from parent.
if (victimPath != NULL) {
// Get index of victim in the path...
for (int objInd = victimPath->getLength() - 1; objInd >= 0; objInd --) {
if ( victimPath->getNode(objInd) == victim )
break;
}
SoNode *parent = NULL;
SoType bkt = SoBaseKit::getClassTypeId();
// Remove victim from its parent.
// For parent, if there's a nodekit above victim, use that first kit.
for (int pInd = objInd - 1; pInd >= 0; pInd --) {
if (victimPath->getNode(pInd)->isOfType( bkt ) ) {
parent = victimPath->getNode(pInd);
SoBaseKit *k = (SoBaseKit *)parent;
k->setPart( k->getPartString(victim), NULL );
break;
}
}
// Otherwise, use the first node above victim.
if ( parent == NULL ) {
parent = victimPath->getNodeFromTail( 1 );
if (parent->isOfType( SoGroup::getClassTypeId() ))
((SoGroup *)parent)->removeChild( victim );
}
}
victimPath->unref();
}
void
WorldInfo::deleteCurrentNoodle()
{
if (currentNoodle == NULL)
return;
currentNoodle->ref();
deleteNoodle( currentNoodle );
deletedNoodles->append( currentNoodle );
currentNoodle->unref();
}
GeneralizedCylinder *
WorldInfo::undeleteNoodle()
{
int num = deletedNoodles->getLength();
if ( num <= 0)
return NULL;
GeneralizedCylinder *theNoodle
= (GeneralizedCylinder *) (*deletedNoodles)[num - 1];
theNoodle->ref();
addNoodle( theNoodle );
// Since we're undeleting the noodle, make it the only selection.
selector->deselectAll();
selector->select( theNoodle );
setCurrentNoodle( theNoodle );
theNoodle->unrefNoDelete();
deletedNoodles->remove( num - 1);
return theNoodle;
}
GeneralizedCylinder *
WorldInfo::setFirstNoodleCurrent()
{
if (sceneRoot == NULL)
return NULL;
SoSearchAction sa;
sa.setType( GeneralizedCylinder::getClassTypeId() );
sa.apply( sceneRoot );
SoPath *pathToFirst = sa.getPath();
if (pathToFirst == NULL)
return NULL;
GeneralizedCylinder *first = (GeneralizedCylinder *) pathToFirst->getTail();
// Since we're undeleting the noodle, make it the only selection.
selector->deselectAll();
selector->select( first );
setCurrentNoodle( first );
return first;
}
GeneralizedCylinder *
WorldInfo::addNewNoodle()
{
GeneralizedCylinder *newOne = new GeneralizedCylinder;
newOne->ref();
addNoodle( newOne );
// Since we're adding a new noodle, make it the only selection.
selector->deselectAll();
selector->select( newOne );
setCurrentNoodle( newOne );
newOne->unrefNoDelete();
return newOne;
}
void
WorldInfo::setCurrentNoodle( GeneralizedCylinder *newNoodle )
{
currentNoodle = newNoodle;
// Add a manipulator if necessary...
if (currentNoodle)
currentNoodle->changeTransformType( getManipType() );
}
void
WorldInfo::selectionCB(void *userData, SoPath *selectPath)
{
// Return if pickFilter truncated path down to nothing.
if (selectPath->getLength() == 0)
return;
WorldInfo *myself = (WorldInfo *) userData;
SoNodeKitPath *nkPath = (SoNodeKitPath *) selectPath;
GeneralizedCylinder *g = (GeneralizedCylinder *) nkPath->getTail();
myself->setCurrentNoodle( g );
}
void
WorldInfo::deselectionCB(void *, SoPath *deselectPath)
{
if (deselectPath->getLength() == 0)
return;
SoNodeKitPath *nkPath = (SoNodeKitPath *) deselectPath;
GeneralizedCylinder *g = (GeneralizedCylinder *) nkPath->getTail();
g->changeTransformType( SoTransform::getClassTypeId() );
}
SoPath *
WorldInfo::pickFilterCB(void *, const SoPickedPoint *pick )
{
SoFullPath *fullP = (SoFullPath *) pick->getPath();
// Go up the path, and keep popping nodes until the tail is a
// Generalized Cylinder kit...
// If we hit the selection node first, then just stop right there...
while ( fullP->getLength() > 1 ) {
// If we're at a noodle, stop:
if ( fullP->getTail()->isOfType(
GeneralizedCylinder::getClassTypeId()))
return fullP;
// If we're at the selection node, stop.
if ( fullP->getTail()->isOfType( SoSelection::getClassTypeId()))
return fullP;
// Otherwise, pop off the end of the path.
fullP->pop();
}
// If we got here, the path emptied out.
return NULL;
}