From c82e4117e986e44ad0f6fdb5374a86a95c334423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Wed, 3 Jun 2015 07:47:05 +0200 Subject: [PATCH] add feature loft --- src/Mod/PartDesign/App/AppPartDesign.cpp | 4 + src/Mod/PartDesign/App/CMakeLists.txt | 2 + src/Mod/PartDesign/App/FeatureLoft.cpp | 234 +++++++++++++++++++++++ src/Mod/PartDesign/App/FeatureLoft.h | 79 ++++++++ src/Mod/PartDesign/App/FeaturePipe.cpp | 7 +- 5 files changed, 320 insertions(+), 6 deletions(-) create mode 100644 src/Mod/PartDesign/App/FeatureLoft.cpp create mode 100644 src/Mod/PartDesign/App/FeatureLoft.h diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp index 4ff77b6f3..12ca55f38 100644 --- a/src/Mod/PartDesign/App/AppPartDesign.cpp +++ b/src/Mod/PartDesign/App/AppPartDesign.cpp @@ -55,6 +55,7 @@ #include "DatumCS.h" #include "FeatureThickness.h" #include "FeaturePipe.h" +#include "FeatureLoft.h" namespace PartDesign { extern PyObject* initModule(); @@ -105,6 +106,9 @@ PyMODINIT_FUNC init_PartDesign() PartDesign::Pipe ::init(); PartDesign::AdditivePipe ::init(); PartDesign::SubtractivePipe ::init(); + PartDesign::Loft ::init(); + PartDesign::AdditiveLoft ::init(); + PartDesign::SubtractiveLoft ::init(); PartDesign::Plane ::init(); PartDesign::Line ::init(); PartDesign::Point ::init(); diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt index f06ee84d1..cc5dd7f66 100644 --- a/src/Mod/PartDesign/App/CMakeLists.txt +++ b/src/Mod/PartDesign/App/CMakeLists.txt @@ -102,6 +102,8 @@ SET(FeaturesSketchBased_SRCS FeaturePrimitive.cpp FeaturePipe.h FeaturePipe.cpp + FeatureLoft.h + FeatureLoft.cpp ) SOURCE_GROUP("SketchBasedFeatures" FILES ${FeaturesSketchBased_SRCS}) diff --git a/src/Mod/PartDesign/App/FeatureLoft.cpp b/src/Mod/PartDesign/App/FeatureLoft.cpp new file mode 100644 index 000000000..7f567fbd1 --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureLoft.cpp @@ -0,0 +1,234 @@ +/*************************************************************************** + * Copyright (c) 2015 Stefan Tröger * + * * + * 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 +#include +#include +#endif + +#include +#include +#include +#include +#include + +//#include "Body.h" +#include "FeatureLoft.h" + + +using namespace PartDesign; + +PROPERTY_SOURCE(PartDesign::Loft, PartDesign::SketchBased) + +Loft::Loft() +{ + ADD_PROPERTY_TYPE(Sections,(0),"Loft",App::Prop_None,"List of sections"); + Sections.setSize(0); + ADD_PROPERTY_TYPE(Ruled,(false),"Loft",App::Prop_None,"Create ruled surface"); + ADD_PROPERTY_TYPE(Closed,(false),"Loft",App::Prop_None,"Close Last to First Profile"); +} + +short Loft::mustExecute() const +{ + if (Sections.isTouched()) + return 1; + if (Ruled.isTouched()) + return 1; + if (Closed.isTouched()) + return 1; + + return SketchBased::mustExecute(); +} + +App::DocumentObjectExecReturn *Loft::execute(void) +{ + + Part::Part2DObject* sketch = 0; + std::vector wires; + try { + sketch = getVerifiedSketch(); + wires = getSketchWires(); + } catch (const Base::Exception& e) { + return new App::DocumentObjectExecReturn(e.what()); + } + + TopoDS_Shape sketchshape = makeFace(wires); + if (sketchshape.IsNull()) + return new App::DocumentObjectExecReturn("Loft: Creating a face from sketch failed"); + + // if the Base property has a valid shape, fuse the pipe into it + TopoDS_Shape base; + try { + base = getBaseShape(); + } catch (const Base::Exception&) { + base = TopoDS_Shape(); + } + + try { + //setup the location + this->positionBySketch(); + TopLoc_Location invObjLoc = this->getLocation().Inverted(); + if(!base.IsNull()) + base.Move(invObjLoc); + + //build up multisections + auto multisections = Sections.getValues(); + std::vector> wiresections; + for(TopoDS_Wire& wire : wires) + wiresections.push_back(std::vector(1, wire)); + + for(App::DocumentObject* obj : multisections) { + if(!obj->isDerivedFrom(Part::Feature::getClassTypeId())) + return new App::DocumentObjectExecReturn("All sections need to be part features"); + + TopExp_Explorer ex; + int i=0; + for (ex.Init(static_cast(obj)->Shape.getValue(), TopAbs_WIRE); ex.More(); ex.Next()) { + wiresections[i].push_back(TopoDS::Wire(ex.Current())); + if(i>=wiresections.size()) + return new App::DocumentObjectExecReturn("Sections need to have the same amount of inner wires as the base section"); + + ++i; + } + if(i shells; + for(std::vector& wires : wiresections) { + + BRepOffsetAPI_ThruSections mkTS(false, Ruled.getValue(), Precision::Confusion()); + + for(TopoDS_Wire& wire : wires) + mkTS.AddWire(wire); + + if (!mkTS.IsDone()) + return new App::DocumentObjectExecReturn("Loft could not be build"); + + //build the shell use simulate to get the top and bottom wires in an easy way + shells.push_back(mkTS.Shape()); + } + + //build the top and bottom face, sew the shell and build the final solid + TopoDS_Shape front = makeFace(wires); + std::vector backwires; + for(std::vector& wires : wiresections) + backwires.push_back(wires.back()); + + TopoDS_Shape back = makeFace(backwires); + + BRepBuilderAPI_Sewing sewer; + sewer.SetTolerance(Precision::Confusion()); + sewer.Add(front); + sewer.Add(back); + for(TopoDS_Shape& s : shells) + sewer.Add(s); + + sewer.Perform(); + + //build the solid + BRepBuilderAPI_MakeSolid mkSolid; + mkSolid.Add(TopoDS::Shell(sewer.SewedShape())); + if(!mkSolid.IsDone()) + return new App::DocumentObjectExecReturn("Result is not a solid"); + + TopoDS_Shape result = mkSolid.Shape(); + BRepClass3d_SolidClassifier SC(result); + SC.PerformInfinitePoint(Precision::Confusion()); + if ( SC.State() == TopAbs_IN) { + result.Reverse(); + } + + result.Move(invObjLoc); + AddSubShape.setValue(result); + + if(base.IsNull()) { + Shape.setValue(result); + return App::DocumentObject::StdReturn; + } + + if(getAddSubType() == FeatureAddSub::Additive) { + + BRepAlgoAPI_Fuse mkFuse(base, result); + if (!mkFuse.IsDone()) + return new App::DocumentObjectExecReturn("Adding the loft failed"); + // we have to get the solids (fuse sometimes creates compounds) + TopoDS_Shape boolOp = this->getSolid(mkFuse.Shape()); + // lets check if the result is a solid + if (boolOp.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); + + boolOp = refineShapeIfActive(boolOp); + Shape.setValue(boolOp); + } + else if(getAddSubType() == FeatureAddSub::Subtractive) { + + BRepAlgoAPI_Cut mkCut(base, result); + if (!mkCut.IsDone()) + return new App::DocumentObjectExecReturn("Subtracting the loft failed"); + // we have to get the solids (fuse sometimes creates compounds) + TopoDS_Shape boolOp = this->getSolid(mkCut.Shape()); + // lets check if the result is a solid + if (boolOp.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is not a solid"); + + boolOp = refineShapeIfActive(boolOp); + Shape.setValue(boolOp); + } + + return App::DocumentObject::StdReturn; + + return SketchBased::execute(); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); + } + catch (...) { + return new App::DocumentObjectExecReturn("A fatal error occurred when making the loft"); + } +} + + +PROPERTY_SOURCE(PartDesign::AdditiveLoft, PartDesign::Loft) +AdditiveLoft::AdditiveLoft() { + addSubType = Additive; +} + +PROPERTY_SOURCE(PartDesign::SubtractiveLoft, PartDesign::Loft) +SubtractiveLoft::SubtractiveLoft() { + addSubType = Subtractive; +} \ No newline at end of file diff --git a/src/Mod/PartDesign/App/FeatureLoft.h b/src/Mod/PartDesign/App/FeatureLoft.h new file mode 100644 index 000000000..2a4b374f4 --- /dev/null +++ b/src/Mod/PartDesign/App/FeatureLoft.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (c) 2015 Stefan Tröger * + * * + * 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_Loft_H +#define PARTDESIGN_Loft_H + +#include +#include +#include + +#include "FeatureSketchBased.h" + +namespace PartDesign +{ + +class PartDesignExport Loft : public SketchBased +{ + PROPERTY_HEADER(PartDesign::Loft); + +public: + Loft(); + + App::PropertyLinkList Sections; + App::PropertyBool Ruled; + App::PropertyBool Closed; + + /** @name methods override feature */ + //@{ + App::DocumentObjectExecReturn *execute(void); + short mustExecute() const; + /// returns the type name of the view provider + const char* getViewProviderName(void) const { + return "PartDesignGui::ViewProviderLoft"; + } + //@} + +private: + //static const char* TypeEnums[]; + //static const char* SideEnums[]; +}; + +class PartDesignExport AdditiveLoft : public Loft { + + PROPERTY_HEADER(PartDesign::AdditiveLoft); +public: + AdditiveLoft(); +}; + +class PartDesignExport SubtractiveLoft : public Loft { + + PROPERTY_HEADER(PartDesign::SubtractiveLoft); +public: + SubtractiveLoft(); +}; + +} //namespace PartDesign + + +#endif // PART_Loft_H diff --git a/src/Mod/PartDesign/App/FeaturePipe.cpp b/src/Mod/PartDesign/App/FeaturePipe.cpp index e5c719a51..defe5a027 100644 --- a/src/Mod/PartDesign/App/FeaturePipe.cpp +++ b/src/Mod/PartDesign/App/FeaturePipe.cpp @@ -278,12 +278,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void) } if(getAddSubType() == FeatureAddSub::Additive) { - - auto* b = getDocument()->addObject("Part::Feature", "base"); - static_cast(b)->Shape.setValue(base); - b = getDocument()->addObject("Part::Feature", "pipe"); - static_cast(b)->Shape.setValue(result); - + BRepAlgoAPI_Fuse mkFuse(base, result); if (!mkFuse.IsDone()) return new App::DocumentObjectExecReturn("Adding the pipe failed");