diff --git a/src/Mod/Part/App/AppPartPy.cpp b/src/Mod/Part/App/AppPartPy.cpp index 91666bfde7..113a239bd8 100644 --- a/src/Mod/Part/App/AppPartPy.cpp +++ b/src/Mod/Part/App/AppPartPy.cpp @@ -1281,14 +1281,19 @@ static PyObject * makeLoft(PyObject *self, PyObject *args) PyObject *pcObj; PyObject *psolid=Py_False; PyObject *pruled=Py_False; - if (!PyArg_ParseTuple(args, "O|O!O!", &pcObj, + PyObject *pclosed=Py_False; + if (!PyArg_ParseTuple(args, "O|O!O!O!", &pcObj, &(PyBool_Type), &psolid, - &(PyBool_Type), &pruled)) + &(PyBool_Type), &pruled, + &(PyBool_Type), &pclosed)) { + Base::Console().Message("Part.makeLoft Parameter Error\n"); return NULL; + } try { TopTools_ListOfShape profiles; Py::Sequence list(pcObj); + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { if (PyObject_TypeCheck((*it).ptr(), &(Part::TopoShapePy::Type))) { const TopoDS_Shape& sh = static_cast((*it).ptr())-> @@ -1300,11 +1305,13 @@ static PyObject * makeLoft(PyObject *self, PyObject *args) TopoShape myShape; Standard_Boolean anIsSolid = PyObject_IsTrue(psolid) ? Standard_True : Standard_False; Standard_Boolean anIsRuled = PyObject_IsTrue(pruled) ? Standard_True : Standard_False; - TopoDS_Shape aResult = myShape.makeLoft(profiles, anIsSolid, anIsRuled); + Standard_Boolean anIsClosed = PyObject_IsTrue(pclosed) ? Standard_True : Standard_False; + TopoDS_Shape aResult = myShape.makeLoft(profiles, anIsSolid, anIsRuled,anIsClosed); return new TopoShapePy(new TopoShape(aResult)); } catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); + Base::Console().Message("debug: Part.makeLoft catching 'Standard_Failure' msg: '%s'\n", e->GetMessageString()); PyErr_SetString(PyExc_Exception, e->GetMessageString()); return 0; } diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index 5149b7b2c4..bf923485ed 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -207,6 +207,7 @@ Loft::Loft() Sections.setSize(0); ADD_PROPERTY_TYPE(Solid,(false),"Loft",App::Prop_None,"Create solid"); ADD_PROPERTY_TYPE(Ruled,(false),"Loft",App::Prop_None,"Ruled surface"); + ADD_PROPERTY_TYPE(Closed,(false),"Loft",App::Prop_None,"Close Last to First Profile"); } short Loft::mustExecute() const @@ -217,6 +218,8 @@ short Loft::mustExecute() const return 1; if (Ruled.isTouched()) return 1; + if (Closed.isTouched()) + return 1; return 0; } @@ -262,9 +265,10 @@ App::DocumentObjectExecReturn *Loft::execute(void) Standard_Boolean isSolid = Solid.getValue() ? Standard_True : Standard_False; Standard_Boolean isRuled = Ruled.getValue() ? Standard_True : Standard_False; + Standard_Boolean isClosed = Closed.getValue() ? Standard_True : Standard_False; TopoShape myShape; - this->Shape.setValue(myShape.makeLoft(profiles, isSolid, isRuled)); + this->Shape.setValue(myShape.makeLoft(profiles, isSolid, isRuled,isClosed)); return App::DocumentObject::StdReturn; } catch (Standard_Failure) { diff --git a/src/Mod/Part/App/PartFeatures.h b/src/Mod/Part/App/PartFeatures.h index 609ac6f048..4b397afd80 100644 --- a/src/Mod/Part/App/PartFeatures.h +++ b/src/Mod/Part/App/PartFeatures.h @@ -69,6 +69,7 @@ public: App::PropertyLinkList Sections; App::PropertyBool Solid; App::PropertyBool Ruled; + App::PropertyBool Closed; /** @name methods override feature */ //@{ diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 31d8a3d49e..eb1cbf768e 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -74,6 +74,7 @@ # include # include # include +#include # include # include # include @@ -154,6 +155,8 @@ #include #include #include +#include + #include "TopoShape.h" #include "CrossSection.h" @@ -1788,13 +1791,14 @@ TopoDS_Shape TopoShape::makeThread(Standard_Real pitch, TopoDS_Shape TopoShape::makeLoft(const TopTools_ListOfShape& profiles, Standard_Boolean isSolid, - Standard_Boolean isRuled) const + Standard_Boolean isRuled, + Standard_Boolean isClosed) const { // http://opencascade.blogspot.com/2010/01/surface-modeling-part5.html BRepOffsetAPI_ThruSections aGenerator (isSolid,isRuled); - int countShapes = 0; TopTools_ListIteratorOfListOfShape it; + int countShapes = 0; for (it.Initialize(profiles); it.More(); it.Next()) { const TopoDS_Shape& item = it.Value(); if (!item.IsNull() && item.ShapeType() == TopAbs_VERTEX) { @@ -1812,15 +1816,44 @@ TopoDS_Shape TopoShape::makeLoft(const TopTools_ListOfShape& profiles, } } - if (countShapes < 2) - Standard_Failure::Raise("Need at least two vertices, edges or wires to create loft face"); + if (countShapes < 2) { + Standard_Failure::Raise("Need at least two vertices, edges or wires to create loft face"); } + else { + // close loft by duplicating initial profile as last profile. not perfect. + if (isClosed) { + /* can only close loft in certain combinations of Vertex/Wire(Edge): + - V1-W1-W2-W3-V2 ==> V1-W1-W2-W3-V2-V1 invalid closed + - V1-W1-W2-W3 ==> V1-W1-W2-W3-V1 valid closed + - W1-W2-W3-V1 ==> W1-W2-W3-V1-W1 invalid closed + - W1-W2-W3 ==> W1-W2-W3-W1 valid closed*/ + if (profiles.Last().ShapeType() == TopAbs_VERTEX) { + Base::Console().Message("TopoShape::makeLoft: can't close Loft with Vertex as last profile. 'Closed' ignored.\n"); } + else { + // repeat Add logic above for first profile + const TopoDS_Shape& firstProfile = profiles.First(); + if (firstProfile.ShapeType() == TopAbs_VERTEX) { + aGenerator.AddVertex(TopoDS::Vertex (firstProfile)); + countShapes++; + } + else if (firstProfile.ShapeType() == TopAbs_EDGE) { + aGenerator.AddWire(TopoDS::Wire (firstProfile)); + countShapes++; + } + else if (firstProfile.ShapeType() == TopAbs_WIRE) { + aGenerator.AddWire(TopoDS::Wire (firstProfile)); + countShapes++; + } + } + } + } Standard_Boolean anIsCheck = Standard_True; - aGenerator.CheckCompatibility (anIsCheck); + aGenerator.CheckCompatibility (anIsCheck); // use BRepFill_CompatibleWires on profiles. force #edges, orientation, "origin" to match. aGenerator.Build(); if (!aGenerator.IsDone()) Standard_Failure::Raise("Failed to create loft face"); - + + //Base::Console().Message("DEBUG: TopoShape::makeLoft returns.\n"); return aGenerator.Shape(); } diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 2f9067b048..6c950bc5ab 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -170,7 +170,7 @@ public: TopoDS_Shape makeThread(Standard_Real pitch, Standard_Real depth, Standard_Real height, Standard_Real radius) const; TopoDS_Shape makeLoft(const TopTools_ListOfShape& profiles, Standard_Boolean isSolid, - Standard_Boolean isRuled) const; + Standard_Boolean isRuled, Standard_Boolean isClosed = Standard_False) const; TopoDS_Shape makeOffsetShape(double offset, double tol, bool intersection = false, bool selfInter = false, short offsetMode = 0, short join = 0, bool fill = false) const; diff --git a/src/Mod/Part/Gui/TaskLoft.cpp b/src/Mod/Part/Gui/TaskLoft.cpp index 6f4b5371e7..0c361a4346 100644 --- a/src/Mod/Part/Gui/TaskLoft.cpp +++ b/src/Mod/Part/Gui/TaskLoft.cpp @@ -118,7 +118,7 @@ void LoftWidget::findShapes() bool LoftWidget::accept() { - QString list, solid, ruled; + QString list, solid, ruled, closed; if (d->ui.checkSolid->isChecked()) solid = QString::fromAscii("True"); else @@ -129,6 +129,11 @@ bool LoftWidget::accept() else ruled = QString::fromAscii("False"); + if (d->ui.checkClosed->isChecked()) + closed = QString::fromAscii("True"); + else + closed = QString::fromAscii("False"); + QTextStream str(&list); int count = d->ui.selector->selectedTreeWidget()->topLevelItemCount(); @@ -145,11 +150,12 @@ bool LoftWidget::accept() try { QString cmd; cmd = QString::fromAscii( - "App.getDocument('%4').addObject('Part::Loft','Loft')\n" - "App.getDocument('%4').ActiveObject.Sections=[%1]\n" - "App.getDocument('%4').ActiveObject.Solid=%2\n" - "App.getDocument('%4').ActiveObject.Ruled=%3\n" - ).arg(list).arg(solid).arg(ruled).arg(QString::fromAscii(d->document.c_str())); + "App.getDocument('%5').addObject('Part::Loft','Loft')\n" + "App.getDocument('%5').ActiveObject.Sections=[%1]\n" + "App.getDocument('%5').ActiveObject.Solid=%2\n" + "App.getDocument('%5').ActiveObject.Ruled=%3\n" + "App.getDocument('%5').ActiveObject.Closed=%4\n" + ).arg(list).arg(solid).arg(ruled).arg(closed).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"); diff --git a/src/Mod/Part/Gui/TaskLoft.ui b/src/Mod/Part/Gui/TaskLoft.ui index dd461047d9..08ffafc86d 100644 --- a/src/Mod/Part/Gui/TaskLoft.ui +++ b/src/Mod/Part/Gui/TaskLoft.ui @@ -14,7 +14,7 @@ Loft - + @@ -31,7 +31,7 @@ - + Qt::Horizontal @@ -44,6 +44,13 @@ + + + + Closed + + +