From b6db15a5d3065099beb09456d4d1ef47e686024c Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Sun, 25 Sep 2016 18:19:26 +0300 Subject: [PATCH] Part: Introduce FaceMaker class A general class to implement smart making faces from wires (e.g. making a face from sketch prior to extruding) --- src/Mod/Part/App/CMakeLists.txt | 2 + src/Mod/Part/App/FaceMaker.cpp | 180 ++++++++++++++++++++++++++++++++ src/Mod/Part/App/FaceMaker.h | 145 +++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 src/Mod/Part/App/FaceMaker.cpp create mode 100644 src/Mod/Part/App/FaceMaker.h diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 4e101b76cd30..ed14f393f10f 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -282,6 +282,8 @@ SET(Part_SRCS OCCError.h FT2FC.cpp FT2FC.h + FaceMaker.cpp + FaceMaker.h ) SET(Part_Scripts diff --git a/src/Mod/Part/App/FaceMaker.cpp b/src/Mod/Part/App/FaceMaker.cpp new file mode 100644 index 000000000000..28bd71f086f5 --- /dev/null +++ b/src/Mod/Part/App/FaceMaker.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** + * Copyright (c) 2016 Victor Titov (DeepSOIC) * + * * + * 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 +#endif + +#include "FaceMaker.h" + +#include +#include + +#include + +TYPESYSTEM_SOURCE_ABSTRACT(Part::FaceMaker, Base::BaseClass); +TYPESYSTEM_SOURCE_ABSTRACT(Part::FaceMakerPublic, Part::FaceMaker); + +void Part::FaceMaker::addWire(const TopoDS_Wire& w) +{ + this->addShape(w); +} + +void Part::FaceMaker::addShape(const TopoDS_Shape& sh) +{ + if(sh.IsNull()) + throw Base::ValueError("Input shape is null."); + switch(sh.ShapeType()){ + case TopAbs_COMPOUND: + this->myCompounds.push_back(TopoDS::Compound(sh)); + break; + case TopAbs_WIRE: + this->myWires.push_back(TopoDS::Wire(sh)); + break; + case TopAbs_EDGE: + this->myWires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(sh)).Wire()); + break; + default: + throw Base::TypeError("Shape must be a wire, edge or compound. Something else was supplied."); + break; + } + this->mySourceShapes.push_back(sh); +} + +void Part::FaceMaker::useCompound(const TopoDS_Compound& comp) +{ + TopoDS_Iterator it(comp); + for(; it.More(); it.Next()){ + this->addShape(it.Value()); + } +} + +const TopoDS_Face& Part::FaceMaker::Face() +{ + const TopoDS_Shape &sh = this->Shape(); + if(sh.IsNull()) + throw Base::Exception("Part::FaceMaker: result shape is null."); + if (sh.ShapeType() != TopAbs_FACE) + throw Base::TypeError("Part::FaceMaker: return shape is not a single face."); + return TopoDS::Face(sh); +} + +void Part::FaceMaker::Build() +{ + this->NotDone(); + this->myShapesToReturn.clear(); + this->myGenerated.Clear(); + + this->Build_Essence();//adds stuff to myShapesToReturn + + for(const TopoDS_Compound& cmp : this->myCompounds){ + std::unique_ptr facemaker_instance = Part::FaceMaker::ConstructFromType(this->getTypeId()); + FaceMaker* facemaker = &(*facemaker_instance); //handy to have plain pointer for intellisense to work =) + + facemaker->useCompound(cmp); + + facemaker->Build(); + const TopoDS_Shape &subfaces = facemaker->Shape(); + if (subfaces.IsNull()) + continue; + if (subfaces.ShapeType() == TopAbs_COMPOUND){ + this->myShapesToReturn.push_back(subfaces); + } else { + //result is not a compound (probably, a face)... but we want to follow compounding structure of input, so wrap it into compound. + TopoDS_Builder builder; + TopoDS_Compound cmp_res; + builder.MakeCompound(cmp_res); + builder.Add(cmp_res,subfaces); + this->myShapesToReturn.push_back(cmp_res); + } + } + + if(this->myShapesToReturn.empty()){ + //nothing to do, null shape will be returned. + } else if (this->myShapesToReturn.size() == 1){ + this->myShape = this->myShapesToReturn[0]; + } else { + TopoDS_Builder builder; + TopoDS_Compound cmp_res; + builder.MakeCompound(cmp_res); + for(TopoDS_Shape &sh: this->myShapesToReturn){ + builder.Add(cmp_res,sh); + } + this->myShape = cmp_res; + } + this->Done(); +} + +std::unique_ptr Part::FaceMaker::ConstructFromType(const char* className) +{ + Base::Type fmType = Base::Type::fromName(className); + if (fmType.isBad()){ + std::stringstream ss; + ss << "Class '"<< className <<"' not found."; + throw Base::Exception(ss.str().c_str()); + } + return Part::FaceMaker::ConstructFromType(fmType); +} + +std::unique_ptr Part::FaceMaker::ConstructFromType(Base::Type type) +{ + if (!type.isDerivedFrom(Part::FaceMaker::getClassTypeId())){ + std::stringstream ss; + ss << "Class '" << type.getName() << "' is not derived from Part::FaceMaker."; + throw Base::TypeError(ss.str().c_str()); + } + return std::unique_ptr(static_cast(type.createInstance())); +} + +void Part::FaceMaker::throwNotImplemented() +{ + throw Base::NotImplementedError("Not implemente yet..."); +} + + +//---------------------------------------------------------------------------------------- + +TYPESYSTEM_SOURCE(Part::FaceMakerSimple, Part::FaceMakerPublic); + + +std::string Part::FaceMakerSimple::getUserFriendlyName() const +{ + return std::string(QT_TRANSLATE_NOOP("Part_FaceMaker","Simple")); +} + +std::string Part::FaceMakerSimple::getBriefExplanation() const +{ + return std::string(QT_TRANSLATE_NOOP("Part_FaceMaker","Makes separate plane face from every wire independently. No support for holes; wires can be on different planes.")); +} + +void Part::FaceMakerSimple::Build_Essence() +{ + for(TopoDS_Wire &w: myWires){ + this->myShapesToReturn.push_back(BRepBuilderAPI_MakeFace(w).Shape()); + } +} diff --git a/src/Mod/Part/App/FaceMaker.h b/src/Mod/Part/App/FaceMaker.h new file mode 100644 index 000000000000..24e469f403a1 --- /dev/null +++ b/src/Mod/Part/App/FaceMaker.h @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright (c) 2016 Victor Titov (DeepSOIC) * + * * + * 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 PART_FACEMAKER_H +#define PART_FACEMAKER_H + +#include +#include +#include +#include +#include + +#include + +namespace Part +{ + +/** + * @brief FaceMaker class is the base class for implementing various "smart" + * face making routines. This was created to address the problem of multiple + * private implementations of making faces with holes, which are quite complex. + * The two most important facemaking routines then was: one in Part Extrude, + * and one in PartDesign (there, it is used in every sketch-based feature). + * Plus, another one (new) was needed for filling 2D offset. + */ +class PartExport FaceMaker: public BRepBuilderAPI_MakeShape, public Base::BaseClass +{ + TYPESYSTEM_HEADER(); + +public: + FaceMaker() {}; + virtual ~FaceMaker() {}; + + virtual void addWire(const TopoDS_Wire& w); + /** + * @brief addShape: add another wire, edge, or compound. If compound is + * added, its internals will be treated as isolated from the rest, and the + * compounding structure of result will follow. + * @param sh + */ + virtual void addShape(const TopoDS_Shape& sh); + /** + * @brief useCompound: add children of compound to the FaceMaker. Note that + * this is different from addShape(comp) - structure is lost. The compound + * is NOT expanded recursively. + * @param comp + */ + virtual void useCompound(const TopoDS_Compound &comp); + + /** + * @brief Face: returns the face (result). If result is not a single face, + * throws Base::TypeError. (hint: use .Shape() instead) + * @return + */ + virtual const TopoDS_Face& Face(); + + virtual void Build(); + + //fails to compile, huh! + //virtual const TopTools_ListOfShape& Generated(const TopoDS_Shape &S) override {throwNotImplemented();} + //virtual const TopTools_ListOfShape& Modified(const TopoDS_Shape &S) override {throwNotImplemented();} + //virtual Standard_Boolean IsDeleted(const TopoDS_Shape &S) override {throwNotImplemented();} + + static std::unique_ptr ConstructFromType(const char* className); + static std::unique_ptr ConstructFromType(Base::Type type); + +protected: + std::vector mySourceShapes; //wire or compound + std::vector myWires; //wires from mySourceShapes + std::vector myCompounds; //compounds, for recursive processing + std::vector myShapesToReturn; + + /** + * @brief Build_Essence: build routine that can assume there is no nesting. + * + * Implementing instructions: + * Add new faces (or whatever) to myShapesToReturn. The rest is done by + * base class's Build(). Please ignore contents of myCompounds in + * implementation. If special handling of nesting is required, override + * whole Build(). + */ + virtual void Build_Essence() = 0; + + static void throwNotImplemented(); +}; + +/** + * @brief The FaceMakerPublic class: derive from it if you want the face maker to be listed in tools that allow choosing one. + */ +class PartExport FaceMakerPublic : public FaceMaker +{ + TYPESYSTEM_HEADER(); +public: + virtual std::string getUserFriendlyName() const = 0; + virtual std::string getBriefExplanation() const = 0; +}; + + + +/** + * @brief The FaceMakerSimple class: make plane faces from all closed wires + * supplied, ignoring overlaps. + * + * Strengths: can work with non-coplanar sets of wires. Will not make broken + * faces if wires overlap*. + * + * Limitations: can't make faces with holes (will generate overlapping faces + * instead). Can't make faces from nonplanar wires. + * + * * Compound of valid but overlapping faces is created. The compound is invalid + * for BOPs, but the faces themselves are valid, provided that the source wires + * are valid. + */ +class PartExport FaceMakerSimple : public FaceMakerPublic +{ + TYPESYSTEM_HEADER(); +public: + virtual std::string getUserFriendlyName() const override; + virtual std::string getBriefExplanation() const override; +protected: + virtual void Build_Essence() override; +}; + + +}//namespace Part +#endif // PART_FACEMAKER_H