diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 06439746a9..573e4eb322 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -676,6 +676,47 @@ public: */ TopoShape &makeElementCompound(const std::vector &shapes, const char *op=nullptr, bool force=true); + /* Make a shell using this shape + * @param silent: whether to throw exception on failure + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape. The function returns the TopoShape itself as + * a self reference so that multiple operations can be carried out + * for the same shape in the same line of code. + */ + TopoShape& makeElementShell(bool silent = true, const char* op = nullptr); + + /* Make a shell with input wires + * + * @param wires: input wires + * @param silent: whether to throw exception on failure + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape. The function returns the TopoShape itself as + * a self reference so that multiple operations can be carried out + * for the same shape in the same line of code. + */ + // TopoShape& makeElementShellFromWires(const std::vector& wires, + // bool silent = true, + // const char* op = nullptr); + /* Make a shell with input wires + * + * @param wires: input wires + * @param silent: whether to throw exception on failure + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the new shape. The TopoShape itself is not modified. + */ + // TopoShape& makeElementShellFromWires(bool silent = true, const char* op = nullptr) + // { + // return makeElementShellFromWires(getSubTopoShapes(TopAbs_WIRE), silent, op); + // } + TopoShape& makeElementFace(const std::vector& shapes, const char* op = nullptr, const char* maker = nullptr, diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index f6abf55423..e7554ebaa8 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -35,6 +35,9 @@ #endif +#include +#include +#include #include "TopoShape.h" #include "TopoShapeCache.h" #include "FaceMaker.h" @@ -796,5 +799,141 @@ TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient return TopoShape(); } +struct MapperFill: Part::TopoShape::Mapper +{ + BRepFill_Generator& maker; + explicit MapperFill(BRepFill_Generator& maker) + : maker(maker) + {} + const std::vector& generated(const TopoDS_Shape& s) const override + { + _res.clear(); + try { + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(maker.GeneratedShapes(s)); it.More(); it.Next()) { + _res.push_back(it.Value()); + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; + } +}; + +// topo naming counterpart of TopoShape::makeShell() +TopoShape& TopoShape::makeElementShell(bool silent, const char* op) +{ + if (silent) { + if (isNull()) { + return *this; + } + + if (shapeType(true) != TopAbs_COMPOUND) { + return *this; + } + + // we need a compound that consists of only faces + TopExp_Explorer it; + // no shells + if (hasSubShape(TopAbs_SHELL)) { + return *this; + } + + // no wires outside a face + it.Init(_Shape, TopAbs_WIRE, TopAbs_FACE); + if (it.More()) { + return *this; + } + + // no edges outside a wire + it.Init(_Shape, TopAbs_EDGE, TopAbs_WIRE); + if (it.More()) { + return *this; + } + + // no vertexes outside an edge + it.Init(_Shape, TopAbs_VERTEX, TopAbs_EDGE); + if (it.More()) { + return *this; + } + } + else if (!hasSubShape(TopAbs_FACE)) { + FC_THROWM(Base::CADKernelError, "Cannot make shell without face"); + } + + BRep_Builder builder; + TopoDS_Shape shape; + TopoDS_Shell shell; + builder.MakeShell(shell); + + try { + for (const auto& face : getSubShapes(TopAbs_FACE)) { + builder.Add(shell, face); + } + + TopoShape tmp(Tag, Hasher, shell); + tmp.resetElementMap(); + tmp.mapSubElement(*this, op); + + shape = shell; + BRepCheck_Analyzer check(shell); + if (!check.IsValid()) { + ShapeUpgrade_ShellSewing sewShell; + shape = sewShell.ApplySewing(shell); + // TODO confirm the above won't change OCCT topological naming + } + + if (shape.IsNull()) { + if (silent) { + return *this; + } + FC_THROWM(NullShapeException, "Failed to make shell"); + } + + if (shape.ShapeType() != TopAbs_SHELL) { + if (silent) { + return *this; + } + FC_THROWM(Base::CADKernelError, + "Failed to make shell: unexpected output shape type " + << shapeType(shape.ShapeType(), true)); + } + + setShape(shape); + resetElementMap(tmp.elementMap()); + } + catch (Standard_Failure& e) { + if (!silent) { + FC_THROWM(Base::CADKernelError, "Failed to make shell: " << e.GetMessageString()); + } + } + + return *this; +} + +// TopoShape& TopoShape::makeElementShellFromWires(const std::vector& wires, +// bool silent, +// const char* op) +// { +// BRepFill_Generator maker; +// for (auto& w : wires) { +// if (w.shapeType(silent) == TopAbs_WIRE) { +// maker.AddWire(TopoDS::Wire(w.getShape())); +// } +// } +// if (wires.empty()) { +// if (silent) { +// _Shape.Nullify(); +// return *this; +// } +// FC_THROWM(NullShapeException, "No input shapes"); +// } +// maker.Perform(); +// this->makeShapeWithElementMap(maker.Shell(), MapperFill(maker), wires, op); +// return *this; +// } } // namespace Part