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")