diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index d7a166ba5..7c054f8de 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -105,8 +105,215 @@ PartDesign::Body *Workbench::setUpPart(const App::Part *part) return NULL; } +void Workbench::_doMigration(const App::Document* doc) +{ -void switchToDocument(const App::Document* doc) + if(doc->countObjects() != 0) { + // show a warning about the convertion + Gui::Dialog::DlgCheckableMessageBox::showMessage( + QString::fromLatin1("PartDesign conversion warning"), + QString::fromLatin1( + "

Converting PartDesign features to new Body centric schema

" + "If you are unsure what that mean save the document under a new name.
" + "You will not be able to load your work in an older Version of FreeCAD,
" + "After the translation took place...

" + "More information you will find here:
" + " http://www.freecadweb.org/wiki/index.php?title=Assembly_project
" + "Or the Assembly dedicated portion of our forum:
" + " http://forum.freecadweb.org/
" + ), + false, + QString::fromLatin1("Don't tell me again, I know!") + ); + } + + Gui::Command::openCommand("Migrate part to Body feature"); + + // Get the objects now, before adding the Body and the base planes + std::vector features = doc->getObjects(); + + // Assign all non-PartDesign features to a new group + for (std::vector::iterator f = features.begin(); f != features.end(); ) { + if ((*f)->getTypeId().isDerivedFrom(PartDesign::Feature::getClassTypeId()) || + (*f)->getTypeId().isDerivedFrom(Part::Part2DObject::getClassTypeId())) { + ++f; + } else { + if (!groupCreated) { + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().addObject('App::DocumentObjectGroup','NonBodyFeatures')"); + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().ActiveObject.Label = '%s'", + QObject::tr("NonBodyFeatures").toStdString().c_str()); + groupCreated = true; + } + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().NonBodyFeatures.addObject(App.activeDocument().getObject('%s'))", + (*f)->getNameInDocument()); + f = features.erase(f); + } + } + // TODO: Fold the group (is that possible through the Python interface?) + + // Try to find the root(s) of the model tree (the features that depend on no other feature) + // Note: We assume a linear graph, except for MultiTransform features + std::vector roots; + for (std::vector::iterator f = features.begin(); f != features.end(); f++) { + // Note: The dependency list always contains at least the object itself + std::vector ftemp; + ftemp.push_back(*f); + if (doc->getDependencyList(ftemp).size() == 1) + roots.push_back(*f); + } + + // Always create at least the first body, even if the document is empty + // This adds both the base planes and the body + Gui::Command::runCommand(Gui::Command::Doc, "FreeCADGui.runCommand('PartDesign_Body')"); + activeBody = PartDesignGui::ActivePartObject; + + + // Create one Body for every root and put the appropriate features into it + for (std::vector::iterator r = roots.begin(); r != roots.end(); r++) { + if (r != roots.begin()) { + Gui::Command::runCommand(Gui::Command::Doc, "FreeCADGui.runCommand('PartDesign_Body')"); + activeBody = PartDesignGui::ActivePartObject; + } + + std::set inList; + inList.insert(*r); // start with the root feature + std::vector bodyFeatures; + std::string modelString = ""; + do { + for (std::set::const_iterator o = inList.begin(); o != inList.end(); o++) { + std::vector::iterator feat = std::find(features.begin(), features.end(), *o); + if (feat != features.end()) { + bodyFeatures.push_back(*o); + modelString += std::string(modelString.empty() ? "" : ",") + "App.ActiveDocument." + (*o)->getNameInDocument(); + features.erase(feat); + } else { + QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Non-linear tree"), + QObject::tr("Please look at '") + QString::fromAscii((*o)->getNameInDocument()) + + QObject::tr("' and make sure that the migration result is what you would expect.")); + } + } + std::set newInList; + for (std::set::const_iterator o = inList.begin(); o != inList.end(); o++) { + // Omit members of a MultiTransform from the inList, to avoid migration errors + if (PartDesign::Body::isMemberOfMultiTransform(*o)) + continue; + std::vector iL = doc->getInList(*o); + newInList.insert(iL.begin(), iL.end()); + } + inList = newInList; // TODO: Memory leak? Unnecessary copying? + } while (!inList.empty()); + + if (!modelString.empty()) { + modelString = std::string("[") + modelString + "]"; + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Model = %s", activeBody->getNameInDocument(), modelString.c_str()); + // Set the Tip, but not to a member of a MultiTransform! + for (std::vector::const_reverse_iterator f = bodyFeatures.rbegin(); f != bodyFeatures.rend(); f++) { + if (PartDesign::Body::isMemberOfMultiTransform(*f)) + continue; + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Tip = App.activeDocument().%s", + activeBody->getNameInDocument(), (*f)->getNameInDocument()); + break; + } + } + + // Initialize the BaseFeature property of all PartDesign solid features + App::DocumentObject* baseFeature = NULL; + for (std::vector::const_iterator f = bodyFeatures.begin(); f != bodyFeatures.end(); f++) { + if (PartDesign::Body::isSolidFeature(*f)) { + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.BaseFeature = %s", + (*f)->getNameInDocument(), + baseFeature == NULL ? + "None" : + (std::string("App.activeDocument().") + baseFeature->getNameInDocument()).c_str()); + + baseFeature = *f; + } + } + + // Re-route all sketches without support to the base planes + std::vector::const_iterator prevf; + + for (std::vector::const_iterator f = bodyFeatures.begin(); f != bodyFeatures.end(); f++) { + if ((*f)->getTypeId().isDerivedFrom(Sketcher::SketchObject::getClassTypeId())) { + Sketcher::SketchObject* sketch = static_cast(*f); + App::DocumentObject* support = sketch->Support.getValue(); + if (support != NULL) + continue; // Sketch is on a face of a solid + Base::Placement plm = sketch->Placement.getValue(); + Base::Vector3d pnt = plm.getPosition(); + + // Currently we only handle positions that are parallel to the base planes + Base::Rotation rot = plm.getRotation(); + Base::Vector3d SketchVector(0,0,1); + rot.multVec(SketchVector, SketchVector); + std::string side = (SketchVector.x + SketchVector.y + SketchVector.z) < 0.0 ? "back" : "front"; + if (side == "back") SketchVector *= -1.0; + int index; + + if (SketchVector == Base::Vector3d(0,0,1)) + index = 0; + else if (SketchVector == Base::Vector3d(0,1,0)) + index = 1; + else if (SketchVector == Base::Vector3d(1,0,0)) + index = 2; + else { + QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Sketch plane cannot be migrated"), + QObject::tr("Please edit '") + QString::fromAscii(sketch->getNameInDocument()) + + QObject::tr("' and redefine it to use a Base or Datum plane as the sketch plane.")); + continue; + } + + // Find the normal distance from origin to the sketch plane + gp_Pln pln(gp_Pnt (pnt.x, pnt.y, pnt.z), gp_Dir(SketchVector.x, SketchVector.y, SketchVector.z)); + double offset = pln.Distance(gp_Pnt(0,0,0)); + + if (fabs(offset) < Precision::Confusion()) { + // One of the base planes + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Support = (App.activeDocument().%s,['%s'])", + sketch->getNameInDocument(), BaseplaneNames[index], side.c_str()); + } else { + // Offset to base plane + // Find out which direction we need to offset + double a = SketchVector.GetAngle(pnt); + if ((a < -M_PI_2) || (a > M_PI_2)) + offset *= -1.0; + + // Insert a new datum plane before the sketch + App::DocumentObject* oldTip = activeBody->Tip.getValue(); + Gui::Selection().clearSelection(); + if (f != bodyFeatures.begin()) + Gui::Selection().addSelection(doc->getName(), (*prevf)->getNameInDocument()); + Gui::Command::doCommand(Gui::Command::Gui,"FreeCADGui.runCommand('PartDesign_MoveTip')"); + + std::string Datum = doc->getUniqueObjectName("DatumPlane"); + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().addObject('PartDesign::Plane','%s')",Datum.c_str()); + QString refStr = QString::fromAscii("[(App.activeDocument().") + QString::fromAscii(BaseplaneNames[index]) + + QString::fromAscii(",'')]"); + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.References = %s",Datum.c_str(), refStr.toStdString().c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Offset = %f",Datum.c_str(), offset); + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Angle = 0.0",Datum.c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)", + activeBody->getNameInDocument(), Datum.c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Support = (App.activeDocument().%s,['%s'])", + sketch->getNameInDocument(), Datum.c_str(), side.c_str()); + Gui::Command::doCommand(Gui::Command::Gui,"App.activeDocument().recompute()"); // recompute the feature based on its references + + Gui::Selection().clearSelection(); + if (oldTip != NULL) { + Gui::Selection().addSelection(doc->getName(), oldTip->getNameInDocument()); + Gui::Command::doCommand(Gui::Command::Gui,"FreeCADGui.runCommand('PartDesign_MoveTip')"); + Gui::Selection().clearSelection(); + } + } + } + + prevf = f; + } + } + +} + +void Workbench::_switchToDocument(const App::Document* doc) { bool groupCreated = false; @@ -117,211 +324,12 @@ void switchToDocument(const App::Document* doc) std::vector bodies = doc->getObjectsOfType(PartDesign::Body::getClassTypeId()); // Is there a body feature in this document? - if (bodies.empty()) { - - if(doc->countObjects() != 0) { - // show a warning about the convertion - Gui::Dialog::DlgCheckableMessageBox::showMessage( - QString::fromLatin1("PartDesign conversion warning"), - QString::fromLatin1( - "

Converting PartDesign features to new Body centric schema

" - "If you are unsure what that mean save the document under a new name.
" - "You will not be able to load your work in an older Version of FreeCAD,
" - "After the translation took place...

" - "More information you will find here:
" - " http://www.freecadweb.org/wiki/index.php?title=Assembly_project
" - "Or the Assembly dedicated portion of our forum:
" - " http://forum.freecadweb.org/
" - ), - false, - QString::fromLatin1("Don't tell me again, I know!") - ); - } - - Gui::Command::openCommand("Migrate part to Body feature"); - - // Get the objects now, before adding the Body and the base planes - std::vector features = doc->getObjects(); - - // Assign all non-PartDesign features to a new group - for (std::vector::iterator f = features.begin(); f != features.end(); ) { - if ((*f)->getTypeId().isDerivedFrom(PartDesign::Feature::getClassTypeId()) || - (*f)->getTypeId().isDerivedFrom(Part::Part2DObject::getClassTypeId())) { - ++f; - } else { - if (!groupCreated) { - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().addObject('App::DocumentObjectGroup','NonBodyFeatures')"); - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().ActiveObject.Label = '%s'", - QObject::tr("NonBodyFeatures").toStdString().c_str()); - groupCreated = true; - } - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().NonBodyFeatures.addObject(App.activeDocument().getObject('%s'))", - (*f)->getNameInDocument()); - f = features.erase(f); - } - } - // TODO: Fold the group (is that possible through the Python interface?) - - // Try to find the root(s) of the model tree (the features that depend on no other feature) - // Note: We assume a linear graph, except for MultiTransform features - std::vector roots; - for (std::vector::iterator f = features.begin(); f != features.end(); f++) { - // Note: The dependency list always contains at least the object itself - std::vector ftemp; - ftemp.push_back(*f); - if (doc->getDependencyList(ftemp).size() == 1) - roots.push_back(*f); - } - - // Always create at least the first body, even if the document is empty - // This adds both the base planes and the body - Gui::Command::runCommand(Gui::Command::Doc, "FreeCADGui.runCommand('PartDesign_Body')"); - activeBody = PartDesignGui::ActivePartObject; - - - // Create one Body for every root and put the appropriate features into it - for (std::vector::iterator r = roots.begin(); r != roots.end(); r++) { - if (r != roots.begin()) { - Gui::Command::runCommand(Gui::Command::Doc, "FreeCADGui.runCommand('PartDesign_Body')"); - activeBody = PartDesignGui::ActivePartObject; - } - - std::set inList; - inList.insert(*r); // start with the root feature - std::vector bodyFeatures; - std::string modelString = ""; - do { - for (std::set::const_iterator o = inList.begin(); o != inList.end(); o++) { - std::vector::iterator feat = std::find(features.begin(), features.end(), *o); - if (feat != features.end()) { - bodyFeatures.push_back(*o); - modelString += std::string(modelString.empty() ? "" : ",") + "App.ActiveDocument." + (*o)->getNameInDocument(); - features.erase(feat); - } else { - QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Non-linear tree"), - QObject::tr("Please look at '") + QString::fromAscii((*o)->getNameInDocument()) + - QObject::tr("' and make sure that the migration result is what you would expect.")); - } - } - std::set newInList; - for (std::set::const_iterator o = inList.begin(); o != inList.end(); o++) { - // Omit members of a MultiTransform from the inList, to avoid migration errors - if (PartDesign::Body::isMemberOfMultiTransform(*o)) - continue; - std::vector iL = doc->getInList(*o); - newInList.insert(iL.begin(), iL.end()); - } - inList = newInList; // TODO: Memory leak? Unnecessary copying? - } while (!inList.empty()); - - if (!modelString.empty()) { - modelString = std::string("[") + modelString + "]"; - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Model = %s", activeBody->getNameInDocument(), modelString.c_str()); - // Set the Tip, but not to a member of a MultiTransform! - for (std::vector::const_reverse_iterator f = bodyFeatures.rbegin(); f != bodyFeatures.rend(); f++) { - if (PartDesign::Body::isMemberOfMultiTransform(*f)) - continue; - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Tip = App.activeDocument().%s", - activeBody->getNameInDocument(), (*f)->getNameInDocument()); - break; - } - } - - // Initialize the BaseFeature property of all PartDesign solid features - App::DocumentObject* baseFeature = NULL; - for (std::vector::const_iterator f = bodyFeatures.begin(); f != bodyFeatures.end(); f++) { - if (PartDesign::Body::isSolidFeature(*f)) { - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.BaseFeature = %s", - (*f)->getNameInDocument(), - baseFeature == NULL ? - "None" : - (std::string("App.activeDocument().") + baseFeature->getNameInDocument()).c_str()); - - baseFeature = *f; - } - } - - // Re-route all sketches without support to the base planes - std::vector::const_iterator prevf; - - for (std::vector::const_iterator f = bodyFeatures.begin(); f != bodyFeatures.end(); f++) { - if ((*f)->getTypeId().isDerivedFrom(Sketcher::SketchObject::getClassTypeId())) { - Sketcher::SketchObject* sketch = static_cast(*f); - App::DocumentObject* support = sketch->Support.getValue(); - if (support != NULL) - continue; // Sketch is on a face of a solid - Base::Placement plm = sketch->Placement.getValue(); - Base::Vector3d pnt = plm.getPosition(); - - // Currently we only handle positions that are parallel to the base planes - Base::Rotation rot = plm.getRotation(); - Base::Vector3d SketchVector(0,0,1); - rot.multVec(SketchVector, SketchVector); - std::string side = (SketchVector.x + SketchVector.y + SketchVector.z) < 0.0 ? "back" : "front"; - if (side == "back") SketchVector *= -1.0; - int index; - - if (SketchVector == Base::Vector3d(0,0,1)) - index = 0; - else if (SketchVector == Base::Vector3d(0,1,0)) - index = 1; - else if (SketchVector == Base::Vector3d(1,0,0)) - index = 2; - else { - QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Sketch plane cannot be migrated"), - QObject::tr("Please edit '") + QString::fromAscii(sketch->getNameInDocument()) + - QObject::tr("' and redefine it to use a Base or Datum plane as the sketch plane.")); - continue; - } - - // Find the normal distance from origin to the sketch plane - gp_Pln pln(gp_Pnt (pnt.x, pnt.y, pnt.z), gp_Dir(SketchVector.x, SketchVector.y, SketchVector.z)); - double offset = pln.Distance(gp_Pnt(0,0,0)); - - if (fabs(offset) < Precision::Confusion()) { - // One of the base planes - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Support = (App.activeDocument().%s,['%s'])", - sketch->getNameInDocument(), BaseplaneNames[index], side.c_str()); - } else { - // Offset to base plane - // Find out which direction we need to offset - double a = SketchVector.GetAngle(pnt); - if ((a < -M_PI_2) || (a > M_PI_2)) - offset *= -1.0; - - // Insert a new datum plane before the sketch - App::DocumentObject* oldTip = activeBody->Tip.getValue(); - Gui::Selection().clearSelection(); - if (f != bodyFeatures.begin()) - Gui::Selection().addSelection(doc->getName(), (*prevf)->getNameInDocument()); - Gui::Command::doCommand(Gui::Command::Gui,"FreeCADGui.runCommand('PartDesign_MoveTip')"); - - std::string Datum = doc->getUniqueObjectName("DatumPlane"); - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().addObject('PartDesign::Plane','%s')",Datum.c_str()); - QString refStr = QString::fromAscii("[(App.activeDocument().") + QString::fromAscii(BaseplaneNames[index]) + - QString::fromAscii(",'')]"); - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.References = %s",Datum.c_str(), refStr.toStdString().c_str()); - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Offset = %f",Datum.c_str(), offset); - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Angle = 0.0",Datum.c_str()); - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.addFeature(App.activeDocument().%s)", - activeBody->getNameInDocument(), Datum.c_str()); - Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.Support = (App.activeDocument().%s,['%s'])", - sketch->getNameInDocument(), Datum.c_str(), side.c_str()); - Gui::Command::doCommand(Gui::Command::Gui,"App.activeDocument().recompute()"); // recompute the feature based on its references - - Gui::Selection().clearSelection(); - if (oldTip != NULL) { - Gui::Selection().addSelection(doc->getName(), oldTip->getNameInDocument()); - Gui::Command::doCommand(Gui::Command::Gui,"FreeCADGui.runCommand('PartDesign_MoveTip')"); - Gui::Selection().clearSelection(); - } - } - } - - prevf = f; - } - } - } else { + if (bodies.empty()) + { + _doMigration(doc); + } + else + { // Find active body for (std::vector::const_iterator b = bodies.begin(); b != bodies.end(); b++) { PartDesign::Body* body = static_cast(*b); diff --git a/src/Mod/PartDesign/Gui/Workbench.h b/src/Mod/PartDesign/Gui/Workbench.h index 3e9cd2853..0de375b0d 100644 --- a/src/Mod/PartDesign/Gui/Workbench.h +++ b/src/Mod/PartDesign/Gui/Workbench.h @@ -100,6 +100,10 @@ private: void slotDeleteDocument(const App::Document&); // Add new objects to the body, if appropriate //void slotNewObject(const App::DocumentObject& obj); + + void _doMigration(const App::Document* doc); + void _switchToDocument(const App::Document* doc); + }; } // namespace PartDesignGui