/* * * 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 #include #include #include #include #include #include #include #include #include #include #include #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; }