diff --git a/src/Mod/Part/App/Part2DObject.cpp b/src/Mod/Part/App/Part2DObject.cpp index 159b5a8cf..46f063541 100644 --- a/src/Mod/Part/App/Part2DObject.cpp +++ b/src/Mod/Part/App/Part2DObject.cpp @@ -45,6 +45,7 @@ #include +#include #include "Part2DObject.h" #include "Geometry.h" @@ -74,42 +75,57 @@ App::DocumentObjectExecReturn *Part2DObject::execute(void) void Part2DObject::positionBySupport(void) { // recalculate support: - Part::Feature *part = static_cast(Support.getValue()); - if (!part || !part->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) - return; - - Base::Placement Place = part->Placement.getValue(); - const std::vector &sub = Support.getSubValues(); - assert(sub.size()==1); - // get the selected sub shape (a Face) - const Part::TopoShape &shape = part->Shape.getShape(); - if (shape._Shape.IsNull()) - throw Base::Exception("Support shape is empty!"); + Base::Placement Place; TopoDS_Shape sh; - try { - sh = shape.getSubShape(sub[0].c_str()); - } - catch (Standard_Failure) { - throw Base::Exception("Face in support shape doesn't exist!"); - } - const TopoDS_Face &face = TopoDS::Face(sh); - if (face.IsNull()) - throw Base::Exception("Null face in Part2DObject::positionBySupport()!"); - - BRepAdaptor_Surface adapt(face); - if (adapt.GetType() != GeomAbs_Plane) - throw Base::Exception("No planar face in Part2DObject::positionBySupport()!"); - bool Reverse = false; - if (face.Orientation() == TopAbs_REVERSED) - Reverse = true; + gp_Pln plane; + App::DocumentObject* support = Support.getValue(); - gp_Pln plane = adapt.Plane(); - Standard_Boolean ok = plane.Direct(); - if (!ok) { - // toggle if plane has a left-handed coordinate system - plane.UReverse(); - Reverse = !Reverse; + if (support->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { + Place = static_cast(support)->Placement.getValue(); + // TODO: How to handle the Reverse property??? + Base::Vector3d pos = Place.getPosition(); + Base::Vector3d dir; + Place.getRotation().multVec(Base::Vector3d(0,0,1),dir); + plane = gp_Pln(gp_Pnt(pos.x, pos.y, pos.z), gp_Dir(dir.x, dir.y, dir.z)); + } else { + Part::Feature *part = static_cast(support); + if (!part || !part->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + return; + + Place = part->Placement.getValue(); + const std::vector &sub = Support.getSubValues(); + assert(sub.size()==1); + // get the selected sub shape (a Face) + const Part::TopoShape &shape = part->Shape.getShape(); + if (shape._Shape.IsNull()) + throw Base::Exception("Support shape is empty!"); + + try { + sh = shape.getSubShape(sub[0].c_str()); + } + catch (Standard_Failure) { + throw Base::Exception("Face in support shape doesn't exist!"); + } + + const TopoDS_Face &face = TopoDS::Face(sh); + if (face.IsNull()) + throw Base::Exception("Null face in Part2DObject::positionBySupport()!"); + + BRepAdaptor_Surface adapt(face); + if (adapt.GetType() != GeomAbs_Plane) + throw Base::Exception("No planar face in Part2DObject::positionBySupport()!"); + + if (face.Orientation() == TopAbs_REVERSED) + Reverse = true; + + plane = adapt.Plane(); + Standard_Boolean ok = plane.Direct(); + if (!ok) { + // toggle if plane has a left-handed coordinate system + plane.UReverse(); + Reverse = !Reverse; + } } gp_Ax1 Normal = plane.Axis(); diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 2f5ca4e1e..6f46c38ea 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -82,6 +82,8 @@ PartDesign::Body *getBody(void) } +const char* BasePlaneNames[3] = {"Body_PlaneXY", "Body_PlaneYZ", "Body_PlaneXZ"}; + //=========================================================================== // PartDesign_Body //=========================================================================== @@ -110,34 +112,35 @@ void CmdPartDesignBody::activated(int iMsg) bool found = false; std::vector planes = getDocument()->getObjectsOfType(App::Plane::getClassTypeId()); for (std::vector::const_iterator p = planes.begin(); p != planes.end(); p++) { - if ((strcmp("Body_PlaneXY", (*p)->getNameInDocument()) == 0) || - (strcmp("Body_PlaneYZ", (*p)->getNameInDocument()) == 0) || - (strcmp("Body_PlaneXZ", (*p)->getNameInDocument()) == 0)) { - found = true; - break; + for (unsigned i = 0; i < 3; i++) { + if (strcmp(BasePlaneNames[i], (*p)->getNameInDocument()) == 0) { + found = true; + break; + } } + if (found) break; } if (!found) { // Add the planes ... - doCommand(Doc,"App.activeDocument().addObject('App::Plane','Body_PlaneXY')"); + doCommand(Doc,"App.activeDocument().addObject('App::Plane','%s')", BasePlaneNames[0]); doCommand(Doc,"App.activeDocument().ActiveObject.Label = 'XY-Plane'"); - doCommand(Doc,"App.activeDocument().addObject('App::Plane','Body_PlaneYZ')"); + doCommand(Doc,"App.activeDocument().addObject('App::Plane','%s')", BasePlaneNames[1]); doCommand(Doc,"App.activeDocument().ActiveObject.Placement = App.Placement(App.Vector(),App.Rotation(App.Vector(0,1,0),90))"); doCommand(Doc,"App.activeDocument().ActiveObject.Label = 'YZ-Plane'"); - doCommand(Doc,"App.activeDocument().addObject('App::Plane','Body_PlaneXZ')"); + doCommand(Doc,"App.activeDocument().addObject('App::Plane','%s')", BasePlaneNames[2]); doCommand(Doc,"App.activeDocument().ActiveObject.Placement = App.Placement(App.Vector(),App.Rotation(App.Vector(1,0,0),90))"); doCommand(Doc,"App.activeDocument().ActiveObject.Label = 'XZ-Plane'"); // ... and put them in the 'Origin' group doCommand(Doc,"App.activeDocument().addObject('App::DocumentObjectGroup','Origin')"); - doCommand(Doc,"App.activeDocument().Origin.addObject(App.activeDocument().getObject('Body_PlaneXY'))"); - doCommand(Doc,"App.activeDocument().Origin.addObject(App.activeDocument().getObject('Body_PlaneYZ'))"); - doCommand(Doc,"App.activeDocument().Origin.addObject(App.activeDocument().getObject('Body_PlaneXZ'))"); + for (unsigned i = 0; i < 3; i++) + doCommand(Doc,"App.activeDocument().Origin.addObject(App.activeDocument().getObject('%s'))", BasePlaneNames[i]); // TODO: Fold the group (is that possible through the Python interface?) } // add the Body feature itself, and make it active doCommand(Doc,"App.activeDocument().addObject('PartDesign::Body','%s')",FeatName.c_str()); + doCommand(Doc,"import PartDesignGui"); doCommand(Gui,"PartDesignGui.setActivePart(App.ActiveDocument.ActiveObject)"); // Make the "Create sketch" prompt appear in the task panel doCommand(Gui,"Gui.Selection.addSelection(App.ActiveDocument.ActiveObject)"); @@ -179,42 +182,48 @@ void CmdPartDesignNewSketch::activated(int iMsg) Gui::SelectionFilter SketchFilter("SELECT Sketcher::SketchObject COUNT 1"); Gui::SelectionFilter FaceFilter ("SELECT Part::Feature SUBELEMENT Face COUNT 1"); + Gui::SelectionFilter PlaneFilter ("SELECT App::Plane COUNT 1"); if (SketchFilter.match()) { Sketcher::SketchObject *Sketch = static_cast(SketchFilter.Result[0][0].getObject()); openCommand("Edit Sketch"); doCommand(Gui,"Gui.activeDocument().setEdit('%s')",Sketch->getNameInDocument()); } - else if (FaceFilter.match()) { + else if (FaceFilter.match() || PlaneFilter.match()) { // get the selected object - Part::Feature *part = static_cast(FaceFilter.Result[0][0].getObject()); - Base::Placement ObjectPos = part->Placement.getValue(); - const std::vector &sub = FaceFilter.Result[0][0].getSubNames(); - if (sub.size() > 1){ - // No assert for wrong user input! - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Several sub-elements selected"), - QObject::tr("You have to select a single face as support for a sketch!")); - return; - } - // get the selected sub shape (a Face) - const Part::TopoShape &shape = part->Shape.getValue(); - TopoDS_Shape sh = shape.getSubShape(sub[0].c_str()); - const TopoDS_Face& face = TopoDS::Face(sh); - if (face.IsNull()){ - // No assert for wrong user input! - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No support face selected"), - QObject::tr("You have to select a face as support for a sketch!")); - return; - } + std::string supportString; - BRepAdaptor_Surface adapt(face); - if (adapt.GetType() != GeomAbs_Plane){ - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No planar support"), - QObject::tr("You need a planar face as support for a sketch!")); - return; - } + if (FaceFilter.match()) { + Part::Feature *part = static_cast(FaceFilter.Result[0][0].getObject()); + const std::vector &sub = FaceFilter.Result[0][0].getSubNames(); + if (sub.size() > 1){ + // No assert for wrong user input! + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Several sub-elements selected"), + QObject::tr("You have to select a single face as support for a sketch!")); + return; + } + // get the selected sub shape (a Face) + const Part::TopoShape &shape = part->Shape.getValue(); + TopoDS_Shape sh = shape.getSubShape(sub[0].c_str()); + const TopoDS_Face& face = TopoDS::Face(sh); + if (face.IsNull()){ + // No assert for wrong user input! + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No support face selected"), + QObject::tr("You have to select a face as support for a sketch!")); + return; + } - std::string supportString = FaceFilter.Result[0][0].getAsPropertyLinkSubString(); + BRepAdaptor_Surface adapt(face); + if (adapt.GetType() != GeomAbs_Plane){ + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No planar support"), + QObject::tr("You need a planar face as support for a sketch!")); + return; + } + + supportString = FaceFilter.Result[0][0].getAsPropertyLinkSubString(); + } else { + supportString = PlaneFilter.Result[0][0].getAsPropertyLinkSubString(); + } // create Sketch on Face std::string FeatName = getUniqueObjectName("Sketch"); @@ -229,47 +238,71 @@ void CmdPartDesignNewSketch::activated(int iMsg) doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); } else { - // ask user for orientation - SketcherGui::SketchOrientationDialog Dlg; + // Get a valid plane from the user + std::vector status; + std::vector planes = getDocument()->getObjectsOfType(App::Plane::getClassTypeId()); - if (Dlg.exec() != QDialog::Accepted) - return; // canceled - Base::Vector3d p = Dlg.Pos.getPosition(); - Base::Rotation r = Dlg.Pos.getRotation(); + unsigned validPlanes = 0; + std::vector::const_iterator firstValidPlane = planes.end(); - // do the right view direction - std::string camstring; - switch(Dlg.DirType){ - case 0: - camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n position 0 0 87 \\n orientation 0 0 1 0 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }"; - break; - case 1: - camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n position 0 0 -87 \\n orientation -1 0 0 3.1415927 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }"; - break; - case 2: - camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position 0 -87 0 \\n orientation -1 0 0 4.712389\\n nearDistance -112.88701\\n farDistance 287.28702\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; - break; - case 3: - camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position 0 87 0 \\n orientation 0 0.70710683 0.70710683 3.1415927\\n nearDistance -112.88701\\n farDistance 287.28702\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; - break; - case 4: - camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position 87 0 0 \\n orientation 0.57735026 0.57735026 0.57735026 2.0943952 \\n nearDistance -112.887\\n farDistance 287.28699\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; - break; - case 5: - camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position -87 0 0 \\n orientation -0.57735026 0.57735026 0.57735026 4.1887903 \\n nearDistance -112.887\\n farDistance 287.28699\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; - break; + for (std::vector::iterator p = planes.begin(); p != planes.end(); p++) { + // Check whether this plane is a base plane + bool base = false; + for (unsigned i = 0; i < 3; i++) { + if (strcmp(BasePlaneNames[i], (*p)->getNameInDocument()) == 0) { + status.push_back(PartDesignGui::FeaturePickDialog::basePlane); + if (firstValidPlane == planes.end()) + firstValidPlane = p; + validPlanes++; + base = true; + break; + } + } + if (base) continue; + + // Check whether this plane belongs to the active body + PartDesign::Body* body = getBody(); + if (!body->hasFeature(*p)) { + status.push_back(PartDesignGui::FeaturePickDialog::otherBody); + continue; + } + + // All checks passed - found a valid plane + if (firstValidPlane == planes.end()) + firstValidPlane = p; + validPlanes++; + status.push_back(PartDesignGui::FeaturePickDialog::validFeature); } + + if (validPlanes == 0) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid planes in this document"), + QObject::tr("Please create a plane first or select a face to sketch on")); + return; + } + + // If there is more than one possibility, show dialog and let user pick plane + if (validPlanes > 1) { + PartDesignGui::FeaturePickDialog Dlg(planes, status); + if ((Dlg.exec() != QDialog::Accepted) || (planes = Dlg.getFeatures()).empty()) + return; // Cancelled or nothing selected + firstValidPlane = planes.begin(); + } + + App::Plane* plane = static_cast(*firstValidPlane); + Base::Vector3d p = plane->Placement.getValue().getPosition(); + Base::Rotation r = plane->Placement.getValue().getRotation(); + std::string FeatName = getUniqueObjectName("Sketch"); + std::string supportString = std::string("(App.activeDocument().") + plane->getNameInDocument() + ", [])"; openCommand("Create a new Sketch"); doCommand(Doc,"App.activeDocument().addObject('Sketcher::SketchObject','%s')",FeatName.c_str()); + doCommand(Gui,"App.activeDocument().%s.Support = %s",FeatName.c_str(),supportString.c_str()); doCommand(Doc,"App.activeDocument().%s.Placement = App.Placement(App.Vector(%f,%f,%f),App.Rotation(%f,%f,%f,%f))",FeatName.c_str(),p.x,p.y,p.z,r[0],r[1],r[2],r[3]); doCommand(Doc,"App.activeDocument().%s.Model = App.activeDocument().%s.Model + [App.activeDocument().%s]",pcActiveBody->getNameInDocument(),pcActiveBody->getNameInDocument(),FeatName.c_str()); doCommand(Doc,"App.activeDocument().%s.Tip = App.activeDocument().%s",pcActiveBody->getNameInDocument(),FeatName.c_str()); - doCommand(Gui,"Gui.activeDocument().activeView().setCamera('%s')",camstring.c_str()); doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); } - } bool CmdPartDesignNewSketch::isActive(void) @@ -392,7 +425,7 @@ void prepareSketchBased(Gui::Command* cmd, const std::string& which, } void finishSketchBased(const Gui::Command* cmd, - const Part::Part2DObject* sketch, const std::string& FeatName, App::DocumentObject*& prevTip) + const Part::Part2DObject* sketch, const std::string& FeatName, const App::DocumentObject* prevTip) { App::DocumentObjectGroup* grp = sketch->getGroup(); if (grp) { diff --git a/src/Mod/PartDesign/Gui/FeaturePickDialog.cpp b/src/Mod/PartDesign/Gui/FeaturePickDialog.cpp index c46b6662d..a2003b666 100644 --- a/src/Mod/PartDesign/Gui/FeaturePickDialog.cpp +++ b/src/Mod/PartDesign/Gui/FeaturePickDialog.cpp @@ -47,6 +47,7 @@ const QString FeaturePickDialog::getFeatureStatusString(const featureStatus st) case noWire: return tr("No wire in sketch"); case isUsed: return tr("Sketch already used by other feature"); case otherBody: return tr("Sketch belongs to another Body feature"); + case basePlane: return tr("Base plane"); } return tr(""); @@ -104,6 +105,7 @@ void FeaturePickDialog::updateList() case noWire: item->setFlags(Qt::NoItemFlags); break; case isUsed: item->setFlags(ui->checkOtherFeature->isChecked() ? Qt::ItemIsSelectable | Qt::ItemIsEnabled : Qt::NoItemFlags); break; case otherBody: item->setFlags(ui->checkOtherBody->isChecked() ? Qt::ItemIsSelectable | Qt::ItemIsEnabled : Qt::NoItemFlags); break; + case basePlane: item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); break; } index++; diff --git a/src/Mod/PartDesign/Gui/FeaturePickDialog.h b/src/Mod/PartDesign/Gui/FeaturePickDialog.h index 72a96458e..e70c04229 100644 --- a/src/Mod/PartDesign/Gui/FeaturePickDialog.h +++ b/src/Mod/PartDesign/Gui/FeaturePickDialog.h @@ -40,7 +40,8 @@ public: invalidShape, noWire, isUsed, - otherBody + otherBody, + basePlane }; FeaturePickDialog(std::vector &objects, const std::vector &status); diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 4158d905a..088cb1a0c 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -102,6 +102,16 @@ void Workbench::activated() "Part_Box" )); + const char* Plane[] = { + "PartDesign_NewSketch", + 0}; + Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( + "SELECT App::Plane COUNT 1", + Plane, + "Start Part", + "Part_Box" + )); + const char* NoSel[] = { "PartDesign_Body", 0}; diff --git a/src/Mod/Start/StartPage/PartDesign.py b/src/Mod/Start/StartPage/PartDesign.py index 3931e0257..bf59c27db 100644 --- a/src/Mod/Start/StartPage/PartDesign.py +++ b/src/Mod/Start/StartPage/PartDesign.py @@ -25,3 +25,6 @@ import FreeCADGui FreeCADGui.activateWorkbench("PartDesignWorkbench") App.newDocument() FreeCADGui.runCommand('PartDesign_Body') +# Make the planes properly visible +FreeCADGui.activeDocument().activeView().viewAxometric() +FreeCADGui.SendMsgToActiveView("ViewFit")