diff --git a/data/tests/PadTest.fcstd b/data/tests/PadTest.fcstd new file mode 100644 index 000000000..8a7223083 Binary files /dev/null and b/data/tests/PadTest.fcstd differ diff --git a/data/tests/PocketTest.fcstd b/data/tests/PocketTest.fcstd new file mode 100644 index 000000000..6acf91ba0 Binary files /dev/null and b/data/tests/PocketTest.fcstd differ diff --git a/src/App/PropertyStandard.h b/src/App/PropertyStandard.h index bff973cb2..fc2989379 100644 --- a/src/App/PropertyStandard.h +++ b/src/App/PropertyStandard.h @@ -394,9 +394,6 @@ protected: const Constraints* _ConstStruct; }; - - - class AppExport PropertyFloatList: public PropertyLists { TYPESYSTEM_HEADER(); @@ -470,10 +467,11 @@ public: */ virtual ~PropertyString(); - void setValue(const char* sString); void setValue(const std::string &sString); const char* getValue(void) const; + const std::string& getStrValue(void) const + { return _cValue; } bool isEmpty(void){return _cValue.empty();} virtual const char* getEditorName(void) const { return "Gui::PropertyEditor::PropertyStringItem"; } diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index ceb773473..91b71eba8 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -85,6 +85,7 @@ UnitsSchema *UnitsApi::UserPrefSystem = new UnitsSchemaInternal(); double UnitsApi::UserPrefFactor [50]; QString UnitsApi::UserPrefUnit [50]; +int UnitsApi::UserPrefDecimals = 2; UnitsApi::UnitsApi(const char* filter) { @@ -196,6 +197,16 @@ const double UnitsApi::getPrefFactorOf(QuantityType t) return UserPrefFactor[t]; } +void UnitsApi::setDecimals(int prec) +{ + UserPrefDecimals = prec; +} + +int UnitsApi::getDecimals() +{ + return UserPrefDecimals; +} + void UnitsApi::setDefaults(void) { setPrefOf( Length ,"mm" ); diff --git a/src/Base/UnitsApi.h b/src/Base/UnitsApi.h index e3beae5d5..1b2fdcd64 100644 --- a/src/Base/UnitsApi.h +++ b/src/Base/UnitsApi.h @@ -101,6 +101,10 @@ public: static const QString getQuantityName(QuantityType t); /// get the translation factor for the default unit of a quantity static const double getPrefFactorOf(QuantityType t); + // set the number of decimals + static void setDecimals(int); + // fet the number of decimals + static int getDecimals(); /// set the application defaults static void setDefaults(void); //@} @@ -119,6 +123,8 @@ protected: static double UserPrefFactor [50] ; /// name of the unit the user wants to use as quantities static QString UserPrefUnit [50] ; + /// number of decimals for floats + static int UserPrefDecimals; // do the real work static double parse(const char*,bool &UsedUnit); diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index a4c3542b3..ad5663902 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -328,6 +329,10 @@ Application::Application(bool GUIenabled) Translator::instance()->activateLanguage(hPGrp->GetASCII("Language", (const char*)lang.toAscii()).c_str()); GetWidgetFactorySupplier(); + ParameterGrp::handle hUnits = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Units"); + Base::UnitsApi::setDecimals(hUnits->GetInt("Decimals", Base::UnitsApi::getDecimals())); + // setting up Python binding Base::PyGILStateLocker lock; PyObject* module = Py_InitModule3("FreeCADGui", Application::Methods, diff --git a/src/Gui/SelectionObject.h b/src/Gui/SelectionObject.h index 784b89f27..5c871594c 100644 --- a/src/Gui/SelectionObject.h +++ b/src/Gui/SelectionObject.h @@ -55,11 +55,11 @@ public: /// are there any SubNames selected bool hasSubNames(void)const { return SubNames.size() != 0; } /// get the name of the Document of this SelctionObject - inline const char* getDocName(void) { return DocName.c_str(); } + inline const char* getDocName(void) const { return DocName.c_str(); } /// get the name of the Document Object of this SelectionObject - inline const char* getFeatName(void) { return FeatName.c_str(); } + inline const char* getFeatName(void) const { return FeatName.c_str(); } /// get the Type of the selcted Object - inline const char* getTypeName(void) { return TypeName.c_str(); } + inline const char* getTypeName(void) const { return TypeName.c_str(); } /// returns the selected DocumentObject or NULL if the object is already deleted const App::DocumentObject *getObject(void) const; diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 3f0e5b08f..eab7c4c20 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -57,6 +57,7 @@ TYPESYSTEM_SOURCE(Gui::PropertyEditor::PropertyItem, Base::BaseClass); PropertyItem::PropertyItem() : parentItem(0), readonly(false) { + precision = Base::UnitsApi::getDecimals(); } PropertyItem::~PropertyItem() @@ -130,6 +131,16 @@ bool PropertyItem::isReadOnly() const return readonly; } +void PropertyItem::setDecimals(int prec) +{ + precision = prec; +} + +int PropertyItem::decimals() const +{ + return precision; +} + QVariant PropertyItem::toolTip(const App::Property* prop) const { return QVariant(QString::fromUtf8(prop->getDocumentation())); @@ -574,7 +585,7 @@ void PropertyFloatItem::setValue(const QVariant& value) if (!value.canConvert(QVariant::Double)) return; double val = value.toDouble(); - QString data = QString::fromAscii("%1").arg(val,0,'f',2); + QString data = QString::fromAscii("%1").arg(val,0,'f',decimals()); setPropertyValue(data); } @@ -582,6 +593,7 @@ QWidget* PropertyFloatItem::createEditor(QWidget* parent, const QObject* receive { QDoubleSpinBox *sb = new QDoubleSpinBox(parent); sb->setFrame(false); + sb->setDecimals(decimals()); QObject::connect(sb, SIGNAL(valueChanged(double)), receiver, method); return sb; } @@ -703,13 +715,14 @@ void PropertyFloatConstraintItem::setValue(const QVariant& value) if (!value.canConvert(QVariant::Double)) return; double val = value.toDouble(); - QString data = QString::fromAscii("%1").arg(val,0,'f',2); + QString data = QString::fromAscii("%1").arg(val,0,'f',decimals()); setPropertyValue(data); } QWidget* PropertyFloatConstraintItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { QDoubleSpinBox *sb = new QDoubleSpinBox(parent); + sb->setDecimals(decimals()); sb->setFrame(false); QObject::connect(sb, SIGNAL(valueChanged(double)), receiver, method); return sb; @@ -873,9 +886,9 @@ void PropertyVectorItem::setValue(const QVariant& value) return; const Base::Vector3f& val = value.value(); QString data = QString::fromAscii("(%1, %2, %3)") - .arg(val.x,0,'f',2) - .arg(val.y,0,'f',2) - .arg(val.z,0,'f',2); + .arg(val.x,0,'f',decimals()) + .arg(val.y,0,'f',decimals()) + .arg(val.z,0,'f',decimals()); setPropertyValue(data); } @@ -976,9 +989,9 @@ void PropertyDoubleVectorItem::setValue(const QVariant& value) return; const Base::Vector3d& val = value.value(); QString data = QString::fromAscii("(%1, %2, %3)") - .arg(val.x,0,'f',2) - .arg(val.y,0,'f',2) - .arg(val.z,0,'f',2); + .arg(val.x,0,'f',decimals()) + .arg(val.y,0,'f',decimals()) + .arg(val.z,0,'f',decimals()); setPropertyValue(data); } @@ -1503,9 +1516,9 @@ void PropertyColorItem::setValue(const QVariant& value) val.g = (float)col.green()/255.0f; val.b = (float)col.blue()/255.0f; QString data = QString::fromAscii("(%1,%2,%3)") - .arg(val.r,0,'f',2) - .arg(val.g,0,'f',2) - .arg(val.b,0,'f',2); + .arg(val.r,0,'f',decimals()) + .arg(val.g,0,'f',decimals()) + .arg(val.b,0,'f',decimals()); setPropertyValue(data); } diff --git a/src/Gui/propertyeditor/PropertyItem.h b/src/Gui/propertyeditor/PropertyItem.h index d334d8ced..bc247966f 100644 --- a/src/Gui/propertyeditor/PropertyItem.h +++ b/src/Gui/propertyeditor/PropertyItem.h @@ -69,6 +69,8 @@ public: void setReadOnly(bool); bool isReadOnly() const; + void setDecimals(int); + int decimals() const; PropertyItem *child(int row); int childCount() const; @@ -99,6 +101,7 @@ private: PropertyItem *parentItem; QList childItems; bool readonly; + int precision; }; /** diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py index d165e69ea..a2ea31033 100644 --- a/src/Mod/Arch/ArchAxis.py +++ b/src/Mod/Arch/ArchAxis.py @@ -109,7 +109,8 @@ class _ViewProviderAxis: vobj.LineColor = (0.13,0.15,0.37) vobj.DrawStyle = "Dashdot" - def getIcon(self): + def getIcon(self): + import Arch_rc return ":/icons/Arch_Axis_Tree.svg" def claimChildren(self): diff --git a/src/Mod/Arch/ArchBuilding.py b/src/Mod/Arch/ArchBuilding.py index 0d6d5cc40..968165527 100644 --- a/src/Mod/Arch/ArchBuilding.py +++ b/src/Mod/Arch/ArchBuilding.py @@ -86,6 +86,7 @@ class _ViewProviderBuilding(ArchFloor._ViewProviderFloor): ArchFloor._ViewProviderFloor.__init__(self,vobj) def getIcon(self): + import Arch_rc return ":/icons/Arch_Building_Tree.svg" FreeCADGui.addCommand('Arch_Building',_CommandBuilding()) diff --git a/src/Mod/Arch/ArchCell.py b/src/Mod/Arch/ArchCell.py index 8783602cc..6c4527ce5 100644 --- a/src/Mod/Arch/ArchCell.py +++ b/src/Mod/Arch/ArchCell.py @@ -121,6 +121,7 @@ class _ViewProviderCell(ArchComponent.ViewProviderComponent): self.Object = vobj.Object def getIcon(self): + import Arch_rc return ":/icons/Arch_Cell_Tree.svg" def updateData(self,obj,prop): diff --git a/src/Mod/Arch/ArchFloor.py b/src/Mod/Arch/ArchFloor.py index f7ccab17a..be9956107 100644 --- a/src/Mod/Arch/ArchFloor.py +++ b/src/Mod/Arch/ArchFloor.py @@ -114,6 +114,7 @@ class _ViewProviderFloor: vobj.Proxy = self def getIcon(self): + import Arch_rc return ":/icons/Arch_Floor_Tree.svg" def attach(self,vobj): diff --git a/src/Mod/Arch/ArchRoof.py b/src/Mod/Arch/ArchRoof.py index 4c8d6964c..e85581e60 100644 --- a/src/Mod/Arch/ArchRoof.py +++ b/src/Mod/Arch/ArchRoof.py @@ -148,7 +148,8 @@ class _ViewProviderRoof(ArchComponent.ViewProviderComponent): def __init__(self,vobj): ArchComponent.ViewProviderComponent.__init__(self,vobj) - def getIcon(self): + def getIcon(self): + import Arch_rc return ":/icons/Arch_Roof_Tree.svg" FreeCADGui.addCommand('Arch_Roof',_CommandRoof()) diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index aa1d8bb99..460d3a7d1 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -123,6 +123,7 @@ class _ViewProviderSectionPlane(ArchComponent.ViewProviderComponent): self.Object = vobj.Object def getIcon(self): + import Arch_rc return ":/icons/Arch_SectionPlane_Tree.svg" def claimChildren(self): diff --git a/src/Mod/Arch/ArchSite.py b/src/Mod/Arch/ArchSite.py index 69cfda8ba..f6c6e9acb 100644 --- a/src/Mod/Arch/ArchSite.py +++ b/src/Mod/Arch/ArchSite.py @@ -89,6 +89,7 @@ class _ViewProviderSite(ArchFloor._ViewProviderFloor): ArchFloor._ViewProviderFloor.__init__(self,vobj) def getIcon(self): + import Arch_rc return ":/icons/Arch_Site_Tree.svg" diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index d257c4d92..1a9f51c07 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -193,7 +193,8 @@ class _ViewProviderStructure(ArchComponent.ViewProviderComponent): def __init__(self,vobj): ArchComponent.ViewProviderComponent.__init__(self,vobj) - def getIcon(self): + def getIcon(self): + import Arch_rc return ":/icons/Arch_Structure_Tree.svg" FreeCADGui.addCommand('Arch_Structure',_CommandStructure()) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 1d6f8e5e0..e8236af3f 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -423,7 +423,8 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): def __init__(self,vobj): ArchComponent.ViewProviderComponent.__init__(self,vobj) - def getIcon(self): + def getIcon(self): + import Arch_rc return ":/icons/Arch_Wall_Tree.svg" def getDisplayModes(self,vobj): diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index 2f593257f..ad9d49369 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -179,7 +179,8 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): def __init__(self,vobj): ArchComponent.ViewProviderComponent.__init__(self,vobj) - def getIcon(self): + def getIcon(self): + import Arch_rc return ":/icons/Arch_Window_Tree.svg" def setEdit(self,vobj,mode): diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 716597e2b..4980b0be1 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -179,6 +179,7 @@ void PartExport initPart() Part::Part2DObjectPython ::init(); Part::RuledSurface ::init(); Part::Loft ::init(); + Part::Sweep ::init(); // Geometry types Part::Geometry ::init(); diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index ef43452df..45e0076e9 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -30,6 +30,10 @@ # include # include # include +// includes for findAllFacesCutBy() +# include +# include +# include // for Precision::Confusion() #endif @@ -139,22 +143,25 @@ ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_Sha history.type = type; TopTools_IndexedMapOfShape newM, oldM; - TopExp::MapShapes(newS, type, newM); - TopExp::MapShapes(oldS, type, oldM); + TopExp::MapShapes(newS, type, newM); // map containing all old objects of type "type" + TopExp::MapShapes(oldS, type, oldM); // map containing all new objects of type "type" + // Look at all objects in the old shape and try to find the modified object in the new shape for (int i=1; i<=oldM.Extent(); i++) { bool found = false; TopTools_ListIteratorOfListOfShape it; + // Find all new objects that are a modification of the old object (e.g. a face was resized) for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) { found = true; - for (int j=1; j<=newM.Extent(); j++) { + for (int j=1; j<=newM.Extent(); j++) { // one old object might create several new ones! if (newM(j).IsPartner(it.Value())) { - history.shapeMap[i-1].push_back(j-1); + history.shapeMap[i-1].push_back(j-1); // adjust indices to start at zero break; } } } + // Find all new objects that were generated from an old object (e.g. a face generated from an edge) for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) { found = true; for (int j=1; j<=newM.Extent(); j++) { @@ -166,10 +173,12 @@ ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_Sha } if (!found) { + // Find all old objects that don't exist any more (e.g. a face was completely cut away) if (mkShape.IsDeleted(oldM(i))) { history.shapeMap[i-1] = std::vector(); } else { + // Mop up the rest (will this ever be reached?) for (int j=1; j<=newM.Extent(); j++) { if (newM(j).IsPartner(oldM(i))) { history.shapeMap[i-1].push_back(j-1); @@ -204,6 +213,15 @@ ShapeHistory Feature::joinHistory(const ShapeHistory& oldH, const ShapeHistory& return join; } +const TopoDS_Shape Feature::findOriginOf(const TopoDS_Shape& reference) { +/* Base::Console().Error("Looking for origin of face in %s\n", this->getName()); + if (reference.ShapeType() == TopAbs_FACE) { + // Find index of reference in the history + } +*/ + return TopoDS_Shape(); +} + /// returns the type name of the ViewProvider const char* Feature::getViewProviderName(void) const { return "PartGui::ViewProviderPart"; @@ -253,3 +271,51 @@ template<> PyObject* Part::FeaturePython::getPyObject(void) { template class PartExport FeaturePythonT; } +// ---------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +std::vector Part::findAllFacesCutBy( + const TopoDS_Shape& shape, const TopoDS_Shape& face, const gp_Dir& dir) +{ + // Find the centre of gravity of the face + GProp_GProps props; + BRepGProp::SurfaceProperties(face,props); + gp_Pnt cog = props.CentreOfMass(); + + // create a line through the centre of gravity + gp_Lin line = gce_MakeLin(cog, dir); + + // Find intersection of line with all faces of the shape + std::vector result; + BRepIntCurveSurface_Inter mkSection; + // TODO: Less precision than Confusion() should be OK? + + for (mkSection.Init(shape, line, Precision::Confusion()); mkSection.More(); mkSection.Next()) { + gp_Pnt iPnt = mkSection.Pnt(); + double dsq = cog.SquareDistance(iPnt); + + if (dsq < Precision::Confusion()) + continue; // intersection with original face + + // Find out which side of the original face the intersection is on + gce_MakeDir mkDir(cog, iPnt); + if (!mkDir.IsDone()) + continue; // some error (appears highly unlikely to happen, though...) + + if (mkDir.Value().IsOpposite(dir, Precision::Confusion())) + continue; // wrong side of face (opposite to extrusion direction) + + cutFaces newF; + newF.face = mkSection.Face(); + newF.distsq = dsq; + result.push_back(newF); + } + + return result; +} diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index 689736a42..9bb4bd49a 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -29,9 +29,16 @@ #include #include #include +// includes for findAllFacesCutBy() +#include +class gp_Dir; class BRepBuilderAPI_MakeShape; +// includes for findAllFacesCutBy() +#include +class gp_Dir; + namespace Part { @@ -63,9 +70,22 @@ public: virtual PyObject* getPyObject(void); virtual std::vector getPySubObjects(const std::vector&) const; + /** + /* Find the origin of a reference, e.g. the vertex or edge in a sketch that + /* produced a face + */ + const TopoDS_Shape findOriginOf(const TopoDS_Shape& reference); + protected: void onChanged(const App::Property* prop); TopLoc_Location getLocation() const; + /** + /* Build a history of changes + /* MakeShape: The operation that created the changes, e.g. BRepAlgoAPI_Common + /* type: The type of object we are interested in, e.g. TopAbs_FACE + /* newS: The new shape that was created by the operation + /* oldS: The original shape prior to the operation + */ ShapeHistory buildHistory(BRepBuilderAPI_MakeShape&, TopAbs_ShapeEnum type, const TopoDS_Shape& newS, const TopoDS_Shape& oldS); ShapeHistory joinHistory(const ShapeHistory&, const ShapeHistory&); @@ -99,6 +119,20 @@ public: } }; +// Utility methods +/** +/* Find all faces cut by a line through the centre of gravity of a given face +/* Useful for the "up to face" options to pocket or pad +*/ +struct cutFaces { + TopoDS_Face face; + double distsq; +}; + +PartExport +std::vector findAllFacesCutBy(const TopoDS_Shape& shape, + const TopoDS_Shape& face, const gp_Dir& dir); + } //namespace Part diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index 1f2088874..1bf2ce874 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -28,6 +28,8 @@ # include # include # include +# include +# include #endif @@ -196,3 +198,132 @@ App::DocumentObjectExecReturn *Loft::execute(void) return new App::DocumentObjectExecReturn(e->GetMessageString()); } } + +// ---------------------------------------------------------------------------- + +const char* Part::Sweep::TransitionEnums[]= {"Transformed","Right corner", "Round corner",NULL}; + +PROPERTY_SOURCE(Part::Sweep, Part::Feature) + +Sweep::Sweep() +{ + ADD_PROPERTY_TYPE(Sections,(0),"Sweep",App::Prop_None,"List of sections"); + Sections.setSize(0); + ADD_PROPERTY_TYPE(Spine,(0),"Sweep",App::Prop_None,"Path to sweep along"); + ADD_PROPERTY_TYPE(Solid,(false),"Sweep",App::Prop_None,"Create solid"); + ADD_PROPERTY_TYPE(Frenet,(false),"Sweep",App::Prop_None,"Frenet"); + ADD_PROPERTY_TYPE(Transition,(long(0)),"Sweep",App::Prop_None,"Transition mode"); + Transition.setEnums(TransitionEnums); +} + +short Sweep::mustExecute() const +{ + if (Sections.isTouched()) + return 1; + if (Spine.isTouched()) + return 1; + if (Solid.isTouched()) + return 1; + if (Frenet.isTouched()) + return 1; + if (Transition.isTouched()) + return 1; + return 0; +} + +void Sweep::onChanged(const App::Property* prop) +{ + Part::Feature::onChanged(prop); +} + +App::DocumentObjectExecReturn *Sweep::execute(void) +{ + if (Sections.getSize() == 0) + return new App::DocumentObjectExecReturn("No sections linked."); + App::DocumentObject* spine = Spine.getValue(); + if (!(spine && spine->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()))) + return new App::DocumentObjectExecReturn("No spine linked."); + const std::vector& subedge = Spine.getSubValues(); + if (subedge.size() != 1) + return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked."); + + TopoDS_Shape path; + const Part::TopoShape& shape = static_cast(spine)->Shape.getValue(); + if (!shape._Shape.IsNull()) { + if (!subedge[0].empty()) { + path = shape.getSubShape(subedge[0].c_str()); + } + else { + if (shape._Shape.ShapeType() == TopAbs_EDGE) + path = shape._Shape; + else if (shape._Shape.ShapeType() == TopAbs_WIRE) + path = shape._Shape; + else + return new App::DocumentObjectExecReturn("Spine is neither an edge nor a wire."); + } + } + + try { + TopTools_ListOfShape profiles; + const std::vector& shapes = Sections.getValues(); + std::vector::const_iterator it; + for (it = shapes.begin(); it != shapes.end(); ++it) { + if (!(*it)->isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("Linked object is not a shape."); + const TopoDS_Shape& shape = static_cast(*it)->Shape.getValue(); + if (shape.IsNull()) + return new App::DocumentObjectExecReturn("Linked shape is invalid."); + if (shape.ShapeType() == TopAbs_WIRE) { + profiles.Append(shape); + } + else if (shape.ShapeType() == TopAbs_EDGE) { + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(shape)); + profiles.Append(mkWire.Wire()); + } + else if (shape.ShapeType() == TopAbs_VERTEX) { + profiles.Append(shape); + } + else { + return new App::DocumentObjectExecReturn("Linked shape is not a vertex, edge nor wire."); + } + } + + Standard_Boolean isSolid = Solid.getValue() ? Standard_True : Standard_False; + Standard_Boolean isFrenet = Frenet.getValue() ? Standard_True : Standard_False; + BRepBuilderAPI_TransitionMode transMode; + switch (Transition.getValue()) { + case 1: transMode = BRepBuilderAPI_RightCorner; + break; + case 2: transMode = BRepBuilderAPI_RoundCorner; + break; + default: transMode = BRepBuilderAPI_Transformed; + break; + } + + if (path.ShapeType() == TopAbs_EDGE) { + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(path)); + path = mkWire.Wire(); + } + + BRepOffsetAPI_MakePipeShell mkPipeShell(TopoDS::Wire(path)); + mkPipeShell.SetMode(isFrenet); + mkPipeShell.SetTransitionMode(transMode); + TopTools_ListIteratorOfListOfShape iter; + for (iter.Initialize(profiles); iter.More(); iter.Next()) { + mkPipeShell.Add(TopoDS_Shape(iter.Value())); + } + + if (!mkPipeShell.IsReady()) + Standard_Failure::Raise("shape is not ready to build"); + mkPipeShell.Build(); + if (isSolid) + mkPipeShell.MakeSolid(); + + this->Shape.setValue(mkPipeShell.Shape()); + return App::DocumentObject::StdReturn; + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); + } +} diff --git a/src/Mod/Part/App/PartFeatures.h b/src/Mod/Part/App/PartFeatures.h index f01fde575..ddaae9627 100644 --- a/src/Mod/Part/App/PartFeatures.h +++ b/src/Mod/Part/App/PartFeatures.h @@ -73,6 +73,33 @@ protected: void onChanged (const App::Property* prop); }; +class Sweep : public Part::Feature +{ + PROPERTY_HEADER(Part::Sweep); + +public: + Sweep(); + + App::PropertyLinkList Sections; + App::PropertyLinkSub Spine; + App::PropertyBool Solid; + App::PropertyBool Frenet; + App::PropertyEnumeration Transition; + + /** @name methods override feature */ + //@{ + /// recalculate the feature + App::DocumentObjectExecReturn *execute(void); + short mustExecute() const; + //@} + +protected: + void onChanged (const App::Property* prop); + +private: + static const char* TransitionEnums[]; +}; + } //namespace Part diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index d9ff99f95..ec068eb95 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -41,6 +41,7 @@ set(PartGui_MOC_HDRS TaskFaceColors.h TaskShapeBuilder.h TaskLoft.h + TaskSweep.h ) fc_wrap_cpp(PartGui_MOC_SRCS ${PartGui_MOC_HDRS}) SOURCE_GROUP("Moc" FILES ${PartGui_MOC_SRCS}) @@ -65,6 +66,7 @@ set(PartGui_UIC_SRCS TaskFaceColors.ui TaskShapeBuilder.ui TaskLoft.ui + TaskSweep.ui ) qt4_wrap_ui(PartGui_UIC_HDRS ${PartGui_UIC_SRCS}) @@ -155,6 +157,9 @@ SET(PartGui_SRCS TaskLoft.cpp TaskLoft.h TaskLoft.ui + TaskSweep.cpp + TaskSweep.h + TaskSweep.ui ) SET(PartGui_Scripts diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index 33cd008ee..5020e4872 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -61,6 +61,7 @@ #include "ViewProvider.h" #include "TaskShapeBuilder.h" #include "TaskLoft.h" +#include "TaskSweep.h" //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -960,6 +961,31 @@ bool CmdPartLoft::isActive(void) //-------------------------------------------------------------------------------------- +DEF_STD_CMD_A(CmdPartSweep); + +CmdPartSweep::CmdPartSweep() + : Command("Part_Sweep") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Sweep..."); + sToolTipText = QT_TR_NOOP("Advanced utility to sweep"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; +} + +void CmdPartSweep::activated(int iMsg) +{ + Gui::Control().showDialog(new PartGui::TaskSweep()); +} + +bool CmdPartSweep::isActive(void) +{ + return (hasActiveDocument() && !Gui::Control().activeDialog()); +} + +//-------------------------------------------------------------------------------------- + DEF_STD_CMD_A(CmdShapeInfo); CmdShapeInfo::CmdShapeInfo() @@ -1194,5 +1220,6 @@ void CreatePartCommands(void) rcCmdMgr.addCommand(new CmdPartRuledSurface()); rcCmdMgr.addCommand(new CmdPartBuilder()); rcCmdMgr.addCommand(new CmdPartLoft()); + rcCmdMgr.addCommand(new CmdPartSweep()); } diff --git a/src/Mod/Part/Gui/TaskSweep.cpp b/src/Mod/Part/Gui/TaskSweep.cpp new file mode 100644 index 000000000..effd5c28b --- /dev/null +++ b/src/Mod/Part/Gui/TaskSweep.cpp @@ -0,0 +1,255 @@ +/*************************************************************************** + * Copyright (c) 2011 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +#endif + +#include "ui_TaskSweep.h" +#include "TaskSweep.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +using namespace PartGui; + +class SweepWidget::Private +{ +public: + Ui_TaskSweep ui; + std::string document; + Private() + { + } + ~Private() + { + } +}; + +/* TRANSLATOR PartGui::SweepWidget */ + +SweepWidget::SweepWidget(QWidget* parent) + : d(new Private()) +{ + Gui::Application::Instance->runPythonCode("from FreeCAD import Base"); + Gui::Application::Instance->runPythonCode("import Part"); + + d->ui.setupUi(this); + d->ui.selector->setAvailableLabel(tr("Vertex/Wire")); + d->ui.selector->setSelectedLabel(tr("Sweep")); + + connect(d->ui.selector->availableTreeWidget(), SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + this, SLOT(onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); + connect(d->ui.selector->selectedTreeWidget(), SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + this, SLOT(onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); + + findShapes(); +} + +SweepWidget::~SweepWidget() +{ + delete d; +} + +void SweepWidget::findShapes() +{ + App::Document* activeDoc = App::GetApplication().getActiveDocument(); + Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc); + if (!activeGui) return; + d->document = activeDoc->getName(); + + std::vector objs = activeDoc->getObjectsOfType(); + + for (std::vector::iterator it = objs.begin(); it!=objs.end(); ++it) { + const TopoDS_Shape& shape = (*it)->Shape.getValue(); + if (shape.IsNull()) continue; + + if (shape.ShapeType() == TopAbs_WIRE || + shape.ShapeType() == TopAbs_EDGE || + shape.ShapeType() == TopAbs_VERTEX) { + QString label = QString::fromUtf8((*it)->Label.getValue()); + QString name = QString::fromAscii((*it)->getNameInDocument()); + + QTreeWidgetItem* child = new QTreeWidgetItem(); + child->setText(0, label); + child->setToolTip(0, label); + child->setData(0, Qt::UserRole, name); + Gui::ViewProvider* vp = activeGui->getViewProvider(*it); + if (vp) child->setIcon(0, vp->getIcon()); + d->ui.selector->availableTreeWidget()->addTopLevelItem(child); + } + } +} + +bool SweepWidget::accept() +{ + Gui::SelectionFilter edgeFilter ("SELECT Part::Feature SUBELEMENT Edge COUNT 1"); + Gui::SelectionFilter partFilter ("SELECT Part::Feature COUNT 1"); + bool matchEdge = edgeFilter.match(); + bool matchPart = partFilter.match(); + if (!matchEdge && !matchPart) { + QMessageBox::critical(this, tr("Sweep path"), tr("Select an edge or wire you want to sweep along.")); + return false; + } + + // get the selected object + std::string objectName, subShape; + if (matchEdge) { + const std::vector& result = edgeFilter.Result[0]; + const std::vector& edges = result[0].getSubNames(); + objectName = result.front().getFeatName(); + subShape = edges.front(); + } + else { + const std::vector& result = partFilter.Result[0]; + objectName = result.front().getFeatName(); + } + + QString list, solid, frenet; + if (d->ui.checkSolid->isChecked()) + solid = QString::fromAscii("True"); + else + solid = QString::fromAscii("False"); + + if (d->ui.checkFrenet->isChecked()) + frenet = QString::fromAscii("True"); + else + frenet = QString::fromAscii("False"); + + QTextStream str(&list); + + int count = d->ui.selector->selectedTreeWidget()->topLevelItemCount(); + if (count < 1) { + QMessageBox::critical(this, tr("Too few elements"), tr("At least one edge or wire is required.")); + return false; + } + for (int i=0; iui.selector->selectedTreeWidget()->topLevelItem(i); + QString name = child->data(0, Qt::UserRole).toString(); + str << "App.getDocument('" << d->document.c_str() << "')." << name << ", "; + } + + try { + QString cmd; + cmd = QString::fromAscii( + "App.getDocument('%6').addObject('Part::Sweep','Sweep')\n" + "App.getDocument('%6').ActiveObject.Sections=[%1]\n" + "App.getDocument('%6').ActiveObject.Spine=(FreeCAD.ActiveDocument.%2,['%3'])\n" + "App.getDocument('%6').ActiveObject.Solid=%4\n" + "App.getDocument('%6').ActiveObject.Frenet=%5\n" + ) + .arg(list).arg(QLatin1String(objectName.c_str())) + .arg(QLatin1String(subShape.c_str())) + .arg(solid).arg(frenet).arg(QString::fromAscii(d->document.c_str())); + + Gui::Document* doc = Gui::Application::Instance->getDocument(d->document.c_str()); + if (!doc) throw Base::Exception("Document doesn't exist anymore"); + doc->openCommand("Sweep"); + Gui::Application::Instance->runPythonCode((const char*)cmd.toAscii(), false, false); + doc->commitCommand(); + doc->getDocument()->recompute(); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + return false; + } + + return true; +} + +bool SweepWidget::reject() +{ + return true; +} + +void SweepWidget::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) +{ + if (previous) { + Gui::Selection().rmvSelection(d->document.c_str(), + (const char*)previous->data(0,Qt::UserRole).toByteArray()); + } + if (current) { + Gui::Selection().addSelection(d->document.c_str(), + (const char*)current->data(0,Qt::UserRole).toByteArray()); + } +} + +void SweepWidget::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + d->ui.retranslateUi(this); + d->ui.selector->setAvailableLabel(tr("Vertex/Wire")); + d->ui.selector->setSelectedLabel(tr("Sweep")); + } +} + + +/* TRANSLATOR PartGui::TaskSweep */ + +TaskSweep::TaskSweep() +{ + widget = new SweepWidget(); + taskbox = new Gui::TaskView::TaskBox( + QPixmap(), widget->windowTitle(), true, 0); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); +} + +TaskSweep::~TaskSweep() +{ +} + +void TaskSweep::open() +{ +} + +void TaskSweep::clicked(int) +{ +} + +bool TaskSweep::accept() +{ + return widget->accept(); +} + +bool TaskSweep::reject() +{ + return widget->reject(); +} + +#include "moc_TaskSweep.cpp" diff --git a/src/Mod/Part/Gui/TaskSweep.h b/src/Mod/Part/Gui/TaskSweep.h new file mode 100644 index 000000000..f0c6fc592 --- /dev/null +++ b/src/Mod/Part/Gui/TaskSweep.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (c) 2011 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_TASKSWEEP_H +#define PARTGUI_TASKSWEEP_H + +#include +#include + +class QTreeWidgetItem; + +namespace PartGui { + +class SweepWidget : public QWidget +{ + Q_OBJECT + +public: + SweepWidget(QWidget* parent = 0); + ~SweepWidget(); + + bool accept(); + bool reject(); + +private Q_SLOTS: + void onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); + +private: + void changeEvent(QEvent *e); + void findShapes(); + +private: + class Private; + Private* d; +}; + +class TaskSweep : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskSweep(); + ~TaskSweep(); + +public: + void open(); + bool accept(); + bool reject(); + void clicked(int); + + QDialogButtonBox::StandardButtons getStandardButtons() const + { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } + +private: + SweepWidget* widget; + Gui::TaskView::TaskBox* taskbox; +}; + +} //namespace PartGui + +#endif // PARTGUI_TASKSWEEP_H diff --git a/src/Mod/Part/Gui/TaskSweep.ui b/src/Mod/Part/Gui/TaskSweep.ui new file mode 100644 index 000000000..6cfab459d --- /dev/null +++ b/src/Mod/Part/Gui/TaskSweep.ui @@ -0,0 +1,68 @@ + + + PartGui::TaskSweep + + + + 0 + 0 + 336 + 326 + + + + Sweep + + + + + + + + + Create solid + + + + + + + Qt::Horizontal + + + + 130 + 20 + + + + + + + + Frenet + + + + + + + Select one or more profiles and select an edge or wire +in the 3D view for the sweep path. + + + + + + + + Gui::ActionSelector + QWidget +
Gui/Widgets.h
+
+
+ + + + +
diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index ea42cb9f3..9997750b2 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -72,7 +72,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Part_RefineShape" << "Separator" << "Part_Boolean" << "Part_CrossSections" << "Part_Extrude" << "Part_Revolve" << "Part_Mirror" << "Part_Fillet" << "Part_Chamfer" - << "Part_RuledSurface" << "Part_Loft" + << "Part_RuledSurface" << "Part_Loft" << "Part_Sweep" << "Part_Builder"; //Gui::MenuItem* partSimple = new Gui::MenuItem; diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp index debb60f26..16620762f 100644 --- a/src/Mod/PartDesign/App/AppPartDesign.cpp +++ b/src/Mod/PartDesign/App/AppPartDesign.cpp @@ -34,6 +34,7 @@ #include "FeatureFillet.h" #include "FeatureSketchBased.h" #include "FeatureRevolution.h" +#include "FeatureGroove.h" #include "Body.h" #include "FeatureDressUp.h" #include "FeatureChamfer.h" @@ -82,6 +83,7 @@ void PartDesignExport initPartDesign() PartDesign::Pocket ::init(); PartDesign::Fillet ::init(); PartDesign::Revolution ::init(); + PartDesign::Groove ::init(); PartDesign::Chamfer ::init(); PartDesign::Face ::init(); } diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt index 49af69836..837189bb1 100644 --- a/src/Mod/PartDesign/App/CMakeLists.txt +++ b/src/Mod/PartDesign/App/CMakeLists.txt @@ -50,6 +50,8 @@ SET(FeaturesSketchBased_SRCS FeaturePocket.h FeatureRevolution.cpp FeatureRevolution.h + FeatureGroove.cpp + FeatureGroove.h FeatureAdditive.cpp FeatureAdditive.h FeatureSubtractive.h diff --git a/src/Mod/PartDesign/App/FeatureGroove.cpp b/src/Mod/PartDesign/App/FeatureGroove.cpp new file mode 100644 index 000000000..1cf17541d --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureGroove.cpp @@ -0,0 +1,208 @@ +/*************************************************************************** + * Copyright (c) 2010 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include +#include + +#include "FeatureGroove.h" + + +using namespace PartDesign; + +namespace PartDesign { + + +PROPERTY_SOURCE(PartDesign::Groove, PartDesign::SketchBased) + +Groove::Groove() +{ + ADD_PROPERTY_TYPE(Base,(Base::Vector3f(0.0f,0.0f,0.0f)),"Groove", App::Prop_ReadOnly, "Base"); + ADD_PROPERTY_TYPE(Axis,(Base::Vector3f(0.0f,1.0f,0.0f)),"Groove", App::Prop_ReadOnly, "Axis"); + ADD_PROPERTY_TYPE(Angle,(360.0),"Groove", App::Prop_None, "Angle"); + ADD_PROPERTY_TYPE(ReferenceAxis,(0),"Groove",(App::PropertyType)(App::Prop_None),"Reference axis of Groove"); + ADD_PROPERTY_TYPE(Midplane,(0),"Groove", App::Prop_None, "Mid plane"); + ADD_PROPERTY_TYPE(Reversed, (0),"Groove", App::Prop_None, "Reversed"); +} + +short Groove::mustExecute() const +{ + if (Placement.isTouched() || + Sketch.isTouched() || + ReferenceAxis.isTouched() || + Axis.isTouched() || + Base.isTouched() || + Angle.isTouched() || + Midplane.isTouched() || + Reversed.isTouched()) + return 1; + return 0; +} + +App::DocumentObjectExecReturn *Groove::execute(void) +{ + App::DocumentObject* link = Sketch.getValue(); + if (!link) + return new App::DocumentObjectExecReturn("No sketch linked"); + if (!link->getTypeId().isDerivedFrom(Part::Part2DObject::getClassTypeId())) + return new App::DocumentObjectExecReturn("Linked object is not a Sketch or Part2DObject"); + + Part::Part2DObject* pcSketch=static_cast(link); + + TopoDS_Shape shape = pcSketch->Shape.getShape()._Shape; + if (shape.IsNull()) + return new App::DocumentObjectExecReturn("Linked shape object is empty"); + + // this is a workaround for an obscure OCC bug which leads to empty tessellations + // for some faces. Making an explicit copy of the linked shape seems to fix it. + // The error only happens when re-computing the shape. + if (!this->Shape.getValue().IsNull()) { + BRepBuilderAPI_Copy copy(shape); + shape = copy.Shape(); + if (shape.IsNull()) + return new App::DocumentObjectExecReturn("Linked shape object is empty"); + } + + TopExp_Explorer ex; + std::vector wires; + for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next()) { + wires.push_back(TopoDS::Wire(ex.Current())); + } + if (wires.empty()) // there can be several wires + return new App::DocumentObjectExecReturn("Linked shape object is not a wire"); + + // get the Sketch plane + Base::Placement SketchPlm = pcSketch->Placement.getValue(); + + // get reference axis + App::DocumentObject *pcReferenceAxis = ReferenceAxis.getValue(); + const std::vector &subReferenceAxis = ReferenceAxis.getSubValues(); + if (pcReferenceAxis && pcReferenceAxis == pcSketch) { + bool hasValidAxis=false; + Base::Axis axis; + if (subReferenceAxis[0] == "V_Axis") { + hasValidAxis = true; + axis = pcSketch->getAxis(Part::Part2DObject::V_Axis); + } + else if (subReferenceAxis[0] == "H_Axis") { + hasValidAxis = true; + axis = pcSketch->getAxis(Part::Part2DObject::H_Axis); + } + else if (subReferenceAxis[0].size() > 4 && subReferenceAxis[0].substr(0,4) == "Axis") { + int AxId = std::atoi(subReferenceAxis[0].substr(4,4000).c_str()); + if (AxId >= 0 && AxId < pcSketch->getAxisCount()) { + hasValidAxis = true; + axis = pcSketch->getAxis(AxId); + } + } + if (hasValidAxis) { + axis *= SketchPlm; + Base::Vector3d base=axis.getBase(); + Base::Vector3d dir=axis.getDirection(); + Base.setValue(base.x,base.y,base.z); + Axis.setValue(dir.x,dir.y,dir.z); + } + } + + // get revolve axis + Base::Vector3f b = Base.getValue(); + gp_Pnt pnt(b.x,b.y,b.z); + Base::Vector3f v = Axis.getValue(); + gp_Dir dir(v.x,v.y,v.z); + + // get the support of the Sketch if any + App::DocumentObject* pcSupport = pcSketch->Support.getValue(); + Part::Feature *SupportObject = 0; + if (pcSupport && pcSupport->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + SupportObject = static_cast(pcSupport); + + TopoDS_Shape aFace = makeFace(wires); + if (aFace.IsNull()) + return new App::DocumentObjectExecReturn("Creating a face from sketch failed"); + + // Rotate the face by half the angle to get Groove symmetric to sketch plane + if (Midplane.getValue()) { + gp_Trsf mov; + mov.SetRotation(gp_Ax1(pnt, dir), Base::toRadians(Angle.getValue()) * (-1.0) / 2.0); + TopLoc_Location loc(mov); + aFace.Move(loc); + } + + this->positionBySketch(); + TopLoc_Location invObjLoc = this->getLocation().Inverted(); + pnt.Transform(invObjLoc.Transformation()); + dir.Transform(invObjLoc.Transformation()); + + // Reverse angle if selected + double angle = Base::toRadians(Angle.getValue()); + if (Reversed.getValue() && !Midplane.getValue()) + angle *= (-1.0); + + try { + // revolve the face to a solid + BRepPrimAPI_MakeRevol RevolMaker(aFace.Moved(invObjLoc), gp_Ax1(pnt, dir), angle); + + if (RevolMaker.IsDone()) { + TopoDS_Shape result = RevolMaker.Shape(); + // if the sketch has a support fuse them to get one result object (PAD!) + if (SupportObject) { + const TopoDS_Shape& support = SupportObject->Shape.getValue(); + if (!support.IsNull() && support.ShapeType() == TopAbs_SOLID) { + // Let's call algorithm computing a fuse operation: + BRepAlgoAPI_Cut mkCut(support.Moved(invObjLoc), result); + // Let's check if the fusion has been successful + if (!mkCut.IsDone()) + throw Base::Exception("Cut out of support failed"); + result = mkCut.Shape(); + } + } + + this->Shape.setValue(result); + } + else + return new App::DocumentObjectExecReturn("Could not revolve the sketch!"); + + return App::DocumentObject::StdReturn; + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); + } +} + +} diff --git a/src/Mod/PartDesign/App/FeatureGroove.h b/src/Mod/PartDesign/App/FeatureGroove.h new file mode 100644 index 000000000..782e728cb --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureGroove.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (c) 2010 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PARTDESIGN_Groove_H +#define PARTDESIGN_Groove_H + +#include +#include "FeatureSketchBased.h" + +namespace PartDesign +{ + +class Groove : public SketchBased +{ + PROPERTY_HEADER(PartDesign::Groove); + +public: + Groove(); + + App::PropertyVector Base; + App::PropertyVector Axis; + App::PropertyAngle Angle; + App::PropertyBool Midplane; + App::PropertyBool Reversed; + + /** if this property is set to a valid link, both Axis and Base properties + * are calculated according to the linked line + */ + App::PropertyLinkSub ReferenceAxis; + + /** @name methods override feature */ + //@{ + /// recalculate the feature + App::DocumentObjectExecReturn *execute(void); + short mustExecute() const; + /// returns the type name of the view provider + const char* getViewProviderName(void) const { + return "PartDesignGui::ViewProviderGroove"; + } + //@} +}; + +} //namespace PartDesign + + +#endif // PART_Groove_H diff --git a/src/Mod/PartDesign/App/FeaturePad.cpp b/src/Mod/PartDesign/App/FeaturePad.cpp index 1a12c4556..8335668ae 100644 --- a/src/Mod/PartDesign/App/FeaturePad.cpp +++ b/src/Mod/PartDesign/App/FeaturePad.cpp @@ -23,14 +23,12 @@ #include "PreCompiled.h" #ifndef _PreComp_ -//# include -//# include # include +# include # include # include # include # include -//# include # include # include # include @@ -39,23 +37,32 @@ # include # include # include +# include +# include #endif #include #include +#include #include "FeaturePad.h" using namespace PartDesign; +const char* Pad::TypeEnums[]= {"Length","UpToLast","UpToFirst","UpToFace","TwoLengths",NULL}; + PROPERTY_SOURCE(PartDesign::Pad, PartDesign::Additive) Pad::Pad() { + ADD_PROPERTY(Type,((long)0)); + Type.setEnums(TypeEnums); ADD_PROPERTY(Length,(100.0)); ADD_PROPERTY(Reversed,(0)); - ADD_PROPERTY(MirroredExtent,(0)); + ADD_PROPERTY(Midplane,(0)); + ADD_PROPERTY(Length2,(100.0)); + ADD_PROPERTY(FaceName,("")); } short Pad::mustExecute() const @@ -63,8 +70,10 @@ short Pad::mustExecute() const if (Placement.isTouched() || Sketch.isTouched() || Length.isTouched() || - MirroredExtent.isTouched() || - Reversed.isTouched()) + Midplane.isTouched() || + Reversed.isTouched() || + Length2.isTouched() || + FaceName.isTouched()) return 1; return 0; } @@ -74,6 +83,10 @@ App::DocumentObjectExecReturn *Pad::execute(void) double L = Length.getValue(); if (L < Precision::Confusion()) return new App::DocumentObjectExecReturn("Length of pad too small"); + double L2 = Length2.getValue(); + if ((std::string(Type.getValueAsString()) == "TwoLengths") && (L < Precision::Confusion())) + return new App::DocumentObjectExecReturn("Second length of pad too small"); + App::DocumentObject* link = Sketch.getValue(); if (!link) return new App::DocumentObjectExecReturn("No sketch linked"); @@ -103,10 +116,8 @@ App::DocumentObjectExecReturn *Pad::execute(void) // get the Sketch plane Base::Placement SketchPos = static_cast(link)->Placement.getValue(); Base::Rotation SketchOrientation = SketchPos.getRotation(); - Base::Vector3d SketchOrientationVector(0,0,1); - if (Reversed.getValue()) // negative direction - SketchOrientationVector *= -1; - SketchOrientation.multVec(SketchOrientationVector,SketchOrientationVector); + Base::Vector3d SketchVector(0,0,1); + SketchOrientation.multVec(SketchVector,SketchVector); // get the support of the Sketch if any App::DocumentObject* SupportLink = static_cast(link)->Support.getValue(); @@ -118,27 +129,159 @@ App::DocumentObjectExecReturn *Pad::execute(void) if (aFace.IsNull()) return new App::DocumentObjectExecReturn("Creating a face from sketch failed"); - // lengthen the vector - SketchOrientationVector *= L; - this->positionBySketch(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); try { // extrude the face to a solid - gp_Vec vec(SketchOrientationVector.x,SketchOrientationVector.y,SketchOrientationVector.z); - vec.Transform(invObjLoc.Transformation()); - BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); - if (PrismMaker.IsDone()) { - // if the sketch has a support fuse them to get one result object (PAD!) - if (SupportObject) { - // At this point the prism can be a compound - TopoDS_Shape result = PrismMaker.Shape(); - // set the additive shape property for later usage in e.g. pattern - this->AddShape.setValue(result); + TopoDS_Shape prism; + bool isSolid = false; // support is a solid? + bool isSolidChecked = false; // not checked yet + + if ((std::string(Type.getValueAsString()) == "UpToLast") || + (std::string(Type.getValueAsString()) == "UpToFirst") || + (std::string(Type.getValueAsString()) == "UpToFace")) + { + TopoDS_Face upToFace; + gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z); + + if ((std::string(Type.getValueAsString()) == "UpToLast") || + (std::string(Type.getValueAsString()) == "UpToFirst")) + { + // Check for valid support object + if (!SupportObject) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No support in Sketch!"); const TopoDS_Shape& support = SupportObject->Shape.getValue(); - bool isSolid = false; + if (support.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Support shape is invalid"); + TopExp_Explorer xp (support, TopAbs_SOLID); + if (!xp.More()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Support shape is not a solid"); + isSolid = true; + isSolidChecked = true; + + TopoDS_Shape origFace = makeFace(wires); // original sketch face before moving one unit + std::vector cfaces = Part::findAllFacesCutBy(support, origFace, dir); + if (cfaces.empty()) + return new App::DocumentObjectExecReturn("No faces found in this direction"); + + // Find nearest/furthest face + std::vector::const_iterator it, it_near, it_far; + it_near = it_far = cfaces.begin(); + for (it = cfaces.begin(); it != cfaces.end(); it++) + if (it->distsq > it_far->distsq) + it_far = it; + else if (it->distsq < it_near->distsq) + it_near = it; + upToFace = (std::string(Type.getValueAsString()) == "UpToLast" ? it_far->face : it_near->face); + } else { + if (FaceName.isEmpty()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No face selected"); + + // Get active object, this is the object that the user referenced when he clicked on the face! + App::DocumentObject* baseLink = this->getDocument()->getActiveObject(); + + if (!baseLink) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No object linked"); + if (!baseLink->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Linked object is not a Part object"); + Part::Feature *base = static_cast(baseLink); + const Part::TopoShape& baseShape = base->Shape.getShape(); + if (baseShape._Shape.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Cannot work on invalid shape"); + + TopoDS_Shape sub = baseShape.getSubShape(FaceName.getValue()); + if (!sub.IsNull() && sub.ShapeType() == TopAbs_FACE) + upToFace = TopoDS::Face(sub); + else + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Selection is not a face"); + + // Validate face + // TODO: This would also exclude faces that are valid but not cut by the line + // So for now we trust to the intelligence of the user when picking the face + /*std::vector cfaces = findAllFacesCutBy(upToFace, origFace, dir); + if (cfaces.empty()) + return new App::DocumentObjectExecReturn("No faces found in this direction");*/ + } + + // Create semi-infinite prism from sketch in direction dir + // Hack, because the two lines commented out below do NOT work!!! + SketchVector *= 1E6; + gp_Vec vec(SketchVector.x,SketchVector.y,SketchVector.z); + vec.Transform(invObjLoc.Transformation()); + BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); // very long, but finite prism + //dir.Transform(invObjLoc.Transformation()); + //BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),dir,0,0,1); + if (!PrismMaker.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Could not extrude the sketch!"); + + // Cut off the prism at the face we found + // Grab any point from the sketch + TopExp_Explorer exp; + exp.Init(aFace, TopAbs_VERTEX); + if (!exp.More()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Sketch without points?"); + gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current())); + + // Create a halfspace from the face, extending in direction of sketch plane + BRepPrimAPI_MakeHalfSpace mkHalfSpace(upToFace, aPnt); + if (!mkHalfSpace.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: HalfSpace creation failed"); + + // Find common material between halfspace and prism + BRepAlgoAPI_Common mkCommon(PrismMaker.Shape(), mkHalfSpace.Solid().Moved(invObjLoc)); + if (!mkCommon.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Common creation failed"); + + prism = this->getSolid(mkCommon.Shape()); + if (prism.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Resulting shape is not a solid"); + } else if ((std::string(Type.getValueAsString()) == "Length") || + (std::string(Type.getValueAsString()) == "TwoLengths")) { + if (std::string(Type.getValueAsString()) == "Length") { + if (Midplane.getValue()) { + // Move face by half the extrusion distance to get pad symmetric to sketch plane + gp_Trsf mov; + mov.SetTranslation(gp_Vec(SketchVector.x,SketchVector.y,SketchVector.z) * (-1.0) * L/2.0); + TopLoc_Location loc(mov); + aFace.Move(loc); + } else if (Reversed.getValue()) { // negative direction + SketchVector *= -1.0; + } + + // lengthen the vector + SketchVector *= L; + } else { + // Move face by the second length to get pad extending to both sides of sketch plane + gp_Trsf mov; + mov.SetTranslation(gp_Vec(SketchVector.x,SketchVector.y,SketchVector.z) * (-1.0) * L2); + TopLoc_Location loc(mov); + aFace.Move(loc); + + // lengthen the vector + SketchVector *= (L + L2); + } + + // create the extrusion + gp_Vec vec(SketchVector.x,SketchVector.y,SketchVector.z); + vec.Transform(invObjLoc.Transformation()); + BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); // finite prism + if (!PrismMaker.IsDone()) + return new App::DocumentObjectExecReturn("Could not extrude the sketch!"); + prism = PrismMaker.Shape(); + } else { + return new App::DocumentObjectExecReturn("Internal error: Unknown type for Pad feature"); + } + + // if the sketch has a support fuse them to get one result object (PAD!) + if (SupportObject) { + // set the additive shape property for later usage in e.g. pattern + this->AddShape.setValue(prism); + + const TopoDS_Shape& support = SupportObject->Shape.getValue(); + + if (!isSolidChecked) { // we haven't checked for solid, yet if (!support.IsNull()) { TopExp_Explorer xp; xp.Init(support,TopAbs_SOLID); @@ -147,32 +290,30 @@ App::DocumentObjectExecReturn *Pad::execute(void) break; } } - if (isSolid) { - // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Fuse mkFuse(support.Moved(invObjLoc), result); - // Let's check if the fusion has been successful - if (!mkFuse.IsDone()) - return new App::DocumentObjectExecReturn("Fusion with support failed"); - result = mkFuse.Shape(); - // we have to get the solids (fuse create seldomly compounds) - TopoDS_Shape solRes = this->getSolid(result); - // lets check if the result is a solid - if (solRes.IsNull()) - return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); - this->Shape.setValue(solRes); - } - else + + if (!isSolid) return new App::DocumentObjectExecReturn("Support is not a solid"); } - else { - TopoDS_Shape result = this->getSolid(PrismMaker.Shape()); - // set the additive shape property for later usage in e.g. pattern - this->AddShape.setValue(result); - this->Shape.setValue(result); - } + + // Let's call algorithm computing a fuse operation: + BRepAlgoAPI_Fuse mkFuse(support.Moved(invObjLoc), prism); + // Let's check if the fusion has been successful + if (!mkFuse.IsDone()) + return new App::DocumentObjectExecReturn("Fusion with support failed"); + TopoDS_Shape result = mkFuse.Shape(); + // we have to get the solids (fuse create seldomly compounds) + TopoDS_Shape solRes = this->getSolid(result); + // lets check if the result is a solid + if (solRes.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); + this->Shape.setValue(solRes); + } + else { + TopoDS_Shape result = this->getSolid(prism); + // set the additive shape property for later usage in e.g. pattern + this->AddShape.setValue(result); + this->Shape.setValue(result); } - else - return new App::DocumentObjectExecReturn("Could not extrude the sketch!"); return App::DocumentObject::StdReturn; } diff --git a/src/Mod/PartDesign/App/FeaturePad.h b/src/Mod/PartDesign/App/FeaturePad.h index c758c94f7..91bdeff59 100644 --- a/src/Mod/PartDesign/App/FeaturePad.h +++ b/src/Mod/PartDesign/App/FeaturePad.h @@ -38,10 +38,13 @@ class Pad : public Additive public: Pad(); - App::PropertyLength Length; + App::PropertyEnumeration Type; + App::PropertyLength Length; //App::PropertyEnumeration Side; - App::PropertyBool Reversed; - App::PropertyBool MirroredExtent; + App::PropertyBool Reversed; + App::PropertyBool Midplane; + App::PropertyLength Length2; + App::PropertyString FaceName; /** @name methods override feature */ //@{ @@ -54,7 +57,8 @@ public: } //@} private: - static const char* SideEnums[]; + static const char* TypeEnums[]; + //static const char* SideEnums[]; }; } //namespace PartDesign diff --git a/src/Mod/PartDesign/App/FeaturePocket.cpp b/src/Mod/PartDesign/App/FeaturePocket.cpp index c7f4fa8ac..a0ef1cdd4 100644 --- a/src/Mod/PartDesign/App/FeaturePocket.cpp +++ b/src/Mod/PartDesign/App/FeaturePocket.cpp @@ -27,6 +27,7 @@ # include # include # include +# include # include # include # include @@ -40,17 +41,20 @@ # include # include # include +# include +# include #endif #include #include +#include #include "FeaturePocket.h" using namespace PartDesign; -const char* Pocket::TypeEnums[]= {"Length","UpToLast","UpToFirst",NULL}; +const char* Pocket::TypeEnums[]= {"Length","UpToLast","UpToFirst","ThroughAll","UpToFace",NULL}; PROPERTY_SOURCE(PartDesign::Pocket, PartDesign::SketchBased) @@ -59,13 +63,15 @@ Pocket::Pocket() ADD_PROPERTY(Type,((long)0)); Type.setEnums(TypeEnums); ADD_PROPERTY(Length,(100.0)); + ADD_PROPERTY(FaceName,("")); } short Pocket::mustExecute() const { if (Placement.isTouched() || Sketch.isTouched() || - Length.isTouched()) + Length.isTouched() || + FaceName.isTouched()) return 1; return 0; } @@ -113,13 +119,22 @@ App::DocumentObjectExecReturn *Pocket::execute(void) if (!SupportObject) return new App::DocumentObjectExecReturn("No support in Sketch!"); + const TopoDS_Shape& support = SupportObject->Shape.getValue(); + if (support.IsNull()) + return new App::DocumentObjectExecReturn("Support shape is invalid"); + TopExp_Explorer xp (support, TopAbs_SOLID); + if (!xp.More()) + return new App::DocumentObjectExecReturn("Support shape is not a solid"); + TopoDS_Shape aFace = makeFace(wires); if (aFace.IsNull()) return new App::DocumentObjectExecReturn("Creating a face from sketch failed"); // This is a trick to avoid problems with the cut operation. Sometimes a cut doesn't - // work as expected if faces or coincident. Thus, we move the face in normal direction + // work as expected if faces are coincident. Thus, we move the face in normal direction // but make it longer by one unit in the opposite direction. + // TODO: Isn't one unit (one millimeter) a lot, assuming someone models a really tiny solid? + // What about using 2 * Precision::Confusion() ? gp_Trsf mov; mov.SetTranslation(gp_Vec(SketchVector.x,SketchVector.y,SketchVector.z)); TopLoc_Location loc(mov); @@ -134,39 +149,133 @@ App::DocumentObjectExecReturn *Pocket::execute(void) this->positionBySketch(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); - // extrude the face to a solid - gp_Vec vec(SketchVector.x,SketchVector.y,SketchVector.z); - vec.Transform(invObjLoc.Transformation()); - BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); - if (PrismMaker.IsDone()) { - // if the sketch has a support fuse them to get one result object (PAD!) - if (SupportObject) { - const TopoDS_Shape& support = SupportObject->Shape.getValue(); - if (support.IsNull()) - return new App::DocumentObjectExecReturn("Support shape is invalid"); - TopExp_Explorer xp (support, TopAbs_SOLID); - if (!xp.More()) - return new App::DocumentObjectExecReturn("Support shape is not a solid"); - // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Cut mkCut(support.Moved(invObjLoc), PrismMaker.Shape()); - // Let's check if the fusion has been successful - if (!mkCut.IsDone()) - return new App::DocumentObjectExecReturn("Cut with support failed"); + try { + // extrude the face to a solid + TopoDS_Shape prism; - // we have to get the solids (fuse create seldomly compounds) - TopoDS_Shape solRes = this->getSolid(mkCut.Shape()); - if (solRes.IsNull()) - return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); + if ((std::string(Type.getValueAsString()) == "UpToLast") || + (std::string(Type.getValueAsString()) == "UpToFirst") || + (std::string(Type.getValueAsString()) == "UpToFace")) + { + TopoDS_Face upToFace; + gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z); - this->Shape.setValue(solRes); - } - else { - return new App::DocumentObjectExecReturn("Cannot create a tool out of sketch with no support"); + if ((std::string(Type.getValueAsString()) == "UpToLast") || + (std::string(Type.getValueAsString()) == "UpToFirst")) + { + TopoDS_Shape origFace = makeFace(wires); // original sketch face before moving one unit + std::vector cfaces = Part::findAllFacesCutBy(support, origFace, dir); + if (cfaces.empty()) + return new App::DocumentObjectExecReturn("No faces found in this direction"); + + // Find nearest/furthest face + std::vector::const_iterator it, it_near, it_far; + it_near = it_far = cfaces.begin(); + for (it = cfaces.begin(); it != cfaces.end(); it++) + if (it->distsq > it_far->distsq) + it_far = it; + else if (it->distsq < it_near->distsq) + it_near = it; + upToFace = (std::string(Type.getValueAsString()) == "UpToLast" ? it_far->face : it_near->face); + } else { + if (FaceName.getValue() == "") + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No face selected"); + + // Get active object, this is the object that the user referenced when he clicked on the face! + App::DocumentObject* baseLink = this->getDocument()->getActiveObject(); + + if (!baseLink) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: No object linked"); + if (!baseLink->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Linked object is not a Part object"); + Part::Feature *base = static_cast(baseLink); + const Part::TopoShape& baseShape = base->Shape.getShape(); + if (baseShape._Shape.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Cannot work on invalid shape"); + + TopoDS_Shape sub = baseShape.getSubShape(FaceName.getValue()); + if (!sub.IsNull() && sub.ShapeType() == TopAbs_FACE) + upToFace = TopoDS::Face(sub); + else + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Selection is not a face"); + + // Find the origin of this face (i.e. a vertex or a edge in a sketch) + TopoDS_Shape origin = base->findOriginOf(sub); + + // Validate face + // TODO: This would also exclude faces that are valid but not cut by the line + // So for now we trust to the intelligence of the user when picking the face + /*std::vector cfaces = findAllFacesCutBy(upToFace, origFace, dir); + if (cfaces.empty()) + return new App::DocumentObjectExecReturn("No faces found in this direction");*/ + } + + // Create semi-infinite prism from sketch in direction dir + dir.Transform(invObjLoc.Transformation()); + BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),dir,0,0,1); + if (!PrismMaker.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Could not extrude the sketch!"); + + // Cut off the prism at the face we found + // Grab any point from the sketch + TopExp_Explorer exp; + exp.Init(aFace, TopAbs_VERTEX); + if (!exp.More()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Sketch without points?"); + gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current())); + + // Create a halfspace from the face, extending in direction of sketch plane + BRepPrimAPI_MakeHalfSpace mkHalfSpace(upToFace, aPnt); + if (!mkHalfSpace.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: HalfSpace creation failed"); + + // Find common material between halfspace and prism + BRepAlgoAPI_Common mkCommon(PrismMaker.Shape(), mkHalfSpace.Solid().Moved(invObjLoc)); + if (!mkCommon.IsDone()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Common creation failed"); + + prism = this->getSolid(mkCommon.Shape()); + if (prism.IsNull()) + return new App::DocumentObjectExecReturn("Cannot extrude up to face: Resulting shape is not a solid"); + } else if (std::string(Type.getValueAsString()) == "ThroughAll") { + gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z); + dir.Transform(invObjLoc.Transformation()); + BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),dir,1,0,1); // infinite prism (in both directions!) + if (!PrismMaker.IsDone()) + return new App::DocumentObjectExecReturn("Could not extrude the sketch!"); + prism = PrismMaker.Shape(); + } else if (std::string(Type.getValueAsString()) == "Length") { + gp_Vec vec(SketchVector.x,SketchVector.y,SketchVector.z); + vec.Transform(invObjLoc.Transformation()); + BRepPrimAPI_MakePrism PrismMaker(aFace.Moved(invObjLoc),vec,0,1); // finite prism + if (!PrismMaker.IsDone()) + return new App::DocumentObjectExecReturn("Could not extrude the sketch!"); + prism = PrismMaker.Shape(); + } else { + return new App::DocumentObjectExecReturn("Internal error: Unknown type for Pocket feature"); } + + // TODO: Set the subtractive shape property for later usage in e.g. pattern + //this->SubShape.setValue(prism); // This crashes with "Illegal storage access". Why? + + // Cut out the pocket + BRepAlgoAPI_Cut mkCut(support.Moved(invObjLoc), prism); + + // Let's check if the fusion has been successful + if (!mkCut.IsDone()) + return new App::DocumentObjectExecReturn("Cut with support failed"); + + // we have to get the solids (fuse sometimes creates compounds) + TopoDS_Shape solRes = this->getSolid(mkCut.Shape()); + if (solRes.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); + + this->Shape.setValue(solRes); + + return App::DocumentObject::StdReturn; + } catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); } - else - return new App::DocumentObjectExecReturn("Could not extrude the sketch!"); - - return App::DocumentObject::StdReturn; } diff --git a/src/Mod/PartDesign/App/FeaturePocket.h b/src/Mod/PartDesign/App/FeaturePocket.h index 7a7d57962..b9b47aa72 100644 --- a/src/Mod/PartDesign/App/FeaturePocket.h +++ b/src/Mod/PartDesign/App/FeaturePocket.h @@ -39,6 +39,7 @@ public: App::PropertyEnumeration Type; App::PropertyLength Length; + App::PropertyString FaceName; /** @name methods override feature */ //@{ diff --git a/src/Mod/PartDesign/App/FeatureRevolution.cpp b/src/Mod/PartDesign/App/FeatureRevolution.cpp index 317aa52d1..a31bebf43 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.cpp +++ b/src/Mod/PartDesign/App/FeatureRevolution.cpp @@ -52,10 +52,12 @@ PROPERTY_SOURCE(PartDesign::Revolution, PartDesign::SketchBased) Revolution::Revolution() { - ADD_PROPERTY(Base,(Base::Vector3f(0.0f,0.0f,0.0f))); - ADD_PROPERTY(Axis,(Base::Vector3f(0.0f,1.0f,0.0f))); - ADD_PROPERTY(Angle,(360.0)); - ADD_PROPERTY_TYPE(ReferenceAxis,(0),"Revolution",(App::PropertyType)(App::Prop_None),"Reference axis of revolution"); + ADD_PROPERTY_TYPE(Base,(Base::Vector3f(0.0f,0.0f,0.0f)),"Revolution", App::Prop_ReadOnly, "Base"); + ADD_PROPERTY_TYPE(Axis,(Base::Vector3f(0.0f,1.0f,0.0f)),"Revolution", App::Prop_ReadOnly, "Axis"); + ADD_PROPERTY_TYPE(Angle,(360.0),"Revolution", App::Prop_None, "Angle"); + ADD_PROPERTY_TYPE(ReferenceAxis,(0),"Revolution",(App::Prop_None),"Reference axis of revolution"); + ADD_PROPERTY_TYPE(Midplane,(0),"Revolution", App::Prop_None, "Mid plane"); + ADD_PROPERTY_TYPE(Reversed, (0),"Revolution", App::Prop_None, "Reversed"); } short Revolution::mustExecute() const @@ -65,7 +67,9 @@ short Revolution::mustExecute() const ReferenceAxis.isTouched() || Axis.isTouched() || Base.isTouched() || - Angle.isTouched()) + Angle.isTouched() || + Midplane.isTouched() || + Reversed.isTouched()) return 1; return 0; } @@ -151,35 +155,54 @@ App::DocumentObjectExecReturn *Revolution::execute(void) if (aFace.IsNull()) return new App::DocumentObjectExecReturn("Creating a face from sketch failed"); + // Rotate the face by half the angle to get revolution symmetric to sketch plane + if (Midplane.getValue()) { + gp_Trsf mov; + mov.SetRotation(gp_Ax1(pnt, dir), Base::toRadians(Angle.getValue()) * (-1.0) / 2.0); + TopLoc_Location loc(mov); + aFace.Move(loc); + } + this->positionBySketch(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); pnt.Transform(invObjLoc.Transformation()); dir.Transform(invObjLoc.Transformation()); - // revolve the face to a solid - BRepPrimAPI_MakeRevol RevolMaker(aFace.Moved(invObjLoc), gp_Ax1(pnt, dir), Base::toRadians(Angle.getValue())); + // Reverse angle if selected + double angle = Base::toRadians(Angle.getValue()); + if (Reversed.getValue() && !Midplane.getValue()) + angle *= (-1.0); - if (RevolMaker.IsDone()) { - TopoDS_Shape result = RevolMaker.Shape(); - // if the sketch has a support fuse them to get one result object (PAD!) - if (SupportObject) { - const TopoDS_Shape& support = SupportObject->Shape.getValue(); - if (!support.IsNull() && support.ShapeType() == TopAbs_SOLID) { - // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Fuse mkFuse(support.Moved(invObjLoc), result); - // Let's check if the fusion has been successful - if (!mkFuse.IsDone()) - throw Base::Exception("Fusion with support failed"); - result = mkFuse.Shape(); + try { + // revolve the face to a solid + BRepPrimAPI_MakeRevol RevolMaker(aFace.Moved(invObjLoc), gp_Ax1(pnt, dir), angle); + + if (RevolMaker.IsDone()) { + TopoDS_Shape result = RevolMaker.Shape(); + // if the sketch has a support fuse them to get one result object (PAD!) + if (SupportObject) { + const TopoDS_Shape& support = SupportObject->Shape.getValue(); + if (!support.IsNull() && support.ShapeType() == TopAbs_SOLID) { + // Let's call algorithm computing a fuse operation: + BRepAlgoAPI_Fuse mkFuse(support.Moved(invObjLoc), result); + // Let's check if the fusion has been successful + if (!mkFuse.IsDone()) + throw Base::Exception("Fusion with support failed"); + result = mkFuse.Shape(); + } } + + this->Shape.setValue(result); } + else + return new App::DocumentObjectExecReturn("Could not revolve the sketch!"); - this->Shape.setValue(result); + return App::DocumentObject::StdReturn; + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); } - else - return new App::DocumentObjectExecReturn("Could not revolve the sketch!"); - - return App::DocumentObject::StdReturn; } } diff --git a/src/Mod/PartDesign/App/FeatureRevolution.h b/src/Mod/PartDesign/App/FeatureRevolution.h index 4d6677d54..ed11adc06 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.h +++ b/src/Mod/PartDesign/App/FeatureRevolution.h @@ -39,7 +39,9 @@ public: App::PropertyVector Base; App::PropertyVector Axis; - App::PropertyAngle Angle; + App::PropertyAngle Angle; + App::PropertyBool Midplane; + App::PropertyBool Reversed; /** if this property is set to a valid link, both Axis and Base properties * are calculated according to the linked line diff --git a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp index d86285fbe..32e718778 100644 --- a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp +++ b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp @@ -37,6 +37,7 @@ #include "ViewProviderChamfer.h" #include "ViewProviderFillet.h" #include "ViewProviderRevolution.h" +#include "ViewProviderGroove.h" //#include "resources/qrc_PartDesign.cpp" @@ -83,6 +84,7 @@ void PartDesignGuiExport initPartDesignGui() PartDesignGui::ViewProviderPocket ::init(); PartDesignGui::ViewProviderPad ::init(); PartDesignGui::ViewProviderRevolution::init(); + PartDesignGui::ViewProviderGroove ::init(); PartDesignGui::ViewProviderChamfer ::init(); PartDesignGui::ViewProviderFillet ::init(); diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index 8f6e26556..0aa1cb918 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -32,6 +32,7 @@ set(PartDesignGui_MOC_HDRS TaskFilletParameters.h TaskHoleParameters.h TaskRevolutionParameters.h + TaskGrooveParameters.h ) fc_wrap_cpp(PartDesignGui_MOC_SRCS ${PartDesignGui_MOC_HDRS}) SOURCE_GROUP("Moc" FILES ${PartDesignGui_MOC_SRCS}) @@ -46,6 +47,7 @@ set(PartDesignGui_UIC_SRCS TaskFilletParameters.ui TaskHoleParameters.ui TaskRevolutionParameters.ui + TaskGrooveParameters.ui ) qt4_wrap_ui(PartDesignGui_UIC_HDRS ${PartDesignGui_UIC_SRCS}) @@ -64,6 +66,8 @@ SET(PartDesignGuiViewProvider_SRCS ViewProviderFillet.h ViewProviderRevolution.cpp ViewProviderRevolution.h + ViewProviderGroove.cpp + ViewProviderGroove.h ViewProviderPatternRectangular.cpp ViewProviderPatternRectangular.h ) @@ -88,6 +92,9 @@ SET(PartDesignGuiTaskDlgs_SRCS TaskRevolutionParameters.ui TaskRevolutionParameters.cpp TaskRevolutionParameters.h + TaskGrooveParameters.ui + TaskGrooveParameters.cpp + TaskGrooveParameters.h TaskHoleParameters.ui TaskHoleParameters.cpp TaskHoleParameters.h diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 37e02dc6a..335d6d5dd 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -327,6 +327,82 @@ bool CmdPartDesignRevolution::isActive(void) return hasActiveDocument(); } +//=========================================================================== +// PartDesign_Groove +//=========================================================================== +DEF_STD_CMD_A(CmdPartDesignGroove); + +CmdPartDesignGroove::CmdPartDesignGroove() + : Command("PartDesign_Groove") +{ + sAppModule = "PartDesign"; + sGroup = QT_TR_NOOP("PartDesign"); + sMenuText = QT_TR_NOOP("Groove"); + sToolTipText = QT_TR_NOOP("Groove a selected sketch"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "PartDesign_Groove"; +} + +void CmdPartDesignGroove::activated(int iMsg) +{ + unsigned int n = getSelection().countObjectsOfType(Part::Part2DObject::getClassTypeId()); + if (n != 1) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Select a sketch or 2D object.")); + return; + } + + std::string FeatName = getUniqueObjectName("Groove"); + + std::vector Sel = getSelection().getObjectsOfType(Part::Part2DObject::getClassTypeId()); + Part::Part2DObject* sketch = static_cast(Sel.front()); + const TopoDS_Shape& shape = sketch->Shape.getValue(); + if (shape.IsNull()) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("The shape of the selected object is empty.")); + return; + } + + // count free wires + int ctWires=0; + TopExp_Explorer ex; + for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next()) { + ctWires++; + } + if (ctWires == 0) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("The shape of the selected object is not a wire.")); + return; + } + + App::DocumentObject* support = sketch->Support.getValue(); + + openCommand("Make Groove"); + doCommand(Doc,"App.activeDocument().addObject(\"PartDesign::Groove\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Sketch = App.activeDocument().%s",FeatName.c_str(),sketch->getNameInDocument()); + doCommand(Doc,"App.activeDocument().%s.ReferenceAxis = (App.activeDocument().%s,['V_Axis'])", + FeatName.c_str(), sketch->getNameInDocument()); + doCommand(Doc,"App.activeDocument().%s.Angle = 360.0",FeatName.c_str()); + updateActive(); + if (isActiveObjectValid()) { + doCommand(Gui,"Gui.activeDocument().hide(\"%s\")",sketch->getNameInDocument()); + if (support) + doCommand(Gui,"Gui.activeDocument().hide(\"%s\")",support->getNameInDocument()); + } + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); + + if (support) { + copyVisual(FeatName.c_str(), "ShapeColor", support->getNameInDocument()); + copyVisual(FeatName.c_str(), "LineColor", support->getNameInDocument()); + copyVisual(FeatName.c_str(), "PointColor", support->getNameInDocument()); + } +} + +bool CmdPartDesignGroove::isActive(void) +{ + return hasActiveDocument(); +} //=========================================================================== // PartDesign_Fillet //=========================================================================== @@ -628,6 +704,7 @@ void CreatePartDesignCommands(void) rcCmdMgr.addCommand(new CmdPartDesignPad()); rcCmdMgr.addCommand(new CmdPartDesignPocket()); rcCmdMgr.addCommand(new CmdPartDesignRevolution()); + rcCmdMgr.addCommand(new CmdPartDesignGroove()); rcCmdMgr.addCommand(new CmdPartDesignFillet()); //rcCmdMgr.addCommand(new CmdPartDesignNewSketch()); rcCmdMgr.addCommand(new CmdPartDesignChamfer()); diff --git a/src/Mod/PartDesign/Gui/Resources/Makefile.am b/src/Mod/PartDesign/Gui/Resources/Makefile.am index 332007335..79bee0639 100644 --- a/src/Mod/PartDesign/Gui/Resources/Makefile.am +++ b/src/Mod/PartDesign/Gui/Resources/Makefile.am @@ -37,6 +37,7 @@ EXTRA_DIST = \ translations/PartDesign_uk.ts \ translations/PartDesign_zh.qm \ translations/PartDesign_zh.ts \ + icons/PartDesign_Groove.svg \ icons/PartDesign_Pad.svg \ icons/PartDesign_Pocket.svg \ icons/PartDesign_Revolution.svg \ diff --git a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc index caff25cab..37804e403 100644 --- a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc +++ b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc @@ -1,5 +1,6 @@ + icons/PartDesign_Groove.svg icons/PartDesign_Pad.svg icons/PartDesign_Pocket.svg icons/PartDesign_Revolution.svg diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Groove.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Groove.svg new file mode 100644 index 000000000..2c1ca60aa --- /dev/null +++ b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Groove.svg @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/PartDesign/Gui/TaskGrooveParameters.cpp b/src/Mod/PartDesign/Gui/TaskGrooveParameters.cpp new file mode 100644 index 000000000..79b0278fe --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskGrooveParameters.cpp @@ -0,0 +1,287 @@ +/*************************************************************************** + * Copyright (c) 2011 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "ui_TaskGrooveParameters.h" +#include "TaskGrooveParameters.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace PartDesignGui; +using namespace Gui; + +/* TRANSLATOR PartDesignGui::TaskGrooveParameters */ + +TaskGrooveParameters::TaskGrooveParameters(ViewProviderGroove *GrooveView,QWidget *parent) + : TaskBox(Gui::BitmapFactory().pixmap("PartDesign_Groove"),tr("Groove parameters"),true, parent),GrooveView(GrooveView) +{ + // we need a separate container widget to add all controls to + proxy = new QWidget(this); + ui = new Ui_TaskGrooveParameters(); + ui->setupUi(proxy); + QMetaObject::connectSlotsByName(this); + + connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(onAngleChanged(double))); + connect(ui->axis, SIGNAL(activated(int)), + this, SLOT(onAxisChanged(int))); + connect(ui->checkBoxMidplane, SIGNAL(toggled(bool)), + this, SLOT(onMidplane(bool))); + connect(ui->checkBoxReversed, SIGNAL(toggled(bool)), + this, SLOT(onReversed(bool))); + + this->groupLayout()->addWidget(proxy); + + PartDesign::Groove* pcGroove = static_cast(GrooveView->getObject()); + double l = pcGroove->Angle.getValue(); + bool mirrored = pcGroove->Midplane.getValue(); + bool reversed = pcGroove->Reversed.getValue(); + + ui->doubleSpinBox->setValue(l); + + int count=pcGroove->getSketchAxisCount(); + + for (int i=ui->axis->count()-1; i >= count+2; i--) + ui->axis->removeItem(i); + for (int i=ui->axis->count(); i < count+2; i++) + ui->axis->addItem(QString::fromAscii("Sketch axis %1").arg(i-2)); + + int pos=-1; + + App::DocumentObject *pcReferenceAxis = pcGroove->ReferenceAxis.getValue(); + const std::vector &subReferenceAxis = pcGroove->ReferenceAxis.getSubValues(); + if (pcReferenceAxis && pcReferenceAxis == pcGroove->Sketch.getValue()) { + assert(subReferenceAxis.size()==1); + if (subReferenceAxis[0] == "V_Axis") + pos = 0; + else if (subReferenceAxis[0] == "H_Axis") + pos = 1; + else if (subReferenceAxis[0].size() > 4 && subReferenceAxis[0].substr(0,4) == "Axis") + pos = 2 + std::atoi(subReferenceAxis[0].substr(4,4000).c_str()); + } + + if (pos < 0 || pos >= ui->axis->count()) { + ui->axis->addItem(QString::fromAscii("Undefined")); + pos = ui->axis->count()-1; + } + + ui->axis->setCurrentIndex(pos); + + ui->checkBoxMidplane->setChecked(mirrored); + ui->checkBoxReversed->setChecked(reversed); + + setFocus (); +} + +void TaskGrooveParameters::onAngleChanged(double len) +{ + PartDesign::Groove* pcGroove = static_cast(GrooveView->getObject()); + pcGroove->Angle.setValue((float)len); + pcGroove->getDocument()->recomputeFeature(pcGroove); +} + +void TaskGrooveParameters::onAxisChanged(int num) +{ + PartDesign::Groove* pcGroove = static_cast(GrooveView->getObject()); + Sketcher::SketchObject *pcSketch = static_cast(pcGroove->Sketch.getValue()); + if (pcSketch) { + int maxcount = pcSketch->getAxisCount()+2; + if (num == 0) + pcGroove->ReferenceAxis.setValue(pcSketch, std::vector(1,"V_Axis")); + else if (num == 1) + pcGroove->ReferenceAxis.setValue(pcSketch, std::vector(1,"H_Axis")); + else if (num >= 2 && num < maxcount) { + QString buf = QString::fromUtf8("Axis%1").arg(num-2); + std::string str = buf.toStdString(); + pcGroove->ReferenceAxis.setValue(pcSketch, std::vector(1,str)); + } + if (num < maxcount && ui->axis->count() > maxcount) + ui->axis->setMaxCount(maxcount); + } + pcGroove->getDocument()->recomputeFeature(pcGroove); +} + +void TaskGrooveParameters::onMidplane(bool on) +{ + PartDesign::Groove* pcGroove = static_cast(GrooveView->getObject()); + pcGroove->Midplane.setValue(on); + pcGroove->getDocument()->recomputeFeature(pcGroove); +} + +void TaskGrooveParameters::onReversed(bool on) +{ + PartDesign::Groove* pcGroove = static_cast(GrooveView->getObject()); + pcGroove->Reversed.setValue(on); + pcGroove->getDocument()->recomputeFeature(pcGroove); +} + + +double TaskGrooveParameters::getAngle(void) const +{ + return ui->doubleSpinBox->value(); +} + +QString TaskGrooveParameters::getReferenceAxis(void) const +{ + // get the support and Sketch + PartDesign::Groove* pcGroove = static_cast(GrooveView->getObject()); + Sketcher::SketchObject *pcSketch = static_cast(pcGroove->Sketch.getValue()); + + QString buf; + if (pcSketch) { + buf = QString::fromUtf8("(App.ActiveDocument.%1,[%2])"); + buf = buf.arg(QString::fromUtf8(pcSketch->getNameInDocument())); + if (ui->axis->currentIndex() == 0) + buf = buf.arg(QString::fromUtf8("'V_Axis'")); + else if (ui->axis->currentIndex() == 1) + buf = buf.arg(QString::fromUtf8("'H_Axis'")); + else if (ui->axis->currentIndex() >= 2) { + buf = buf.arg(QString::fromUtf8("'Axis%1'")); + buf = buf.arg(ui->axis->currentIndex()-2); + } + } + else + buf = QString::fromUtf8("''"); + + return buf; +} + +bool TaskGrooveParameters::getMidplane(void) const +{ + return ui->checkBoxMidplane->isChecked(); +} + +bool TaskGrooveParameters::getReversed(void) const +{ + return ui->checkBoxReversed->isChecked(); +} + +TaskGrooveParameters::~TaskGrooveParameters() +{ + delete ui; +} + +void TaskGrooveParameters::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(proxy); + } +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgGrooveParameters::TaskDlgGrooveParameters(ViewProviderGroove *GrooveView) + : TaskDialog(),GrooveView(GrooveView) +{ + assert(GrooveView); + parameter = new TaskGrooveParameters(GrooveView); + + Content.push_back(parameter); +} + +TaskDlgGrooveParameters::~TaskDlgGrooveParameters() +{ + +} + +//==== calls from the TaskView =============================================================== + + +void TaskDlgGrooveParameters::open() +{ + +} + +void TaskDlgGrooveParameters::clicked(int) +{ + +} + +bool TaskDlgGrooveParameters::accept() +{ + std::string name = GrooveView->getObject()->getNameInDocument(); + + //Gui::Command::openCommand("Groove changed"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Angle = %f",name.c_str(),parameter->getAngle()); + std::string axis = parameter->getReferenceAxis().toStdString(); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.ReferenceAxis = %s",name.c_str(),axis.c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Midplane = %i",name.c_str(),parameter->getMidplane()?1:0); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %i",name.c_str(),parameter->getReversed()?1:0); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + Gui::Command::commitCommand(); + + return true; +} + +bool TaskDlgGrooveParameters::reject() +{ + // get the support and Sketch + PartDesign::Groove* pcGroove = static_cast(GrooveView->getObject()); + Sketcher::SketchObject *pcSketch; + App::DocumentObject *pcSupport; + if (pcGroove->Sketch.getValue()) { + pcSketch = static_cast(pcGroove->Sketch.getValue()); + pcSupport = pcSketch->Support.getValue(); + } + + // role back the done things + Gui::Command::abortCommand(); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + + // if abort command deleted the object the support is visible again + if (!Gui::Application::Instance->getViewProvider(pcGroove)) { + if (pcSketch && Gui::Application::Instance->getViewProvider(pcSketch)) + Gui::Application::Instance->getViewProvider(pcSketch)->show(); + if (pcSupport && Gui::Application::Instance->getViewProvider(pcSupport)) + Gui::Application::Instance->getViewProvider(pcSupport)->show(); + } + + //Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); + //Gui::Command::commitCommand(); + + return true; +} + + + +#include "moc_TaskGrooveParameters.cpp" diff --git a/src/Mod/PartDesign/Gui/TaskGrooveParameters.h b/src/Mod/PartDesign/Gui/TaskGrooveParameters.h new file mode 100644 index 000000000..8b82f0187 --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskGrooveParameters.h @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright (c) 2011 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef GUI_TASKVIEW_TaskGrooveParameters_H +#define GUI_TASKVIEW_TaskGrooveParameters_H + +#include +#include +#include + +#include "ViewProviderGroove.h" + +class Ui_TaskGrooveParameters; + +namespace App { +class Property; +} + +namespace Gui { +class ViewProvider; +} + +namespace PartDesignGui { + + + +class TaskGrooveParameters : public Gui::TaskView::TaskBox +{ + Q_OBJECT + +public: + TaskGrooveParameters(ViewProviderGroove *GrooveView,QWidget *parent = 0); + ~TaskGrooveParameters(); + + QString getReferenceAxis(void) const; + double getAngle(void) const; + bool getMidplane(void) const; + bool getReversed(void) const; + +private Q_SLOTS: + void onAngleChanged(double); + void onAxisChanged(int); + void onMidplane(bool); + void onReversed(bool); + +protected: + void changeEvent(QEvent *e); + +private: + +private: + QWidget* proxy; + Ui_TaskGrooveParameters* ui; + ViewProviderGroove *GrooveView; +}; + +/// simulation dialog for the TaskView +class TaskDlgGrooveParameters : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskDlgGrooveParameters(ViewProviderGroove *GrooveView); + ~TaskDlgGrooveParameters(); + + ViewProviderGroove* getGrooveView() const + { return GrooveView; } + + +public: + /// is called the TaskView when the dialog is opened + virtual void open(); + /// is called by the framework if an button is clicked which has no accept or reject role + virtual void clicked(int); + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + /// is called by the framework if the dialog is rejected (Cancel) + virtual bool reject(); + /// is called by the framework if the user presses the help button + virtual bool isAllowedAlterDocument(void) const + { return false; } + + /// returns for Close and Help button + virtual QDialogButtonBox::StandardButtons getStandardButtons(void) const + { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } + +protected: + ViewProviderGroove *GrooveView; + + TaskGrooveParameters *parameter; +}; + +} //namespace PartDesignGui + +#endif // GUI_TASKVIEW_TASKAPPERANCE_H diff --git a/src/Mod/PartDesign/Gui/TaskGrooveParameters.ui b/src/Mod/PartDesign/Gui/TaskGrooveParameters.ui new file mode 100644 index 000000000..d7a68d6c2 --- /dev/null +++ b/src/Mod/PartDesign/Gui/TaskGrooveParameters.ui @@ -0,0 +1,93 @@ + + + PartDesignGui::TaskGrooveParameters + + + + 0 + 0 + 278 + 158 + + + + Form + + + + + + + + Axis: + + + + + + + + Vertical sketch axis + + + + + Horizontal sketch axis + + + + + + + + + + + + Angle: + + + + + + + 1 + + + 0.000000000000000 + + + 360.000000000000000 + + + 10.000000000000000 + + + 360.000000000000000 + + + + + + + + + true + + + Symmetric to plane + + + + + + + Reversed + + + + + + + + diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.cpp b/src/Mod/PartDesign/Gui/TaskPadParameters.cpp index 8a902b2b9..d7c37b7b3 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.cpp @@ -59,30 +59,106 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView,QWidget *parent) connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onLengthChanged(double))); - connect(ui->checkBoxMirrored, SIGNAL(toggled(bool)), - this, SLOT(onMirrored(bool))); + connect(ui->checkBoxMidplane, SIGNAL(toggled(bool)), + this, SLOT(onMidplane(bool))); connect(ui->checkBoxReversed, SIGNAL(toggled(bool)), this, SLOT(onReversed(bool))); + connect(ui->doubleSpinBox2, SIGNAL(valueChanged(double)), + this, SLOT(onLength2Changed(double))); + connect(ui->changeMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(onModeChanged(int))); + connect(ui->lineFaceName, SIGNAL(textEdited(QString)), + this, SLOT(onFaceName(QString))); this->groupLayout()->addWidget(proxy); + // Get the feature data PartDesign::Pad* pcPad = static_cast(PadView->getObject()); double l = pcPad->Length.getValue(); - bool mirrored = pcPad->MirroredExtent.getValue(); + bool midplane = pcPad->Midplane.getValue(); bool reversed = pcPad->Reversed.getValue(); + double l2 = pcPad->Length2.getValue(); + int index = pcPad->Type.getValue(); // must extract value here, clear() kills it! + const char* upToFace = pcPad->FaceName.getValue(); + // Fill data into dialog elements ui->doubleSpinBox->setMinimum(0); + ui->doubleSpinBox->setMaximum(INT_MAX); ui->doubleSpinBox->setValue(l); - ui->doubleSpinBox->selectAll(); - ui->checkBoxMirrored->setChecked(mirrored); + ui->doubleSpinBox2->setMinimum(0); + ui->doubleSpinBox2->setMaximum(INT_MAX); + ui->doubleSpinBox2->setValue(l2); + ui->checkBoxMidplane->setChecked(midplane); // According to bug #0000521 the reversed option // shouldn't be de-activated if the pad has a support face ui->checkBoxReversed->setChecked(reversed); + ui->lineFaceName->setText(upToFace == "" ? tr("No face selected") : tr(upToFace)); + ui->changeMode->clear(); + ui->changeMode->insertItem(0, tr("Dimension")); + ui->changeMode->insertItem(1, tr("To last")); + ui->changeMode->insertItem(2, tr("To first")); + ui->changeMode->insertItem(3, tr("Up to face")); + ui->changeMode->insertItem(4, tr("Two dimensions")); + ui->changeMode->setCurrentIndex(index); - // Make sure that the spin box has the focus to get key events - // Calling setFocus() directly doesn't work because the spin box is not - // yet visible. - QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + // activate and de-activate dialog elements as appropriate + updateUI(index); +} + +void TaskPadParameters::updateUI(int index) +{ + if (index == 0) { // dimension + ui->doubleSpinBox->setEnabled(true); + ui->doubleSpinBox->selectAll(); + // Make sure that the spin box has the focus to get key events + // Calling setFocus() directly doesn't work because the spin box is not + // yet visible. + QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + ui->checkBoxMidplane->setEnabled(true); + ui->checkBoxReversed->setEnabled(true); + ui->doubleSpinBox2->setEnabled(false); + ui->lineFaceName->setEnabled(false); + } else if ((index == 1) || (index == 2)) { // up to first/last + ui->doubleSpinBox->setEnabled(false); + ui->checkBoxMidplane->setEnabled(false); + ui->checkBoxReversed->setEnabled(false); + ui->doubleSpinBox2->setEnabled(false); + ui->lineFaceName->setEnabled(false); + } else if (index == 3) { // up to face + ui->doubleSpinBox->setEnabled(false); + ui->checkBoxMidplane->setEnabled(false); + ui->checkBoxReversed->setEnabled(false); + ui->doubleSpinBox2->setEnabled(false); + ui->lineFaceName->setEnabled(true); + QMetaObject::invokeMethod(ui->lineFaceName, "setFocus", Qt::QueuedConnection); + } else { // two dimensions + ui->doubleSpinBox->setEnabled(true); + ui->doubleSpinBox->selectAll(); + QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + ui->checkBoxMidplane->setEnabled(false); + ui->checkBoxReversed->setEnabled(false); + ui->doubleSpinBox2->setEnabled(true); + ui->lineFaceName->setEnabled(false); + } +} + +void TaskPadParameters::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + if (pcPad->Type.getValue() != 3) // ignore user selections if mode is not upToFace + return; + + if (!msg.pSubName || msg.pSubName[0] == '\0') + return; + std::string element(msg.pSubName); + if (element.substr(0,4) != "Face") + return; + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + pcPad->FaceName.setValue(element); + pcPad->getDocument()->recomputeFeature(pcPad); + ui->lineFaceName->setText(tr(element.c_str())); + } } void TaskPadParameters::onLengthChanged(double len) @@ -92,10 +168,10 @@ void TaskPadParameters::onLengthChanged(double len) pcPad->getDocument()->recomputeFeature(pcPad); } -void TaskPadParameters::onMirrored(bool on) +void TaskPadParameters::onMidplane(bool on) { PartDesign::Pad* pcPad = static_cast(PadView->getObject()); - pcPad->MirroredExtent.setValue(on); + pcPad->Midplane.setValue(on); pcPad->getDocument()->recomputeFeature(pcPad); } @@ -106,6 +182,40 @@ void TaskPadParameters::onReversed(bool on) pcPad->getDocument()->recomputeFeature(pcPad); } +void TaskPadParameters::onLength2Changed(double len) +{ + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + pcPad->Length2.setValue((float)len); + pcPad->getDocument()->recomputeFeature(pcPad); +} + +void TaskPadParameters::onModeChanged(int index) +{ + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + + switch (index) { + case 0: pcPad->Type.setValue("Length"); break; + case 1: pcPad->Type.setValue("UpToLast"); break; + case 2: pcPad->Type.setValue("UpToFirst"); break; + case 3: pcPad->Type.setValue("UpToFace"); break; + default: pcPad->Type.setValue("TwoLengths"); + } + + updateUI(index); + + pcPad->getDocument()->recomputeFeature(pcPad); +} + +void TaskPadParameters::onFaceName(const QString& text) +{ + if (text.left(4) != tr("Face")) + return; + + PartDesign::Pad* pcPad = static_cast(PadView->getObject()); + pcPad->FaceName.setValue(text.toUtf8()); + pcPad->getDocument()->recomputeFeature(pcPad); +} + double TaskPadParameters::getLength(void) const { return ui->doubleSpinBox->value(); @@ -116,9 +226,24 @@ bool TaskPadParameters::getReversed(void) const return ui->checkBoxReversed->isChecked(); } -bool TaskPadParameters::getMirroredExtent(void) const +bool TaskPadParameters::getMidplane(void) const { - return ui->checkBoxMirrored->isChecked(); + return ui->checkBoxMidplane->isChecked(); +} + +double TaskPadParameters::getLength2(void) const +{ + return ui->doubleSpinBox2->value(); +} + +int TaskPadParameters::getMode(void) const +{ + return ui->changeMode->currentIndex(); +} + +const QString TaskPadParameters::getFaceName(void) const +{ + return ui->lineFaceName->text(); } TaskPadParameters::~TaskPadParameters() @@ -174,7 +299,10 @@ bool TaskDlgPadParameters::accept() //Gui::Command::openCommand("Pad changed"); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Length = %f",name.c_str(),parameter->getLength()); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %i",name.c_str(),parameter->getReversed()?1:0); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.MirroredExtent = %i",name.c_str(),parameter->getMirroredExtent()?1:0); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Midplane = %i",name.c_str(),parameter->getMidplane()?1:0); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Length2 = %f",name.c_str(),parameter->getLength2()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Type = %u",name.c_str(),parameter->getMode()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.FaceName = \"%s\"",name.c_str(),parameter->getFaceName().toAscii().data()); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); if (!PadView->getObject()->isValid()) throw Base::Exception(PadView->getObject()->getStatusString()); diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.h b/src/Mod/PartDesign/Gui/TaskPadParameters.h index c16493cad..53afdacd0 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.h +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.h @@ -44,7 +44,7 @@ namespace PartDesignGui { -class TaskPadParameters : public Gui::TaskView::TaskBox +class TaskPadParameters : public Gui::TaskView::TaskBox, public Gui::SelectionObserver { Q_OBJECT @@ -52,19 +52,27 @@ public: TaskPadParameters(ViewProviderPad *PadView,QWidget *parent = 0); ~TaskPadParameters(); + int getMode(void) const; double getLength(void) const; + double getLength2(void) const; bool getReversed(void) const; - bool getMirroredExtent(void) const; + bool getMidplane(void) const; + const QString getFaceName(void) const; private Q_SLOTS: void onLengthChanged(double); - void onMirrored(bool); + void onMidplane(bool); void onReversed(bool); + void onLength2Changed(double); + void onModeChanged(int); + void onFaceName(const QString& text); protected: void changeEvent(QEvent *e); private: + void onSelectionChanged(const Gui::SelectionChanges& msg); + void updateUI(int index); private: QWidget* proxy; diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.ui b/src/Mod/PartDesign/Gui/TaskPadParameters.ui index d407bbf31..d1f2601cb 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.ui @@ -6,8 +6,8 @@ 0 0 - 158 - 116 + 272 + 238 @@ -19,7 +19,7 @@ - Type: + Type @@ -39,7 +39,7 @@ - Length: + Length @@ -55,19 +55,19 @@ 5.000000000000000 - 20.000000000000000 + 10.000000000000000 - + - false + true - Mirrored extent + Symmetric to plane @@ -78,6 +78,47 @@ + + + + + + 2nd length + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + 5.000000000000000 + + + 0.000000000000000 + + + + + + + + + + + Face + + + + + + + + diff --git a/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp b/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp index 30618f621..2122bafd1 100644 --- a/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp @@ -58,16 +58,39 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onLengthChanged(double))); + connect(ui->changeMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(onModeChanged(int))); + connect(ui->lineFaceName, SIGNAL(textEdited(QString)), + this, SLOT(onFaceName(QString))); this->groupLayout()->addWidget(proxy); PartDesign::Pocket* pcPocket = static_cast(PocketView->getObject()); double l = pcPocket->Length.getValue(); + int index = pcPocket->Type.getValue(); // must extract value here, clear() kills it! + const char* upToFace = pcPocket->FaceName.getValue(); - ui->doubleSpinBox->setMaximum(INT_MAX); - ui->doubleSpinBox->setValue(l); - ui->doubleSpinBox->selectAll(); - QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + ui->changeMode->clear(); + ui->changeMode->insertItem(0, tr("Dimension")); + ui->changeMode->insertItem(1, tr("To last")); + ui->changeMode->insertItem(2, tr("To first")); + ui->changeMode->insertItem(3, tr("Through all")); + ui->changeMode->insertItem(4, tr("Up to face")); + ui->changeMode->setCurrentIndex(index); + + if (index == 0) { // Only this option requires a numeric value + ui->doubleSpinBox->setMaximum(INT_MAX); + ui->doubleSpinBox->setValue(l); + ui->doubleSpinBox->selectAll(); + QMetaObject::invokeMethod(ui->doubleSpinBox, "setFocus", Qt::QueuedConnection); + ui->lineFaceName->setEnabled(false); + } else if (index == 4) { // Only this option requires to select a face + ui->doubleSpinBox->setEnabled(false); + ui->lineFaceName->setText(upToFace == "" ? tr("No face selected") : tr(upToFace)); + } else { // Neither value nor face required + ui->doubleSpinBox->setEnabled(false); + ui->lineFaceName->setEnabled(false); + } //// check if the sketch has support //Sketcher::SketchObject *pcSketch; @@ -81,6 +104,25 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge //} } +void TaskPocketParameters::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + PartDesign::Pocket* pcPocket = static_cast(PocketView->getObject()); + if (pcPocket->Type.getValue() != 4) // ignore user selections if mode is not upToFace + return; + + if (!msg.pSubName || msg.pSubName[0] == '\0') + return; + std::string element(msg.pSubName); + if (element.substr(0,4) != "Face") + return; + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + pcPocket->FaceName.setValue(element); + pcPocket->getDocument()->recomputeFeature(pcPocket); + ui->lineFaceName->setText(tr(element.c_str())); + } +} + void TaskPocketParameters::onLengthChanged(double len) { PartDesign::Pocket* pcPocket = static_cast(PocketView->getObject()); @@ -88,11 +130,59 @@ void TaskPocketParameters::onLengthChanged(double len) pcPocket->getDocument()->recomputeFeature(pcPocket); } +void TaskPocketParameters::onModeChanged(int index) +{ + PartDesign::Pocket* pcPocket = static_cast(PocketView->getObject()); + + switch (index) { + case 0: pcPocket->Type.setValue("Length"); break; + case 1: pcPocket->Type.setValue("UpToLast"); break; + case 2: pcPocket->Type.setValue("UpToFirst"); break; + case 3: pcPocket->Type.setValue("ThroughAll"); break; + case 4: pcPocket->Type.setValue("UpToFace"); break; + default: pcPocket->Type.setValue("Length"); + } + + if (index == 0) { + ui->doubleSpinBox->setEnabled(true); + ui->lineFaceName->setEnabled(false); + ui->doubleSpinBox->setValue(pcPocket->Length.getValue()); + } else if (index == 4) { + ui->lineFaceName->setEnabled(true); + ui->doubleSpinBox->setEnabled(false); + ui->lineFaceName->setText(tr(pcPocket->FaceName.getValue())); + } else { + ui->doubleSpinBox->setEnabled(false); + ui->lineFaceName->setEnabled(false); + } + + pcPocket->getDocument()->recomputeFeature(pcPocket); +} + +void TaskPocketParameters::onFaceName(const QString& text) +{ + if (text.left(4) != tr("Face")) + return; + + PartDesign::Pocket* pcPocket = static_cast(PocketView->getObject()); + pcPocket->FaceName.setValue(text.toUtf8()); + pcPocket->getDocument()->recomputeFeature(pcPocket); +} + double TaskPocketParameters::getLength(void) const { return ui->doubleSpinBox->value(); } +int TaskPocketParameters::getMode(void) const +{ + return ui->changeMode->currentIndex(); +} + +const QString TaskPocketParameters::getFaceName(void) const +{ + return ui->lineFaceName->text(); +} TaskPocketParameters::~TaskPocketParameters() { @@ -145,6 +235,8 @@ bool TaskDlgPocketParameters::accept() //Gui::Command::openCommand("Pocket changed"); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Length = %f",name.c_str(),parameter->getLength()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Type = %u",name.c_str(),parameter->getMode()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.FaceName = \"%s\"",name.c_str(),parameter->getFaceName().toAscii().data()); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); Gui::Command::commitCommand(); @@ -163,7 +255,7 @@ bool TaskDlgPocketParameters::reject() pcSupport = pcSketch->Support.getValue(); } - // role back the done things + // roll back the done things Gui::Command::abortCommand(); Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); diff --git a/src/Mod/PartDesign/Gui/TaskPocketParameters.h b/src/Mod/PartDesign/Gui/TaskPocketParameters.h index 077a9ef0c..bbb6bbb3f 100644 --- a/src/Mod/PartDesign/Gui/TaskPocketParameters.h +++ b/src/Mod/PartDesign/Gui/TaskPocketParameters.h @@ -44,7 +44,7 @@ namespace PartDesignGui { -class TaskPocketParameters : public Gui::TaskView::TaskBox +class TaskPocketParameters : public Gui::TaskView::TaskBox, public Gui::SelectionObserver { Q_OBJECT @@ -53,14 +53,19 @@ public: ~TaskPocketParameters(); double getLength(void) const; + int getMode(void) const; + const QString getFaceName(void) const; private Q_SLOTS: void onLengthChanged(double); + void onModeChanged(int); + void onFaceName(const QString& text); protected: void changeEvent(QEvent *e); private: + void onSelectionChanged(const Gui::SelectionChanges& msg); private: QWidget* proxy; diff --git a/src/Mod/PartDesign/Gui/TaskPocketParameters.ui b/src/Mod/PartDesign/Gui/TaskPocketParameters.ui index 34c8979a1..4f313be76 100644 --- a/src/Mod/PartDesign/Gui/TaskPocketParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskPocketParameters.ui @@ -6,49 +6,97 @@ 0 0 - 137 - 68 + 241 + 134 + + + 0 + 0 + + + + + 233 + 134 + + Form - - - - - + + + + 10 + 10 + 211 + 34 + + + + + + + Type + + + + + + - Type: + Dimension - - - - - - - Dimension - - - - - - - - - - - - Length - - - - - - - - - + + + + + + + + + 10 + 90 + 211 + 34 + + + + + + + Face + + + + + + + + + + + + 10 + 50 + 211 + 34 + + + + + + + Length + + + + + + + + diff --git a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp index 05b01058f..f78134b74 100644 --- a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.cpp @@ -60,11 +60,18 @@ TaskRevolutionParameters::TaskRevolutionParameters(ViewProviderRevolution *Revol this, SLOT(onAngleChanged(double))); connect(ui->axis, SIGNAL(activated(int)), this, SLOT(onAxisChanged(int))); + connect(ui->checkBoxMidplane, SIGNAL(toggled(bool)), + this, SLOT(onMidplane(bool))); + connect(ui->checkBoxReversed, SIGNAL(toggled(bool)), + this, SLOT(onReversed(bool))); this->groupLayout()->addWidget(proxy); PartDesign::Revolution* pcRevolution = static_cast(RevolutionView->getObject()); double l = pcRevolution->Angle.getValue(); + bool mirrored = pcRevolution->Midplane.getValue(); + bool reversed = pcRevolution->Reversed.getValue(); + ui->doubleSpinBox->setValue(l); int count=pcRevolution->getSketchAxisCount(); @@ -95,6 +102,9 @@ TaskRevolutionParameters::TaskRevolutionParameters(ViewProviderRevolution *Revol ui->axis->setCurrentIndex(pos); + ui->checkBoxMidplane->setChecked(mirrored); + ui->checkBoxReversed->setChecked(reversed); + setFocus (); } @@ -126,6 +136,19 @@ void TaskRevolutionParameters::onAxisChanged(int num) pcRevolution->getDocument()->recomputeFeature(pcRevolution); } +void TaskRevolutionParameters::onMidplane(bool on) +{ + PartDesign::Revolution* pcRevolution = static_cast(RevolutionView->getObject()); + pcRevolution->Midplane.setValue(on); + pcRevolution->getDocument()->recomputeFeature(pcRevolution); +} + +void TaskRevolutionParameters::onReversed(bool on) +{ + PartDesign::Revolution* pcRevolution = static_cast(RevolutionView->getObject()); + pcRevolution->Reversed.setValue(on); + pcRevolution->getDocument()->recomputeFeature(pcRevolution); +} double TaskRevolutionParameters::getAngle(void) const { @@ -157,6 +180,16 @@ QString TaskRevolutionParameters::getReferenceAxis(void) const return buf; } +bool TaskRevolutionParameters::getMidplane(void) const +{ + return ui->checkBoxMidplane->isChecked(); +} + +bool TaskRevolutionParameters::getReversed(void) const +{ + return ui->checkBoxReversed->isChecked(); +} + TaskRevolutionParameters::~TaskRevolutionParameters() { delete ui; @@ -210,6 +243,8 @@ bool TaskDlgRevolutionParameters::accept() Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Angle = %f",name.c_str(),parameter->getAngle()); std::string axis = parameter->getReferenceAxis().toStdString(); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.ReferenceAxis = %s",name.c_str(),axis.c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Midplane = %i",name.c_str(),parameter->getMidplane()?1:0); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %i",name.c_str(),parameter->getReversed()?1:0); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); Gui::Command::commitCommand(); diff --git a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.h b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.h index dcafbd618..1d9eb71a8 100644 --- a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.h +++ b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.h @@ -54,10 +54,14 @@ public: QString getReferenceAxis(void) const; double getAngle(void) const; + bool getMidplane(void) const; + bool getReversed(void) const; private Q_SLOTS: void onAngleChanged(double); void onAxisChanged(int); + void onMidplane(bool); + void onReversed(bool); protected: void changeEvent(QEvent *e); diff --git a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui index a80c300fe..dac292f99 100644 --- a/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskRevolutionParameters.ui @@ -6,8 +6,8 @@ 0 0 - 182 - 68 + 278 + 158 @@ -54,7 +54,7 @@ 1 - -360.000000000000000 + 0.000000000000000 360.000000000000000 @@ -69,6 +69,23 @@ + + + + true + + + Symmetric to plane + + + + + + + Reversed + + + diff --git a/src/Mod/PartDesign/Gui/ViewProviderGroove.cpp b/src/Mod/PartDesign/Gui/ViewProviderGroove.cpp new file mode 100644 index 000000000..af7a24854 --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderGroove.cpp @@ -0,0 +1,141 @@ +/*************************************************************************** + * Copyright (c) 2011 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include +#include +#include +#include +#include + +#include "ViewProviderGroove.h" +#include "TaskGrooveParameters.h" + +using namespace PartDesignGui; + +PROPERTY_SOURCE(PartDesignGui::ViewProviderGroove,PartDesignGui::ViewProvider) + +ViewProviderGroove::ViewProviderGroove() +{ +} + +ViewProviderGroove::~ViewProviderGroove() +{ +} + +std::vector ViewProviderGroove::claimChildren(void)const +{ + std::vector temp; + temp.push_back(static_cast(getObject())->Sketch.getValue()); + + return temp; +} + +void ViewProviderGroove::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) +{ + QAction* act; + act = menu->addAction(QObject::tr("Edit Groove"), receiver, member); + act->setData(QVariant((int)ViewProvider::Default)); + PartGui::ViewProviderPart::setupContextMenu(menu, receiver, member); +} + +bool ViewProviderGroove::setEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default ) { + // When double-clicking on the item for this pad the + // object unsets and sets its edit mode without closing + // the task panel + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskDlgGrooveParameters *padDlg = qobject_cast(dlg); + if (padDlg && padDlg->getGrooveView() != this) + padDlg = 0; // another pad left open its task panel + if (dlg && !padDlg) { + QMessageBox msgBox; + msgBox.setText(QObject::tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) + Gui::Control().closeDialog(); + else + return false; + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + //if (ModNum == 1) + // Gui::Command::openCommand("Change Groove parameters"); + + // start the edit dialog + if (padDlg) + Gui::Control().showDialog(padDlg); + else + Gui::Control().showDialog(new TaskDlgGrooveParameters(this)); + + return true; + } + else { + return PartGui::ViewProviderPart::setEdit(ModNum); + } +} + +void ViewProviderGroove::unsetEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default) { + // and update the pad + //getSketchObject()->getDocument()->recompute(); + + // when pressing ESC make sure to close the dialog + Gui::Control().closeDialog(); + } + else { + PartGui::ViewProviderPart::unsetEdit(ModNum); + } +} + +bool ViewProviderGroove::onDelete(const std::vector &) +{ + // get the support and Sketch + PartDesign::Groove* pcGroove = static_cast(getObject()); + Sketcher::SketchObject *pcSketch; + App::DocumentObject *pcSupport; + if (pcGroove->Sketch.getValue()){ + pcSketch = static_cast(pcGroove->Sketch.getValue()); + pcSupport = pcSketch->Support.getValue(); + } + + // if abort command deleted the object the support is visible again + if (pcSketch && Gui::Application::Instance->getViewProvider(pcSketch)) + Gui::Application::Instance->getViewProvider(pcSketch)->show(); + if (pcSupport && Gui::Application::Instance->getViewProvider(pcSupport)) + Gui::Application::Instance->getViewProvider(pcSupport)->show(); + + return true; +} + + diff --git a/src/Mod/PartDesign/Gui/ViewProviderGroove.h b/src/Mod/PartDesign/Gui/ViewProviderGroove.h new file mode 100644 index 000000000..51b1adca4 --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderGroove.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (c) 2011 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_ViewProviderGroove_H +#define PARTGUI_ViewProviderGroove_H + +#include "ViewProvider.h" + + +namespace PartDesignGui { + +class PartDesignGuiExport ViewProviderGroove : public ViewProvider +{ + PROPERTY_HEADER(PartGui::ViewProviderGroove); + +public: + /// constructor + ViewProviderGroove(); + /// destructor + virtual ~ViewProviderGroove(); + + /// grouping handling + std::vector claimChildren(void)const; + + void setupContextMenu(QMenu*, QObject*, const char*); + + virtual bool onDelete(const std::vector &); + +protected: + virtual bool setEdit(int ModNum); + virtual void unsetEdit(int ModNum); + +}; + + +} // namespace PartDesignGui + + +#endif // PARTGUI_ViewProviderGroove_H diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 50f9f9adf..961393e70 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -86,6 +86,7 @@ void Workbench::activated() "PartDesign_Pad", "PartDesign_Pocket", "PartDesign_Revolution", + "PartDesign_Groove", 0}; Watcher.push_back(new Gui::TaskView::TaskWatcherCommands( "SELECT Sketcher::SketchObject COUNT 1", @@ -169,6 +170,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "PartDesign_Pad" << "PartDesign_Pocket" << "PartDesign_Revolution" + << "PartDesign_Groove" << "PartDesign_Fillet" << "PartDesign_Chamfer"; @@ -186,6 +188,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "PartDesign_Pad" << "PartDesign_Pocket" << "PartDesign_Revolution" + << "PartDesign_Groove" << "PartDesign_Fillet" << "PartDesign_Chamfer";