Part::Feature *ProfileBased::getBaseObject(bool silent) const { // Test the base's class feature. Part::Feature *rv = Feature::getBaseObject(/* silent = */ true); if (rv) { return rv; } // getVerifiedObject() may throw it's own exception if fail Part::Feature* obj = getVerifiedObject(silent); if(!obj) return nullptr; if (!obj->isDerivedFrom(Part::Part2DObject::getClassTypeId())) return obj; //due to former test we know we have a 2d object Part::Part2DObject* sketch = getVerifiedSketch(silent); const char* err = nullptr; App::DocumentObject* spt = sketch->Support.getValue(); if (spt) { if (spt->isDerivedFrom(Part::Feature::getClassTypeId())) { rv = static_cast<Part::Feature*>(spt); } else { err = "No base set, sketch support is not Part::Feature"; } } else { err = "No base set, no sketch support either"; } if (!silent && err) { throw Base::RuntimeError (err); } return rv; }
void CmdPartDesignMigrate::activated(int iMsg) { Q_UNUSED(iMsg); App::Document *doc = getDocument(); std::set<PartDesign::Feature*> migrateFeatures; // Retrive all PartDesign Features objects and filter out features already belongs to some body for ( const auto & feat: doc->getObjects( ) ) { if( feat->isDerivedFrom( PartDesign::Feature::getClassTypeId() ) && !PartDesign::Body::findBodyOf( feat ) && PartDesign::Body::isSolidFeature ( feat ) ) { migrateFeatures.insert ( static_cast <PartDesign::Feature *>( feat ) ); } } if ( migrateFeatures.empty() ) { if ( !PartDesignGui::isModernWorkflow ( doc ) ) { // If there is nothing to migrate and workflow is still old just set it to modern PartDesignGui::WorkflowManager::instance()->forceWorkflow ( doc, PartDesignGui::Workflow::Modern ); } else { // Huh? nothing to migrate? QMessageBox::warning ( 0, QObject::tr ( "Nothing to migrate" ), QObject::tr ( "No PartDesign features which doesn't belong to a body found." " Nothing to migrate." ) ); } return; } // Note: this action is undoable, should it be? PartDesignGui::WorkflowManager::instance()->forceWorkflow ( doc, PartDesignGui::Workflow::Modern ); // Put features into chains. Each chain should become a separate body. std::list< std::list<PartDesign::Feature *> > featureChains; std::list<PartDesign::Feature *> chain; //< the current chain we are working on for (auto featIt = migrateFeatures.begin(); !migrateFeatures.empty(); ) { Part::Feature *base = (*featIt)->getBaseObject( /*silent =*/ true ); chain.push_front ( *featIt ); if ( !base || !base->isDerivedFrom (PartDesign::Feature::getClassTypeId () ) || PartDesignGui::isAnyNonPartDesignLinksTo ( static_cast <PartDesign::Feature *>(base), /*respectGroups=*/ true ) ) { // a feature based on nothing as well as on non-partdesign solid starts a new chain auto newChainIt = featureChains.emplace (featureChains.end()); newChainIt->splice (newChainIt->end(), chain); } else { // we are basing on some partdesign feature which supposed to belong to some body PartDesign::Feature *baseFeat = static_cast <PartDesign::Feature *>( base ); auto baseFeatSetIt = find ( migrateFeatures.begin (), migrateFeatures.end (), baseFeat ); if ( baseFeatSetIt != migrateFeatures.end() ) { // base feature is pending for migration, switch to it and continue over migrateFeatures.erase(featIt); featIt = baseFeatSetIt; continue; } else { // The base feature seems already assigned to some chain // Find which std::list<PartDesign::Feature *>::iterator baseFeatIt; auto chainIt = std::find_if( featureChains.begin(), featureChains.end(), [baseFeat, &baseFeatIt] ( std::list<PartDesign::Feature *>&chain ) mutable -> bool { baseFeatIt = std::find( chain.begin(), chain.end(), baseFeat ); return baseFeatIt != chain.end(); } ); if ( chainIt != featureChains.end() ) { assert (baseFeatIt != chainIt->end()); if ( std::next ( baseFeatIt ) == chainIt->end() ) { // just append our chain to already found chainIt->splice ( chainIt->end(), chain ); // TODO If we will hit a third part everything will be messed up again. // Probably it will require a yet another smart-ass find_if. (2015-08-10, Fat-Zer) } else { // We have a fork of a partDesign feature here // add a chain for current body auto newChainIt = featureChains.emplace (featureChains.end()); newChainIt->splice (newChainIt->end(), chain); // add a chain for forked one newChainIt = featureChains.emplace (featureChains.end()); newChainIt->splice (newChainIt->end(), *chainIt, std::next ( baseFeatIt ), chainIt->end()); } } else { // The feature is not present in list pending for migration, // This generally shouldn't happen but may be if we run into some broken file // Try to find out the body we should insert into // TODO Some error/warning is needed here (2015-08-10, Fat-Zer) auto newChainIt = featureChains.emplace (featureChains.end()); newChainIt->splice (newChainIt->end(), chain); } } } migrateFeatures.erase ( featIt ); featIt = migrateFeatures.begin (); // TODO Align visibility (2015-08-17, Fat-Zer) } /* for */ // TODO make it work without parts (2015-09-04, Fat-Zer) // add a part if there is no active yet App::Part *actPart = PartDesignGui::assertActivePart (); if (!actPart) { return; } // do the actual migration Gui::Command::openCommand("Migrate legacy part design features to Bodies"); for ( auto chainIt = featureChains.begin(); !featureChains.empty(); featureChains.erase (chainIt), chainIt = featureChains.begin () ) { #ifndef FC_DEBUG if ( chainIt->empty () ) { // prevent crash in release in case of errors continue; } #else assert ( !chainIt->empty () ); #endif Part::Feature *base = chainIt->front()->getBaseObject ( /*silent =*/ true ); // Find a suitable chain to work with for( ; chainIt != featureChains.end(); chainIt ++) { base = chainIt->front()->getBaseObject ( /*silent =*/ true ); if (!base || !base->isDerivedFrom ( PartDesign::Feature::getClassTypeId () ) ) { break; // no base is ok } else { // The base feature is a PartDesign, it's a fork, try to reassign it to a body... base = PartDesign::Body::findBodyOf ( base ); if ( base ) { break; } } } if ( chainIt == featureChains.end() ) { // Shouldn't happen, may be only in case of some circular dependency? // TODO Some error message (2015-08-11, Fat-Zer) chainIt = featureChains.begin(); base = chainIt->front()->getBaseObject ( /*silent =*/ true ); } // Construct a Pretty Body name based on the Tip std::string bodyName = getUniqueObjectName ( std::string ( chainIt->back()->getNameInDocument() ).append ( "Body" ).c_str () ) ; // Create a body for the chain doCommand ( Doc,"App.activeDocument().addObject('PartDesign::Body','%s')", bodyName.c_str () ); doCommand ( Doc,"App.activeDocument().%s.addObject(App.ActiveDocument.%s)", actPart->getNameInDocument (), bodyName.c_str () ); if (base) { doCommand ( Doc,"App.activeDocument().%s.BaseFeature = App.activeDocument().%s", bodyName.c_str (), base->getNameInDocument () ); } // Fill the body with features for ( auto feature: *chainIt ) { if ( feature->isDerivedFrom ( PartDesign::ProfileBased::getClassTypeId() ) ) { // add the sketch and also reroute it if needed PartDesign::ProfileBased *sketchBased = static_cast<PartDesign::ProfileBased *> ( feature ); Part::Part2DObject *sketch = sketchBased->getVerifiedSketch( /*silent =*/ true); if ( sketch ) { doCommand ( Doc,"App.activeDocument().%s.addObject(App.activeDocument().%s)", bodyName.c_str (), sketch->getNameInDocument() ); if ( sketch->isDerivedFrom ( Sketcher::SketchObject::getClassTypeId() ) ) { try { PartDesignGui::fixSketchSupport ( static_cast<Sketcher::SketchObject *> ( sketch ) ); } catch (Base::Exception &) { QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Sketch plane cannot be migrated"), QObject::tr("Please edit '%1' and redefine it to use a Base or " "Datum plane as the sketch plane."). arg(QString::fromUtf8(sketch->Label.getValue()) ) ); } } else { // TODO Message that sketchbased is based not on a sketch (2015-08-11, Fat-Zer) } } } doCommand ( Doc,"App.activeDocument().%s.addObject(App.activeDocument().%s)", bodyName.c_str (), feature->getNameInDocument() ); PartDesignGui::relinkToBody ( feature ); } } updateActive(); }