Toponaming/Part: Cleaning and tests for makeElementRevolve, makeElementPrism, makeElementPrismUntil

This commit is contained in:
bgbsww
2024-02-23 14:51:27 -05:00
parent c3d152540c
commit 1fa89ea4c1
3 changed files with 368 additions and 206 deletions

View File

@@ -957,14 +957,16 @@ public:
* a self reference so that multiple operations can be carried out
* for the same shape in the same line of code.
*/
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);
// 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
*
@@ -983,23 +985,25 @@ public:
*
* @return Return the generated new shape. The TopoShape itself is not modified.
*/
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);
}
// 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

View File

@@ -3097,198 +3097,227 @@ TopoShape& TopoShape::makeElementLoft(const std::vector<TopoShape>& 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);
}
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)
TopoShape& TopoShape::makeElementPrism(const TopoShape& base, const gp_Vec& vec, 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);
if (!op) {
op = Part::OpCodes::Extrude;
}
TopoShape uptoface(_uptoface);
TopoShape base(_base);
if (base.isNull()) {
Mode = PrismMode::None;
base = profile;
FC_THROWM(NullShapeException, "Null shape");
}
// 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<TopoShape> 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()
#if OCC_VERSION_HEX >= 0x060502
, Precision::Confusion()
#endif
);
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<TopoShape> 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;
BRepPrimAPI_MakePrism mkPrism(base.getShape(), vec);
return makeElementShape(mkPrism, base, op);
}
TopoShape &TopoShape::makeElementRevolve(const TopoShape &_base, const gp_Ax1& axis,
double d, const char *face_maker, const char *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<TopoShape> 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<TopoShape> 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;
if (!op) {
op = Part::OpCodes::Revolve;
}
TopoShape base(_base);
if(base.isNull())
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);
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,

View File

@@ -1966,4 +1966,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)