From e5247001cbc682e1b7746ab0fbf856b3fe3245ed Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 22 Feb 2024 08:40:06 -0500 Subject: [PATCH 1/3] Toposhape/Part: Transfer in makESlice and makEMirror --- src/Mod/Part/App/CrossSection.cpp | 92 +++++++++++++++++++++++++ src/Mod/Part/App/CrossSection.h | 18 +++++ src/Mod/Part/App/TopoShape.h | 83 ++++++++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 24 +++++++ 4 files changed, 217 insertions(+) diff --git a/src/Mod/Part/App/CrossSection.cpp b/src/Mod/Part/App/CrossSection.cpp index e118dba32e..c49aca4208 100644 --- a/src/Mod/Part/App/CrossSection.cpp +++ b/src/Mod/Part/App/CrossSection.cpp @@ -218,3 +218,95 @@ TopoDS_Wire CrossSection::fixWire(const TopoDS_Wire& wire) const aFix.FixClosed(); return aFix.Wire(); } + +TopoCrossSection::TopoCrossSection(double a, double b, double c, const TopoShape& s, const char *op) + : a(a), b(b), c(c), shape(s), op(op?op:Part::OpCodes::Slice) +{ +} + +void TopoCrossSection::slice(int idx, double d, std::vector &wires) const { + // Fixes: 0001228: Cross section of Torus in Part Workbench fails or give wrong results + // Fixes: 0001137: Incomplete slices when using Part.slice on a torus + bool found = false; + for(auto &s : shape.getSubTopoShapes(TopAbs_SOLID)) { + sliceSolid(idx, d, s, wires); + found = true; + } + if(!found) { + for(auto &s : shape.getSubTopoShapes(TopAbs_SHELL)) { + sliceNonSolid(idx, d, s, wires); + found = true; + } + if(!found) { + for(auto &s : shape.getSubTopoShapes(TopAbs_FACE)) + sliceNonSolid(idx, d, s, wires); + } + } +} + +TopoShape TopoCrossSection::slice(int idx, double d) const { + std::vector wires; + slice(idx,d,wires); + return TopoShape().makECompound(wires,0,false); +} + +void TopoCrossSection::sliceNonSolid(int idx, double d, + const TopoShape& shape, std::vector& wires) const +{ + BRepAlgoAPI_Section cs(shape.getShape(), gp_Pln(a,b,c,-d)); + if (cs.IsDone()) { + std::string prefix(op); + if(idx>1) { + prefix += '_'; + prefix += std::to_string(idx); + } + auto res = TopoShape().makEShape(cs,shape,prefix.c_str()).makEWires().getSubTopoShapes(TopAbs_WIRE); + wires.insert(wires.end(),res.begin(),res.end()); + } +} + +void TopoCrossSection::sliceSolid(int idx, double d, + const TopoShape& shape, std::vector& wires) const +{ + gp_Pln slicePlane(a,b,c,-d); + BRepBuilderAPI_MakeFace mkFace(slicePlane); + TopoShape face(idx); + face.setShape(mkFace.Face()); + + // Make sure to choose a point that does not lie on the plane (fixes #0001228) + gp_Vec tempVector(a,b,c); + tempVector.Normalize();//just in case. + tempVector *= (d+1.0); + gp_Pnt refPoint(0.0, 0.0, 0.0); + refPoint.Translate(tempVector); + + BRepPrimAPI_MakeHalfSpace mkSolid(TopoDS::Face(face.getShape()), refPoint); + TopoShape solid(idx); + std::string prefix(op); + if(idx>1) { + prefix += '_'; + prefix += std::to_string(idx); + } + solid.makEShape(mkSolid,face,prefix.c_str()); + BRepAlgoAPI_Cut mkCut(shape.getShape(), solid.getShape()); + + if (mkCut.IsDone()) { + TopoShape res(shape.Tag,shape.Hasher); + std::vector shapes; + shapes.push_back(shape); + shapes.push_back(solid); + res.makEShape(mkCut,shapes,prefix.c_str()); + for(auto &face : res.getSubTopoShapes(TopAbs_FACE)) { + BRepAdaptor_Surface adapt(TopoDS::Face(face.getShape())); + if (adapt.GetType() == GeomAbs_Plane) { + gp_Pln plane = adapt.Plane(); + if (plane.Axis().IsParallel(slicePlane.Axis(), Precision::Confusion()) && + plane.Distance(slicePlane.Location()) < Precision::Confusion()) { + auto repaired_wires = TopoShape(face.Tag).makEWires( + face.getSubTopoShapes(TopAbs_EDGE),prefix.c_str(),true).getSubTopoShapes(TopAbs_WIRE); + wires.insert(wires.end(),repaired_wires.begin(),repaired_wires.end()); + } + } + } + } +} diff --git a/src/Mod/Part/App/CrossSection.h b/src/Mod/Part/App/CrossSection.h index 8dfa95cdb9..b34829cb16 100644 --- a/src/Mod/Part/App/CrossSection.h +++ b/src/Mod/Part/App/CrossSection.h @@ -26,6 +26,7 @@ #include #include #include +#include "TopoShape.h" class TopoDS_Shape; @@ -52,6 +53,23 @@ private: const TopoDS_Shape& s; }; +class PartExport TopoCrossSection +{ +public: + TopoCrossSection(double a, double b, double c, const TopoShape& s, const char *op=0); + void slice(int idx, double d, std::vector &wires) const; + TopoShape slice(int idx, double d) const; + +private: + void sliceNonSolid(int idx, double d, const TopoShape&, std::vector& wires) const; + void sliceSolid(int idx, double d, const TopoShape&, std::vector& wires) const; + +private: + double a,b,c; + const TopoShape& shape; + const char *op; +}; + } #endif // PART_CROSSSECTION_H diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 9b1e1a73bd..aec30fd84c 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -1330,6 +1330,89 @@ public: return TopoShape(0, Hasher).makeElementBoolean(maker, *this, op, tol); } + /** Make a mirrored shape + * + * @param source: the source shape + * @param axis: the axis for mirroring + * @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 &makEMirror(const TopoShape &source, const gp_Ax2& axis, const char *op=nullptr); + /** Make a mirrored shape + * + * @param source: the source shape + * @param axis: the axis for mirroring + * @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 makEMirror(const gp_Ax2& ax, const char *op=nullptr) const { + return TopoShape(0,Hasher).makEMirror(*this,ax,op); + } + + /** Make a cross section slice + * + * @param source: the source shape + * @param dir: direction of the normal of the section plane + * @param d: distance to move the section plane + * @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 &makESlice(const TopoShape &source, const Base::Vector3d& dir, double d, const char *op=nullptr); + /** Make a cross section slice + * + * @param source: the source shape + * @param dir: direction of the normal of the section plane + * @param d: distance to move the section plane + * @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 makESlice(const Base::Vector3d& dir, double d, const char *op=nullptr) const { + return TopoShape(0,Hasher).makESlice(*this,dir,d,op); + } + + /** Make multiple cross section slices + * + * @param source: the source shape + * @param dir: direction of the normal of the section plane + * @param distances: distances to move the section plane for making slices + * @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 &makESlices(const TopoShape &source, const Base::Vector3d& dir, + const std::vector &distances, const char *op=nullptr); + /** Make multiple cross section slices + * + * @param source: the source shape + * @param dir: direction of the normal of the section plane + * @param distances: distances to move the section plane for making slices + * @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 makESlices(const Base::Vector3d &dir, const std::vector &distances, const char *op=nullptr) const { + return TopoShape(0,Hasher).makESlices(*this,dir,distances,op); + } + /* Make fillet shape * * @param source: the source shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 1fc8a6639c..efb1716c6a 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2596,6 +2596,30 @@ struct MapperThruSections: MapperMaker } }; +TopoShape &TopoShape::makESlice(const TopoShape &shape, + const Base::Vector3d& dir, double d, const char *op) +{ + if(shape.isNull()) + HANDLE_NULL_SHAPE; + TopoCrossSection cs(dir.x, dir.y, dir.z,shape,op); + TopoShape res = cs.slice(1,d); + setShape(res._Shape); + Hasher = res.Hasher; + resetElementMap(res.elementMap()); + return *this; +} + +TopoShape &TopoShape::makESlices(const TopoShape &shape, + const Base::Vector3d& dir, const std::vector &d, const char *op) +{ + std::vector wires; + TopoCrossSection cs(dir.x, dir.y, dir.z, shape,op); + int i=0; + for(auto &dd : d) + cs.slice(++i,dd,wires); + return makECompound(wires,op,false); +} + TopoShape& TopoShape::makeElementFillet(const TopoShape& shape, const std::vector& edges, double radius1, From fe72c59e41ddb0ed7a27ee676af0d32e0c533497 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 22 Feb 2024 09:40:37 -0500 Subject: [PATCH 2/3] Toposhape/Part: Cleanups and tests for makeElementSlice, makeElementSlices, and makeElementMirror --- src/Mod/Part/App/CrossSection.cpp | 79 +++++++----- src/Mod/Part/App/CrossSection.h | 10 +- src/Mod/Part/App/TopoShape.h | 35 ++++-- src/Mod/Part/App/TopoShapeExpansion.cpp | 61 +++++++--- tests/src/Mod/Part/App/PartTestHelpers.cpp | 30 ++--- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 113 +++++++++++++++++- 6 files changed, 250 insertions(+), 78 deletions(-) diff --git a/src/Mod/Part/App/CrossSection.cpp b/src/Mod/Part/App/CrossSection.cpp index c49aca4208..b0ec954ef8 100644 --- a/src/Mod/Part/App/CrossSection.cpp +++ b/src/Mod/Part/App/CrossSection.cpp @@ -44,6 +44,7 @@ #endif #include "CrossSection.h" +#include "TopoShapeOpCode.h" using namespace Part; @@ -224,87 +225,103 @@ TopoCrossSection::TopoCrossSection(double a, double b, double c, const TopoShape { } -void TopoCrossSection::slice(int idx, double d, std::vector &wires) const { +void TopoCrossSection::slice(int idx, double d, std::vector& wires) const +{ // Fixes: 0001228: Cross section of Torus in Part Workbench fails or give wrong results // Fixes: 0001137: Incomplete slices when using Part.slice on a torus bool found = false; - for(auto &s : shape.getSubTopoShapes(TopAbs_SOLID)) { + for (auto& s : shape.getSubTopoShapes(TopAbs_SOLID)) { sliceSolid(idx, d, s, wires); found = true; } - if(!found) { - for(auto &s : shape.getSubTopoShapes(TopAbs_SHELL)) { + if (!found) { + for (auto& s : shape.getSubTopoShapes(TopAbs_SHELL)) { sliceNonSolid(idx, d, s, wires); found = true; } - if(!found) { - for(auto &s : shape.getSubTopoShapes(TopAbs_FACE)) + if (!found) { + for (auto& s : shape.getSubTopoShapes(TopAbs_FACE)) { sliceNonSolid(idx, d, s, wires); + } } } } -TopoShape TopoCrossSection::slice(int idx, double d) const { +TopoShape TopoCrossSection::slice(int idx, double d) const +{ std::vector wires; - slice(idx,d,wires); - return TopoShape().makECompound(wires,0,false); + slice(idx, d, wires); + return TopoShape().makeElementCompound( + wires, + 0, + TopoShape::SingleShapeCompoundCreationPolicy::returnShape); } -void TopoCrossSection::sliceNonSolid(int idx, double d, - const TopoShape& shape, std::vector& wires) const +void TopoCrossSection::sliceNonSolid(int idx, + double d, + const TopoShape& shape, + std::vector& wires) const { - BRepAlgoAPI_Section cs(shape.getShape(), gp_Pln(a,b,c,-d)); + BRepAlgoAPI_Section cs(shape.getShape(), gp_Pln(a, b, c, -d)); if (cs.IsDone()) { std::string prefix(op); - if(idx>1) { + if (idx > 1) { prefix += '_'; prefix += std::to_string(idx); } - auto res = TopoShape().makEShape(cs,shape,prefix.c_str()).makEWires().getSubTopoShapes(TopAbs_WIRE); - wires.insert(wires.end(),res.begin(),res.end()); + auto res = TopoShape() + .makeElementShape(cs, shape, prefix.c_str()) + .makeElementWires() + .getSubTopoShapes(TopAbs_WIRE); + wires.insert(wires.end(), res.begin(), res.end()); } } -void TopoCrossSection::sliceSolid(int idx, double d, - const TopoShape& shape, std::vector& wires) const +void TopoCrossSection::sliceSolid(int idx, + double d, + const TopoShape& shape, + std::vector& wires) const { - gp_Pln slicePlane(a,b,c,-d); + gp_Pln slicePlane(a, b, c, -d); BRepBuilderAPI_MakeFace mkFace(slicePlane); TopoShape face(idx); face.setShape(mkFace.Face()); // Make sure to choose a point that does not lie on the plane (fixes #0001228) - gp_Vec tempVector(a,b,c); - tempVector.Normalize();//just in case. - tempVector *= (d+1.0); + gp_Vec tempVector(a, b, c); + tempVector.Normalize(); // just in case. + tempVector *= (d + 1.0); gp_Pnt refPoint(0.0, 0.0, 0.0); refPoint.Translate(tempVector); BRepPrimAPI_MakeHalfSpace mkSolid(TopoDS::Face(face.getShape()), refPoint); TopoShape solid(idx); std::string prefix(op); - if(idx>1) { + if (idx > 1) { prefix += '_'; prefix += std::to_string(idx); } - solid.makEShape(mkSolid,face,prefix.c_str()); + solid.makeElementShape(mkSolid, face, prefix.c_str()); BRepAlgoAPI_Cut mkCut(shape.getShape(), solid.getShape()); if (mkCut.IsDone()) { - TopoShape res(shape.Tag,shape.Hasher); + TopoShape res(shape.Tag, shape.Hasher); std::vector shapes; shapes.push_back(shape); shapes.push_back(solid); - res.makEShape(mkCut,shapes,prefix.c_str()); - for(auto &face : res.getSubTopoShapes(TopAbs_FACE)) { + res.makeElementShape(mkCut, shapes, prefix.c_str()); + for (auto& face : res.getSubTopoShapes(TopAbs_FACE)) { BRepAdaptor_Surface adapt(TopoDS::Face(face.getShape())); if (adapt.GetType() == GeomAbs_Plane) { gp_Pln plane = adapt.Plane(); - if (plane.Axis().IsParallel(slicePlane.Axis(), Precision::Confusion()) && - plane.Distance(slicePlane.Location()) < Precision::Confusion()) { - auto repaired_wires = TopoShape(face.Tag).makEWires( - face.getSubTopoShapes(TopAbs_EDGE),prefix.c_str(),true).getSubTopoShapes(TopAbs_WIRE); - wires.insert(wires.end(),repaired_wires.begin(),repaired_wires.end()); + if (plane.Axis().IsParallel(slicePlane.Axis(), Precision::Confusion()) + && plane.Distance(slicePlane.Location()) < Precision::Confusion()) { + auto repaired_wires = TopoShape(face.Tag) + .makeElementWires(face.getSubTopoShapes(TopAbs_EDGE), + prefix.c_str(), + true) + .getSubTopoShapes(TopAbs_WIRE); + wires.insert(wires.end(), repaired_wires.begin(), repaired_wires.end()); } } } diff --git a/src/Mod/Part/App/CrossSection.h b/src/Mod/Part/App/CrossSection.h index b34829cb16..52c6b65e12 100644 --- a/src/Mod/Part/App/CrossSection.h +++ b/src/Mod/Part/App/CrossSection.h @@ -56,8 +56,8 @@ private: class PartExport TopoCrossSection { public: - TopoCrossSection(double a, double b, double c, const TopoShape& s, const char *op=0); - void slice(int idx, double d, std::vector &wires) const; + TopoCrossSection(double a, double b, double c, const TopoShape& s, const char* op = 0); + void slice(int idx, double d, std::vector& wires) const; TopoShape slice(int idx, double d) const; private: @@ -65,11 +65,11 @@ private: void sliceSolid(int idx, double d, const TopoShape&, std::vector& wires) const; private: - double a,b,c; + double a, b, c; const TopoShape& shape; - const char *op; + const char* op; }; -} +} // namespace Part #endif // PART_CROSSSECTION_H diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index aec30fd84c..37db665359 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -1342,7 +1342,8 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &makEMirror(const TopoShape &source, const gp_Ax2& axis, const char *op=nullptr); + TopoShape& + makeElementMirror(const TopoShape& source, const gp_Ax2& axis, const char* op = nullptr); /** Make a mirrored shape * * @param source: the source shape @@ -1352,15 +1353,16 @@ public: * * @return Return the new shape. The TopoShape itself is not modified. */ - TopoShape makEMirror(const gp_Ax2& ax, const char *op=nullptr) const { - return TopoShape(0,Hasher).makEMirror(*this,ax,op); + TopoShape makeElementMirror(const gp_Ax2& ax, const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementMirror(*this, ax, op); } /** Make a cross section slice * * @param source: the source shape * @param dir: direction of the normal of the section plane - * @param d: distance to move the section plane + * @param distance: distance to move the section plane * @param op: optional string to be encoded into topo naming for indicating * the operation * @@ -1369,19 +1371,23 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &makESlice(const TopoShape &source, const Base::Vector3d& dir, double d, const char *op=nullptr); + TopoShape& makeElementSlice(const TopoShape& source, + const Base::Vector3d& dir, + double distance, + const char* op = nullptr); /** Make a cross section slice * * @param source: the source shape * @param dir: direction of the normal of the section plane - * @param d: distance to move the section plane + * @param distance: distance to move the section plane * @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 makESlice(const Base::Vector3d& dir, double d, const char *op=nullptr) const { - return TopoShape(0,Hasher).makESlice(*this,dir,d,op); + TopoShape makeElementSlice(const Base::Vector3d& dir, double distance, const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementSlice(*this, dir, distance, op); } /** Make multiple cross section slices @@ -1397,8 +1403,10 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &makESlices(const TopoShape &source, const Base::Vector3d& dir, - const std::vector &distances, const char *op=nullptr); + TopoShape& makeElementSlices(const TopoShape& source, + const Base::Vector3d& dir, + const std::vector& distances, + const char* op = nullptr); /** Make multiple cross section slices * * @param source: the source shape @@ -1409,8 +1417,11 @@ public: * * @return Return the new shape. The TopoShape itself is not modified. */ - TopoShape makESlices(const Base::Vector3d &dir, const std::vector &distances, const char *op=nullptr) const { - return TopoShape(0,Hasher).makESlices(*this,dir,distances,op); + TopoShape makeElementSlices(const Base::Vector3d& dir, + const std::vector& distances, + const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementSlices(*this, dir, distances, op); } /* Make fillet shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index efb1716c6a..c1478031e0 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ #endif #include "modelRefine.h" +#include "CrossSection.h" #include "TopoShape.h" #include "TopoShapeOpCode.h" #include "TopoShapeCache.h" @@ -2596,28 +2598,52 @@ struct MapperThruSections: MapperMaker } }; -TopoShape &TopoShape::makESlice(const TopoShape &shape, - const Base::Vector3d& dir, double d, const char *op) +TopoShape& TopoShape::makeElementMirror(const TopoShape& shape, const gp_Ax2& ax2, const char* op) { - if(shape.isNull()) - HANDLE_NULL_SHAPE; - TopoCrossSection cs(dir.x, dir.y, dir.z,shape,op); - TopoShape res = cs.slice(1,d); + if (!op) { + op = Part::OpCodes::Mirror; + } + + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + gp_Trsf mat; + mat.SetMirror(ax2); + TopLoc_Location loc = shape.getShape().Location(); + gp_Trsf placement = loc.Transformation(); + mat = placement * mat; + BRepBuilderAPI_Transform mkTrf(shape.getShape(), mat); + return makeElementShape(mkTrf, shape, op); +} + +TopoShape& TopoShape::makeElementSlice(const TopoShape& shape, + const Base::Vector3d& dir, + double distance, + const char* op) +{ + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + TopoCrossSection cs(dir.x, dir.y, dir.z, shape, op); + TopoShape res = cs.slice(1, distance); setShape(res._Shape); Hasher = res.Hasher; resetElementMap(res.elementMap()); return *this; } -TopoShape &TopoShape::makESlices(const TopoShape &shape, - const Base::Vector3d& dir, const std::vector &d, const char *op) +TopoShape& TopoShape::makeElementSlices(const TopoShape& shape, + const Base::Vector3d& dir, + const std::vector& distances, + const char* op) { std::vector wires; - TopoCrossSection cs(dir.x, dir.y, dir.z, shape,op); - int i=0; - for(auto &dd : d) - cs.slice(++i,dd,wires); - return makECompound(wires,op,false); + TopoCrossSection cs(dir.x, dir.y, dir.z, shape, op); + int index = 0; + for (auto& distance : distances) { + cs.slice(++index, distance, wires); + } + return makeElementCompound(wires, op, SingleShapeCompoundCreationPolicy::returnShape); } TopoShape& TopoShape::makeElementFillet(const TopoShape& shape, @@ -2774,7 +2800,14 @@ TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, const std::vector& shapes, const char* op) { - return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); + TopoDS_Shape shape; + // OCCT 7.3.x requires calling Solid() and not Shape() to function correctly + if ( typeid(mkShape) == typeid(BRepPrimAPI_MakeHalfSpace) ) { + shape = static_cast(mkShape).Solid(); + } else { + shape = mkShape.Shape(); + } + return makeShapeWithElementMap(shape, MapperMaker(mkShape), shapes, op); } TopoShape& TopoShape::makeElementLoft(const std::vector& shapes, diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index ae42a1f19e..9bdde05a58 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -141,21 +141,23 @@ testing::AssertionResult elementsMatch(const TopoShape& shape, const std::vector& names) { auto elements = shape.getElementMap(); - if (std::find_first_of(elements.begin(), - elements.end(), - names.begin(), - names.end(), - [&](const Data::MappedElement& element, const std::string& name) { - return element.name.toString() == name; - }) - == elements.end()) { - std::stringstream output; - output << "{"; - for (const auto& element : elements) { - output << "\"" << element.name.toString() << "\", "; + if (!elements.empty() || !names.empty()) { + if (std::find_first_of(elements.begin(), + elements.end(), + names.begin(), + names.end(), + [&](const Data::MappedElement& element, const std::string& name) { + return element.name.toString() == name; + }) + == elements.end()) { + std::stringstream output; + output << "{"; + for (const auto& element : elements) { + output << "\"" << element.name.toString() << "\", "; + } + output << "}"; + return testing::AssertionFailure() << output.str(); } - output << "}"; - return testing::AssertionFailure() << output.str(); } return testing::AssertionSuccess(); } diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 16f9578827..b630f958fc 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1612,7 +1612,7 @@ TEST_F(TopoShapeExpansionTest, makeElementChamfer) // Act cube1TS.makeElementChamfer({cube1TS}, edges, .05, .05); auto elements = elementMap(cube1TS); - // Assert + // Assert shape is correct EXPECT_EQ(cube1TS.countSubElements("Wire"), 26); EXPECT_FLOAT_EQ(getArea(cube1TS.getShape()), 5.640996); // Assert that we're creating a correct element map @@ -1729,7 +1729,7 @@ TEST_F(TopoShapeExpansionTest, makeElementFillet) // Act cube1TS.makeElementFillet({cube1TS}, edges, .05, .05); auto elements = elementMap(cube1TS); - // Assert + // Assert shape is correct EXPECT_EQ(cube1TS.countSubElements("Wire"), 26); EXPECT_FLOAT_EQ(getArea(cube1TS.getShape()), 5.739646); // Assert that we're creating a correct element map @@ -1845,4 +1845,113 @@ TEST_F(TopoShapeExpansionTest, makeElementFillet) })); } +TEST_F(TopoShapeExpansionTest, makeElementSlice) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); // TopoShape version works too + TopoShape cube1TS {cube1}; // Adding a tag here only adds text in each mapped name + auto faces = cube1TS.getSubShapes(TopAbs_FACE); + TopoShape slicer {faces[0]}; + Base::Vector3d direction {1.0, 0.0, 0.0}; + // Act + auto& result = slicer.makeElementSlice(cube1TS, direction, 0.5); + // Assert shape is correct + EXPECT_FLOAT_EQ(getLength(result.getShape()), 4); + EXPECT_EQ(TopAbs_ShapeEnum::TopAbs_WIRE, result.getShape().ShapeType()); + // Assert that we're creating a correct element map + EXPECT_TRUE(result.getMappedChildElements().empty()); + EXPECT_TRUE(allElementsMatch(result, + { + "Edge1;SLC;D1;MAK", + "Edge1;SLC;D2;MAK", + "Edge1;SLC;D3;MAK", + "Edge1;SLC;MAK", + "Vertex1;SLC;D1;MAK", + "Vertex1;SLC;D2;MAK", + "Vertex1;SLC;MAK", + "Vertex2;SLC;D1;MAK", + "Vertex2;SLC;D2;MAK", + "Vertex2;SLC;MAK", + })); +} + +TEST_F(TopoShapeExpansionTest, makeElementSlices) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape cube1TS {cube1, 1L}; + auto faces = cube1TS.getSubShapes(TopAbs_FACE); + TopoShape slicer {faces[0]}; + Base::Vector3d direction {1.0, 0.0, 0.0}; + // Act + auto& result = slicer.makeElementSlices(cube1TS, direction, {0.25, 0.5, 0.75}); + auto subTopoShapes = result.getSubTopoShapes(TopAbs_WIRE); + // Assert shape is correct + EXPECT_EQ(result.countSubElements("Wire"), 3); + EXPECT_FLOAT_EQ(getLength(result.getShape()), 12); + EXPECT_FLOAT_EQ(getLength(subTopoShapes[0].getShape()), 4); + EXPECT_EQ(TopAbs_ShapeEnum::TopAbs_COMPOUND, result.getShape().ShapeType()); + EXPECT_EQ(TopAbs_ShapeEnum::TopAbs_WIRE, subTopoShapes[0].getShape().ShapeType()); + EXPECT_EQ(TopAbs_ShapeEnum::TopAbs_WIRE, subTopoShapes[1].getShape().ShapeType()); + EXPECT_EQ(TopAbs_ShapeEnum::TopAbs_WIRE, subTopoShapes[2].getShape().ShapeType()); + // Assert that we're creating a correct element map + EXPECT_TRUE(result.getMappedChildElements().empty()); + EXPECT_TRUE(elementsMatch(result, {"Edge1;SLC;:H1:4,E;D1;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC;:H1:4,E;D2;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC;:H1:4,E;D3;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC;:H1:4,E;MAK;:H1:4,E", + "Edge1;SLC_2;:H1:6,E;D1;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC_2;:H1:6,E;D2;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC_2;:H1:6,E;D3;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC_2;:H1:6,E;MAK;:H1:4,E", + "Edge1;SLC_3;:H1:6,E;D1;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC_3;:H1:6,E;D2;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC_3;:H1:6,E;D3;:H1:3,E;MAK;:H1:4,E", + "Edge1;SLC_3;:H1:6,E;MAK;:H1:4,E", + "Vertex1;SLC;:H1:4,V;D2;:H1:3,V;MAK;:H1:4,V", + "Vertex1;SLC;:H1:4,V;MAK;:H1:4,V", + "Vertex1;SLC_2;:H1:6,V;D2;:H1:3,V;MAK;:H1:4,V", + "Vertex1;SLC_2;:H1:6,V;MAK;:H1:4,V", + "Vertex1;SLC_3;:H1:6,V;D2;:H1:3,V;MAK;:H1:4,V", + "Vertex1;SLC_3;:H1:6,V;MAK;:H1:4,V", + "Vertex2;SLC;:H1:4,V;D1;:H1:3,V;MAK;:H1:4,V", + "Vertex2;SLC;:H1:4,V;MAK;:H1:4,V", + "Vertex2;SLC_2;:H1:6,V;D1;:H1:3,V;MAK;:H1:4,V", + "Vertex2;SLC_2;:H1:6,V;MAK;:H1:4,V", + "Vertex2;SLC_3;:H1:6,V;D1;:H1:3,V;MAK;:H1:4,V", + "Vertex2;SLC_3;:H1:6,V;MAK;:H1:4,V"})); + EXPECT_TRUE(subTopoShapes[0].getElementMap().empty()); +} + +TEST_F(TopoShapeExpansionTest, makeElementMirror) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape cube1TS {cube1, 1L}; + auto edges = cube1TS.getSubTopoShapes(TopAbs_EDGE); + gp_Ax2 axis {gp_Pnt {0, 0, 0}, gp_Dir {1, 0, 0}}; + // Act + auto& result = cube1TS.makeElementMirror(cube1TS, axis); + auto elements = elementMap(cube1TS); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-1, 0, 0, 0, 1, 1))); + EXPECT_EQ(result.countSubElements("Wire"), 6); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1); + EXPECT_EQ(TopAbs_ShapeEnum::TopAbs_SOLID, result.getShape().ShapeType()); + // Assert that we're creating a correct element map + EXPECT_TRUE(result.getMappedChildElements().empty()); + EXPECT_TRUE( + elementsMatch(result, + {"Edge10;:M;MIR;:H1:7,E", "Edge11;:M;MIR;:H1:7,E", "Edge12;:M;MIR;:H1:7,E", + "Edge1;:M;MIR;:H1:7,E", "Edge2;:M;MIR;:H1:7,E", "Edge3;:M;MIR;:H1:7,E", + "Edge4;:M;MIR;:H1:7,E", "Edge5;:M;MIR;:H1:7,E", "Edge6;:M;MIR;:H1:7,E", + "Edge7;:M;MIR;:H1:7,E", "Edge8;:M;MIR;:H1:7,E", "Edge9;:M;MIR;:H1:7,E", + "Face1;:M;MIR;:H1:7,F", "Face2;:M;MIR;:H1:7,F", "Face3;:M;MIR;:H1:7,F", + "Face4;:M;MIR;:H1:7,F", "Face5;:M;MIR;:H1:7,F", "Face6;:M;MIR;:H1:7,F", + "Vertex1;:M;MIR;:H1:7,V", "Vertex2;:M;MIR;:H1:7,V", "Vertex3;:M;MIR;:H1:7,V", + "Vertex4;:M;MIR;:H1:7,V", "Vertex5;:M;MIR;:H1:7,V", "Vertex6;:M;MIR;:H1:7,V", + "Vertex7;:M;MIR;:H1:7,V", "Vertex8;:M;MIR;:H1:7,V"})); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From a09da5af5253c5418f451b7c5bce0868962c27b1 Mon Sep 17 00:00:00 2001 From: bgbsww <120601209+bgbsww@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:54:32 -0500 Subject: [PATCH 3/3] Correct incorrect comment --- src/Mod/Part/App/TopoShapeExpansion.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 3fadc5ecd9..b89c7fda71 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2705,14 +2705,10 @@ struct MapperThruSections: MapperMaker // TODO: This method does not appear to ever be called in the codebase, and it is probably // broken, because using TopoShape() with no parameters means the result will not have an -// element Map. If ever restored, code like this should be used to make a tag. +// element Map. //TopoShape& TopoShape::makeElementSolid(const std::vector& shapes, const char* op) //{ -// static std::random_device _RD; -// static std::mt19937 _RGEN(_RD()); -// static std::uniform_int_distribution<> _RDIST(1, 10000); -// long idx = _RDIST(_RGEN); -// return makeElementSolid(TopoShape(idx).makeElementCompound(shapes), op); +// return makeElementSolid(TopoShape().makeElementCompound(shapes), op); //} TopoShape& TopoShape::makeElementSolid(const TopoShape& shape, const char* op)