diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 8c5e8cef07..1a599598ec 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -258,6 +258,12 @@ public: void operator=(const TopoShape&); + bool operator == (const TopoShape &other) const { + return _Shape.IsEqual(other._Shape); + } + + virtual bool isSame (const Data::ComplexGeoData &other) const; + /** @name Placement control */ //@{ /// set the transformation of the CasCade Shape @@ -854,6 +860,148 @@ public: offsetMode,join,op); } + + /** Make revolved shell around a basis shape + * + * @param base: the base shape + * @param axis: the revolving axis + * @param d: rotation angle in degree + * @param face_maker: optional type name of the the maker used to make a + * face from basis shape + * @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 &makeElementRevolve(const TopoShape &base, const gp_Ax1& axis, double d, + const char *face_maker=0, const char *op=nullptr); + + /** Make revolved shell around a basis shape + * + * @param base: the basis shape + * @param axis: the revolving axis + * @param d: rotation angle in degree + * @param face_maker: optional type name of the the maker used to make a + * face from basis shape + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the generated new shape. The TopoShape itself is not modified. + */ + TopoShape makeElementRevolve(const gp_Ax1& axis, double d, + const char *face_maker=nullptr, const char *op=nullptr) const { + return TopoShape(0,Hasher).makeElementRevolve(*this,axis,d,face_maker,op); + } + + + /** Make a prism that is a linear sweep of a basis shape + * + * @param base: the basis shape + * @param vec: vector defines the sweep direction + * @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 &makeElementPrism(const TopoShape &base, const gp_Vec& vec, const char *op=nullptr); + + /** Make a prism that is a linear sweep of this shape + * + * @param vec: vector defines the sweep direction + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the generated new shape. The TopoShape itself is not modified. + */ + TopoShape makeElementPrism(const gp_Vec& vec, const char *op=nullptr) const { + return TopoShape(0,Hasher).makeElementPrism(*this,vec,op); + } + + /// Operation mode for makeElementPrismUntil() + enum PrismMode { + /// Remove the generated prism shape from the base shape with boolean cut + CutFromBase = 0, + /// Add generated prism shape to the base shape with fusion + FuseWithBase = 1, + /// Return the generated prism shape without base shape + None = 2 + }; + /** Make a prism that is either depression or protrusion of a profile shape up to a given face + * + * @param base: the base shape + * @param profile: profile shape used for sweeping to make the prism + * @param supportFace: optional face serves to determining the type of + * operation. If it is inside the basis shape, a local + * operation such as glueing can be performed. + * @param upToFace: sweep the profile up until this give face. + * @param direction: the direction to sweep the profile + * @param mode: defines what shape to return. @sa PrismMode + * @param checkLimits: If true, then remove limit (boundary) of up to face. + * If false, then the generate prism may go beyond the + * boundary of the up to face. + * @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. + */ + // TODO: This code was transferred in Feb 2024 as part of the toponaming project, but appears to be + // unused. It is potentially useful if debugged. +// TopoShape &makeElementPrismUntil(const TopoShape &base, +// const TopoShape& profile, +// const TopoShape& supportFace, +// const TopoShape& upToFace, +// const gp_Dir& direction, +// PrismMode mode, +// Standard_Boolean checkLimits = Standard_True, +// const char *op=nullptr); + + /** Make a prism based on this shape that is either depression or protrusion of a profile shape up to a given face + * + * @param profile: profile shape used for sweeping to make the prism + * @param supportFace: optional face serves to determining the type of + * operation. If it is inside the basis shape, a local + * operation such as glueing can be performed. + * @param upToFace: sweep the profile up until this give face. + * @param direction: the direction to sweep the profile + * @param mode: defines what shape to return. @sa PrismMode + * @param checkLimits: If true, then remove limit (boundary) of up to face. + * If false, then the generate prism may go beyond the + * boundary of the up to face. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the generated new shape. The TopoShape itself is not modified. + */ + // TODO: This code was transferred in Feb 2024 as part of the toponaming project, but appears to be + // unused. It is potentially useful if debugged. +// TopoShape makeElementPrismUntil(const TopoShape& profile, +// const TopoShape& supportFace, +// const TopoShape& upToFace, +// const gp_Dir& direction, +// PrismMode mode, +// Standard_Boolean checkLimits = Standard_True, +// const char *op=nullptr) const +// { +// return TopoShape(0,Hasher).makeElementPrismUntil(*this, +// profile, +// supportFace, +// upToFace, +// direction, +// mode, +// checkLimits, +// op); +// } + + /* Make a shell or solid by sweeping profile wire along a spine * * @params sources: source shapes. The first shape is used as spine. The @@ -1928,6 +2076,22 @@ public: return TopoShape(0, Hasher).makeElementShape(mkShape, *this, op); } + /** Specialized shape making for BRepBuilderAPI_MakePrism with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param sources: list of source shapes. + * @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 built by the shape maker. 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 &makeElementShape(BRepFeat_MakePrism &mkShape, + const std::vector &sources, const TopoShape &uptoface, const char *op); + /* Toponaming migration, February 2014: * Note that the specialized versions of makeElementShape for operations that do not * inherit from BRepBuilderAPI_MakeShape ( like BRepBuilderAPI_Sewing ) have been removed. diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index b89c7fda71..33db6f0c46 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -88,6 +88,10 @@ #include "Geometry.h" #include +#include +#include +#include +#include FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -2703,6 +2707,103 @@ struct MapperThruSections: MapperMaker } }; +struct MapperPrism: MapperMaker { + std::unordered_map vertexMap; + ShapeMapper::ShapeMap edgeMap; + + MapperPrism(BRepFeat_MakePrism &maker, const TopoShape &upTo) + :MapperMaker(maker) + { + (void)upTo; + + std::vector shapes; + for(TopTools_ListIteratorOfListOfShape it(maker.FirstShape());it.More();it.Next()) + shapes.push_back(it.Value()); + + if (shapes.size()) { + // It seems that BRepFeat_MakePrism::newEdges() does not return + // edges generated by extruding the profile vertices. The following + // code assumes BRepFeat_MakePrism::myFShape is the profile, and + // FirstShape() returns the corresponding faces in the new shape, + // i.e. the bottom profile, and add all edges that shares a + // vertex with the profiles as new edges. + + std::unordered_set edgeSet; + TopoShape bottom; + bottom.makeElementCompound(shapes, nullptr, TopoShape::SingleShapeCompoundCreationPolicy::returnShape); + TopoShape shape(maker.Shape()); + for (auto &vertex : bottom.getSubShapes(TopAbs_VERTEX)) { + for (auto &e : shape.findAncestorsShapes(vertex, TopAbs_EDGE)) { + // Make sure to not visit the the same edge twice. + // And check only edge that are not found in the bottom profile + if (!edgeSet.insert(e).second && !bottom.findShape(e)) { + auto otherVertex = TopExp::FirstVertex(TopoDS::Edge(e)); + if (otherVertex.IsSame(vertex)) + otherVertex = TopExp::LastVertex(TopoDS::Edge(e)); + vertexMap[vertex] = otherVertex; + } + } + } + + // Now map each edge in the bottom profile to the extrueded top + // profile. vertexMap created above gives us each pair of vertexes + // of the bottom and top profile. We use it to find the + // corresponding edges in the top profile, what an extra criteria + // for disambiguation. That is, the pair of edges (bottom and top) + // must belong to the same face. + for (auto &edge : bottom.getSubShapes(TopAbs_EDGE)) { + std::vector indices; + auto first = TopExp::FirstVertex(TopoDS::Edge(edge)); + auto last = TopExp::LastVertex(TopoDS::Edge(edge)); + auto itFirst = vertexMap.find(first); + auto itLast = vertexMap.find(last); + if (itFirst == vertexMap.end() || itLast ==vertexMap.end()) + continue; + std::vector faces; + for (int idx : shape.findAncestors(edge, TopAbs_FACE)) + faces.push_back(shape.getSubTopoShape(TopAbs_FACE, idx)); + if (faces.empty()) + continue; + for (int idx : shape.findAncestors(itFirst->second, TopAbs_EDGE)) { + auto e = shape.getSubTopoShape(TopAbs_EDGE, idx); + if (!e.findShape(itLast->second)) + continue; + for (auto &face : faces) { + if (!face.findShape(e.getShape())) + continue; + auto &entry = edgeMap[edge]; + if (entry.shapeSet.insert(e.getShape()).second) + entry.shapes.push_back(e.getShape()); + } + } + } + } + } + virtual const std::vector &generated(const TopoDS_Shape &s) const override { + _res.clear(); + switch(s.ShapeType()) { + case TopAbs_VERTEX: { + auto it = vertexMap.find(s); + if (it != vertexMap.end()) { + _res.push_back(it->second); + return _res; + } + break; + } + case TopAbs_EDGE: { + auto it = edgeMap.find(s); + if (it != edgeMap.end()) + return it->second.shapes; + break; + } + default: + break; + } + MapperMaker::generated(s); + return _res; + } +}; + // 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. @@ -2974,6 +3075,18 @@ TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, return makeShapeWithElementMap(shape, MapperMaker(mkShape), shapes, op); } +TopoShape &TopoShape::makeElementShape(BRepFeat_MakePrism &mkShape, + const std::vector &sources, + const TopoShape &upTo, + const char *op) +{ + if(!op) op = Part::OpCodes::Prism; + MapperPrism mapper(mkShape, upTo); + makeShapeWithElementMap(mkShape.Shape(),mapper,sources,op); + return *this; +} + + TopoShape& TopoShape::makeElementLoft(const std::vector& shapes, IsSolid isSolid, IsRuled isRuled, @@ -3041,6 +3154,229 @@ TopoShape& TopoShape::makeElementLoft(const std::vector& shapes, op); } +TopoShape& TopoShape::makeElementPrism(const TopoShape& base, const gp_Vec& vec, const char* op) +{ + if (!op) { + op = Part::OpCodes::Extrude; + } + if (base.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + BRepPrimAPI_MakePrism mkPrism(base.getShape(), vec); + return makeElementShape(mkPrism, base, op); +} + +// TODO: This code was transferred in Feb 2024 as part of the toponaming project, but appears to be +// unused. It is potentially useful if debugged. +//TopoShape& TopoShape::makeElementPrismUntil(const TopoShape& _base, +// const TopoShape& profile, +// const TopoShape& supportFace, +// const TopoShape& __uptoface, +// const gp_Dir& direction, +// PrismMode Mode, +// Standard_Boolean checkLimits, +// const char* op) +//{ +// if (!op) { +// op = Part::OpCodes::Prism; +// } +// +// BRepFeat_MakePrism PrismMaker; +// +// TopoShape _uptoface(__uptoface); +// if (checkLimits && _uptoface.shapeType(true) == TopAbs_FACE +// && !BRep_Tool::NaturalRestriction(TopoDS::Face(_uptoface.getShape()))) { +// // When using the face with BRepFeat_MakePrism::Perform(const TopoDS_Shape& Until) +// // then the algorithm expects that the 'NaturalRestriction' flag is set in order +// // to work as expected. +// BRep_Builder builder; +// _uptoface = _uptoface.makeElementCopy(); +// builder.NaturalRestriction(TopoDS::Face(_uptoface.getShape()), Standard_True); +// } +// +// TopoShape uptoface(_uptoface); +// TopoShape base(_base); +// +// if (base.isNull()) { +// Mode = PrismMode::None; +// base = profile; +// } +// +// // Check whether the face has limits or not. Unlimited faces have no wire +// // Note: Datum planes are always unlimited +// if (checkLimits && uptoface.hasSubShape(TopAbs_WIRE)) { +// TopoDS_Face face = TopoDS::Face(uptoface.getShape()); +// bool remove_limits = false; +// // Remove the limits of the upToFace so that the extrusion works even if profile is larger +// // than the upToFace +// for (auto& sketchface : profile.getSubTopoShapes(TopAbs_FACE)) { +// // Get outermost wire of sketch face +// TopoShape outerWire = sketchface.splitWires(); +// BRepProj_Projection proj(TopoDS::Wire(outerWire.getShape()), face, direction); +// if (!proj.More() || !proj.Current().Closed()) { +// remove_limits = true; +// break; +// } +// } +// +// // It must also be checked that all projected inner wires of the upToFace +// // lie outside the sketch shape. If this is not the case then the sketch +// // shape is not completely covered by the upToFace. See #0003141 +// if (!remove_limits) { +// std::vector wires; +// uptoface.splitWires(&wires); +// for (auto& w : wires) { +// BRepProj_Projection proj(TopoDS::Wire(w.getShape()), +// profile.getShape(), +// -direction); +// if (proj.More()) { +// remove_limits = true; +// break; +// } +// } +// } +// +// if (remove_limits) { +// // Note: Using an unlimited face every time gives unnecessary failures for concave faces +// TopLoc_Location loc = face.Location(); +// BRepAdaptor_Surface adapt(face, Standard_False); +// // use the placement of the adapter, not of the upToFace +// loc = TopLoc_Location(adapt.Trsf()); +// BRepBuilderAPI_MakeFace mkFace(adapt.Surface().Surface(), Precision::Confusion()); +// if (!mkFace.IsDone()) { +// remove_limits = false; +// } +// else { +// uptoface.setShape(located(mkFace.Shape(), loc), false); +// } +// } +// } +// +// TopoShape uptofaceCopy = uptoface; +// bool checkBase = false; +// auto retry = [&]() { +// if (!uptoface.isSame(_uptoface)) { +// // retry using the original up to face in case unnecessary failure +// // due to removing the limits +// uptoface = _uptoface; +// return true; +// } +// if ((!_base.isNull() && base.isSame(_base)) || (_base.isNull() && base.isSame(profile))) { +// // It is unclear under exactly what condition extrude up to face +// // can fail. Either the support face or the up to face must be part +// // of the base, or maybe some thing else. +// // +// // To deal with it, we retry again by disregard the supplied base, +// // and use up to face to extrude our own base. Later on, use the +// // supplied base (i.e. _base) to calculate the final shape if the +// // mode is FuseWithBase or CutWithBase. +// checkBase = true; +// uptoface = uptofaceCopy; +// base.makeElementPrism(_uptoface, direction); +// return true; +// } +// return false; +// }; +// +// std::vector srcShapes; +// TopoShape result; +// for (;;) { +// try { +// result = base; +// +// // We do not rely on BRepFeat_MakePrism to perform fuse or cut for +// // us because of its poor support of shape history. +// auto mode = PrismMode::None; +// +// for (auto& face : profile.getSubTopoShapes( +// profile.hasSubShape(TopAbs_FACE) ? TopAbs_FACE : TopAbs_WIRE)) { +// srcShapes.clear(); +// if (!profile.isNull() && !result.findShape(profile.getShape())) { +// srcShapes.push_back(profile); +// } +// if (!supportFace.isNull() && !result.findShape(supportFace.getShape())) { +// srcShapes.push_back(supportFace); +// } +// +// // DO NOT include uptoface for element mapping. Because OCCT +// // BRepFeat_MakePrism will report all top extruded face being +// // modified by the uptoface. If there are more than one face in +// // the profile, this will cause uncessary duplicated element +// // mapped name. And will also disrupte element history tracing +// // back to the profile sketch. +// // +// // if (!uptoface.isNull() && !this->findShape(uptoface.getShape())) +// // srcShapes.push_back(uptoface); +// +// srcShapes.push_back(result); +// +// PrismMaker.Init(result.getShape(), +// face.getShape(), +// TopoDS::Face(supportFace.getShape()), +// direction, +// mode, +// Standard_False); +// mode = PrismMode::FuseWithBase; +// +// PrismMaker.Perform(uptoface.getShape()); +// +// if (!PrismMaker.IsDone() || PrismMaker.Shape().IsNull()) { +// FC_THROWM(Base::CADKernelError, "BRepFeat_MakePrism: extrusion failed"); +// } +// +// result.makeElementShape(PrismMaker, srcShapes, uptoface, op); +// } +// break; +// } +// catch (Base::Exception&) { +// if (!retry()) { +// throw; +// } +// } +// catch (Standard_Failure&) { +// if (!retry()) { +// throw; +// } +// } +// } +// +// if (!_base.isNull() && Mode != PrismMode::None) { +// if (Mode == PrismMode::FuseWithBase) { +// result.makeElementFuse({_base, result}); +// } +// else { +// result.makeElementCut({_base, result}); +// } +// } +// +// *this = result; +// return *this; +//} + +TopoShape& TopoShape::makeElementRevolve(const TopoShape& _base, + const gp_Ax1& axis, + double d, + const char* face_maker, + const char* op) +{ + if (!op) { + op = Part::OpCodes::Revolve; + } + + TopoShape base(_base); + if (base.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + if (face_maker && !base.hasSubShape(TopAbs_FACE)) { + if (!base.hasSubShape(TopAbs_WIRE)) { + base = base.makeElementWires(); + } + base = base.makeElementFace(nullptr, face_maker, nullptr); + } + BRepPrimAPI_MakeRevol mkRevol(base.getShape(), axis, d); + return makeElementShape(mkRevol, base, op); +} + TopoShape& TopoShape::makeElementDraft(const TopoShape& shape, const std::vector& _faces, const gp_Dir& pullDirection, @@ -3955,4 +4291,15 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker, return *this; } +bool TopoShape::isSame(const Data::ComplexGeoData &_other) const +{ + if(!_other.isDerivedFrom(TopoShape::getClassTypeId())) + return false; + + const auto &other = static_cast(_other); + return Tag == other.Tag + && Hasher == other.Hasher + && _Shape.IsEqual(other._Shape); +} + } // namespace Part diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index c8cbf4dd49..9c05d7056f 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2075,4 +2075,133 @@ TEST_F(TopoShapeExpansionTest, makeElementSolid) EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;SLD;:H1:4,F")); } +TEST_F(TopoShapeExpansionTest, makeElementRevolve) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape topoShape1 {cube1, 1L}; + gp_Ax1 axis {gp_Pnt {0, 0, 0}, gp_Dir {0, 1, 0}}; + double angle = 45; + auto subTopoFaces = topoShape1.getSubTopoShapes(TopAbs_FACE); + subTopoFaces[0].Tag = 2L; + // Act + TopoShape result = subTopoFaces[0].makeElementRevolve(axis, angle); + auto elements = elementMap(result); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch( + bb, + Base::BoundBox3d(0.0, 0.0, 0.0, 0.85090352453411933, 1.0, 1.0))); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 0.50885141); + // Assert elementMap is correct + EXPECT_TRUE( + elementsMatch(result, + { + "Edge1;:G;RVL;:H2:7,F", + "Edge1;:G;RVL;:H2:7,F;:U;RVL;:H2:7,E", + "Edge1;:G;RVL;:H2:7,F;:U;RVL;:H2:7,E;:L(Edge2;:G;RVL;:H2:7,F;:U;RVL;:H2:" + "7,E|Edge3;:G;RVL;:H2:7,F;:U;RVL;:H2:7,E|Edge4;RVL;:H2:4,E);RVL;:H2:62,F", + "Edge1;:G;RVL;:H2:7,F;:U;RVL;:H2:7,E;:U;RVL;:H2:7,V", + "Edge1;RVL;:H2:4,E", + "Edge2;:G;RVL;:H2:7,F", + "Edge2;:G;RVL;:H2:7,F;:U;RVL;:H2:7,E", + "Edge2;:G;RVL;:H2:7,F;:U;RVL;:H2:7,E;:U;RVL;:H2:7,V", + "Edge2;RVL;:H2:4,E", + "Edge3;:G;RVL;:H2:7,F", + "Edge3;:G;RVL;:H2:7,F;:U;RVL;:H2:7,E", + "Edge3;RVL;:H2:4,E", + "Edge4;RVL;:H2:4,E", + "Face1;RVL;:H2:4,F", + "Vertex1;:G;RVL;:H2:7,E", + "Vertex1;RVL;:H2:4,V", + "Vertex2;RVL;:H2:4,V", + "Vertex3;:G;RVL;:H2:7,E", + "Vertex3;RVL;:H2:4,V", + "Vertex4;RVL;:H2:4,V", + })); +} + +TEST_F(TopoShapeExpansionTest, makeElementPrism) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape topoShape1 {cube1, 1L}; + auto subTopoFaces = topoShape1.getSubTopoShapes(TopAbs_FACE); + subTopoFaces[0].Tag = 2L; + // Act + TopoShape& result = topoShape1.makeElementPrism(subTopoFaces[0], {0.75, 0, 0}); + auto elements = elementMap(result); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0.0, 0.0, 0.0, 0.75, 1.0, 1.0))); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 0.75); + // Assert elementMap is correct + EXPECT_TRUE(elementsMatch( + result, + { + "Edge1;:G;XTR;:H2:7,F", + "Edge1;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E", + "Edge1;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E;:L(Edge2;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E|Edge3;:G;" + "XTR;:H2:7,F;:U;XTR;:H2:7,E|Edge4;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E);XTR;:H2:74,F", + "Edge1;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E;:U2;XTR;:H2:8,V", + "Edge1;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E;:U;XTR;:H2:7,V", + "Edge1;XTR;:H2:4,E", + "Edge2;:G;XTR;:H2:7,F", + "Edge2;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E", + "Edge2;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E;:U;XTR;:H2:7,V", + "Edge2;XTR;:H2:4,E", + "Edge3;:G;XTR;:H2:7,F", + "Edge3;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E", + "Edge3;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E;:U2;XTR;:H2:8,V", + "Edge3;XTR;:H2:4,E", + "Edge4;:G;XTR;:H2:7,F", + "Edge4;:G;XTR;:H2:7,F;:U;XTR;:H2:7,E", + "Edge4;XTR;:H2:4,E", + "Face1;XTR;:H2:4,F", + "Vertex1;:G;XTR;:H2:7,E", + "Vertex1;XTR;:H2:4,V", + "Vertex2;:G;XTR;:H2:7,E", + "Vertex2;XTR;:H2:4,V", + "Vertex3;:G;XTR;:H2:7,E", + "Vertex3;XTR;:H2:4,V", + "Vertex4;:G;XTR;:H2:7,E", + "Vertex4;XTR;:H2:4,V", + }) + + ); +} + +// TODO: This code was written in Feb 2024 as part of the toponaming project, but appears to be +// unused. It is potentially useful if debugged. +// +// TEST_F(TopoShapeExpansionTest, makeElementPrismUntil) +//{ +// // Arrange +// auto [cube1, cube2] = CreateTwoCubes(); +// TopoShape cube1TS {cube1, 1L}; +// auto subFaces = cube1TS.getSubShapes(TopAbs_FACE); +// auto subTopoFaces = cube1TS.getSubTopoShapes(TopAbs_FACE); +// subTopoFaces[0].Tag = 2L; +// subTopoFaces[1].Tag = 3L; +// auto tr {gp_Trsf()}; +// auto direction = gp_Vec(gp_XYZ(0.0, 0.0, 0.25)); +// tr.SetTranslation(direction); +// auto support = subFaces[0].Moved(TopLoc_Location(tr)); +// auto upto = support.Moved(TopLoc_Location(tr)); +// // Act +// TopoShape result = cube1TS.makeElementPrismUntil(subTopoFaces[0], +// TopoShape(support, 4L), +// TopoShape(upto, 5L), +// direction, +// TopoShape::PrismMode::CutFromBase); +// auto elements = elementMap(result); +// Base::BoundBox3d bb = result.getBoundBox(); +// // Assert shape is correct +// EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0.0, -0.5, 0.0, 1.5, 1.0, 1.0))); +// EXPECT_FLOAT_EQ(getVolume(result.getShape()), 2); +// // Assert elementMap is correct +// EXPECT_TRUE(elementsMatch(result, +// {"Edge1;:G;XTR;:H2:7,F",})); +//} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)