Toponaming/Part: Cleanup makeElementPipeShell, add tests, and some delinting

This commit is contained in:
bgbsww
2024-02-16 13:11:38 -05:00
parent 8d5f411b22
commit 0dd2e686ff
4 changed files with 179 additions and 62 deletions

View File

@@ -95,8 +95,8 @@ class PartExport ShapeSegment: public Data::Segment
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit ShapeSegment(const TopoDS_Shape& ShapeIn)
: Shape(ShapeIn)
explicit ShapeSegment(TopoDS_Shape ShapeIn)
: Shape(std::move(ShapeIn))
{}
ShapeSegment() = default;
std::string getName() const override;
@@ -134,7 +134,8 @@ enum class LinearizeFace
linearizeFaces
};
enum class LinearizeEdge {
enum class LinearizeEdge
{
noEdges,
linearizeEdges
};
@@ -158,7 +159,8 @@ enum class IsClosed
};
/// Option to manage discontinuity in pipe sweeping
enum class TransitionMode {
enum class TransitionMode
{
/** Discontinuities are treated by modification of the sweeping mode.
* The pipe is "transformed" at the fractures of the spine. This mode
* assumes building a self-intersected shell.
@@ -176,10 +178,16 @@ enum class TransitionMode {
* through the point of the spine's fracture. This axis is based on
* cross product of directions tangent to the adjacent segments of the
* spine at their common point.
*/
*/
RoundCorner
};
enum class MakeSolid
{
noSolid,
makeSolid
};
/** The representation for a CAD Shape
*/
// NOLINTNEXTLINE cppcoreguidelines-special-member-functions
@@ -374,11 +382,11 @@ public:
/// Returns true if the expansion of the shape is infinite, false otherwise
bool isInfinite() const;
/// Checks whether the shape is a planar face
bool isPlanar(double tol = 1.0e-7) const;
bool isPlanar(double tol = 1.0e-7) const; // NOLINT
/// Check if this shape is a single linear edge, works on BSplineCurve and BezierCurve
bool isLinearEdge(Base::Vector3d *dir = nullptr, Base::Vector3d *base = nullptr) const;
/// Check if this shape is a single planar face, works on BSplineSurface and BezierSurface
bool isPlanarFace(double tol=1e-7) const;
bool isPlanarFace(double tol=1e-7) const; // NOLINT
//@}
/** @name Boolean operation*/
@@ -763,9 +771,14 @@ public:
* a self reference so that multiple operations can be carried out
* for the same shape in the same line of code.
*/
TopoShape &makEPipeShell(const std::vector<TopoShape> &sources, const Standard_Boolean makeSolid,
const Standard_Boolean isFrenet, TransitionMode transition=TransitionMode::Transformed,
const char *op=nullptr, double tol3d=0.0, double tolBound=0.0, double tolAngluar=0.0);
TopoShape& makeElementPipeShell(const std::vector<TopoShape>& sources,
const MakeSolid makeSolid,
const Standard_Boolean isFrenet,
TransitionMode transition = TransitionMode::Transformed,
const char* op = nullptr,
double tol3d = 0.0,
double tolBound = 0.0,
double tolAngluar = 0.0);
/** Try to simplify geometry of any linear/planar subshape to line/plane
*

View File

@@ -249,27 +249,27 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
return res;
}
double tol2 = tol * tol;
int i = 0;
int index = 0;
TopAbs_ShapeEnum shapeType = subshape.shapeType();
switch (shapeType) {
case TopAbs_VERTEX:
// Vertex search will do comparison with tolerance to account for
// rounding error inccured through transformation.
for (auto& s : getSubTopoShapes(TopAbs_VERTEX)) {
++i;
if (BRep_Tool::Pnt(TopoDS::Vertex(s.getShape()))
for (auto& shape : getSubTopoShapes(TopAbs_VERTEX)) {
++index;
if (BRep_Tool::Pnt(TopoDS::Vertex(shape.getShape()))
.SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(subshape.getShape())))
<= tol2) {
if (names) {
names->push_back(std::string("Vertex") + std::to_string(i));
names->push_back(std::string("Vertex") + std::to_string(index));
}
res.push_back(s);
res.push_back(shape);
}
}
break;
case TopAbs_EDGE:
case TopAbs_FACE: {
std::unique_ptr<Geometry> g;
std::unique_ptr<Geometry> geom;
bool isLine = false;
bool isPlane = false;
@@ -284,16 +284,16 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
}
if (vertices.empty() || checkGeometry == CheckGeometry::checkGeometry) {
g = Geometry::fromShape(subshape.getShape());
if (!g) {
geom = Geometry::fromShape(subshape.getShape());
if (!geom) {
return res;
}
if (shapeType == TopAbs_EDGE) {
isLine = (g->isDerivedFrom(GeomLine::getClassTypeId())
|| g->isDerivedFrom(GeomLineSegment::getClassTypeId()));
isLine = (geom->isDerivedFrom(GeomLine::getClassTypeId())
|| geom->isDerivedFrom(GeomLineSegment::getClassTypeId()));
}
else {
isPlane = g->isDerivedFrom(GeomPlane::getClassTypeId());
isPlane = geom->isDerivedFrom(GeomPlane::getClassTypeId());
}
}
@@ -319,7 +319,7 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
return false;
}
}
else if (!g2 || !g2->isSame(*g, tol, atol)) {
else if (!g2 || !g2->isSame(*geom, tol, atol)) {
return false;
}
return true;
@@ -328,13 +328,13 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
if (vertices.empty()) {
// Probably an infinite shape, so we have to search by geometry
int idx = 0;
for (auto& s : getSubTopoShapes(shapeType)) {
for (auto& shape : getSubTopoShapes(shapeType)) {
++idx;
if (!s.countSubShapes(TopAbs_VERTEX) && compareGeometry(s, true)) {
if (!shape.countSubShapes(TopAbs_VERTEX) && compareGeometry(shape, true)) {
if (names) {
names->push_back(shapeName(shapeType) + std::to_string(idx));
}
res.push_back(s);
res.push_back(shape);
}
}
break;
@@ -345,18 +345,18 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
// * Find the ancestor shape of the found vertex
// * Compare each vertex of the ancestor shape and the input shape
// * Perform geometry comparison of the ancestor and input shape.
// * For face, perform addition geometry comparison of each edges.
// * For face, perform addition geometry comparison of each edge.
std::unordered_set<TopoShape,ShapeHasher,ShapeHasher> shapeSet;
for (auto& v : findSubShapesWithSharedVertex(vertices[0], nullptr, checkGeometry, tol, atol)) {
for (auto idx : findAncestors(v.getShape(), shapeType)) {
auto s = getSubTopoShape(shapeType, idx);
if (!shapeSet.insert(s).second) {
for (auto& vert : findSubShapesWithSharedVertex(vertices[0], nullptr, checkGeometry, tol, atol)) {
for (auto idx : findAncestors(vert.getShape(), shapeType)) {
auto shape = getSubTopoShape(shapeType, idx);
if (!shapeSet.insert(shape).second) {
continue;
}
TopoShape otherWire;
std::vector<TopoDS_Shape> otherVertices;
if (shapeType == TopAbs_FACE) {
otherWire = s.splitWires();
otherWire = shape.splitWires();
if (wire.countSubShapes(TopAbs_EDGE)
!= otherWire.countSubShapes(TopAbs_EDGE)) {
continue;
@@ -364,25 +364,25 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
otherVertices = otherWire.getSubShapes(TopAbs_VERTEX);
}
else {
otherVertices = s.getSubShapes(TopAbs_VERTEX);
otherVertices = shape.getSubShapes(TopAbs_VERTEX);
}
if (otherVertices.size() != vertices.size()) {
continue;
}
if (checkGeometry == CheckGeometry::checkGeometry && !compareGeometry(s, false)) {
if (checkGeometry == CheckGeometry::checkGeometry && !compareGeometry(shape, false)) {
continue;
}
unsigned i = 0;
unsigned ind = 0;
bool matched = true;
for (auto& v : vertices) {
for (auto& vertex : vertices) {
bool found = false;
for (unsigned j = 0; j < otherVertices.size(); ++j) {
auto& v1 = otherVertices[i];
if (++i == otherVertices.size()) {
i = 0;
for (unsigned inner = 0; inner < otherVertices.size(); ++inner) {
auto& vertex1 = otherVertices[ind];
if (++ind == otherVertices.size()) {
ind = 0;
}
if (BRep_Tool::Pnt(TopoDS::Vertex(v))
.SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(v1)))
if (BRep_Tool::Pnt(TopoDS::Vertex(vertex))
.SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(vertex1)))
<= tol2) {
found = true;
break;
@@ -403,22 +403,22 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
auto otherEdges = otherWire.getSubShapes(TopAbs_EDGE);
std::vector<std::unique_ptr<Geometry>> geos;
geos.resize(otherEdges.size());
bool matched = true;
bool matched2 = true;
unsigned i = 0;
auto edges = wire.getSubShapes(TopAbs_EDGE);
for (auto& e : edges) {
std::unique_ptr<Geometry> g(Geometry::fromShape(e));
if (!g) {
matched = false;
for (auto& edge : edges) {
std::unique_ptr<Geometry> geom2(Geometry::fromShape(edge));
if (!geom2) {
matched2 = false;
break;
}
bool isLine = false;
bool isLine2 = false;
gp_Pnt pt1, pt2;
if (g->isDerivedFrom(GeomLine::getClassTypeId())
|| g->isDerivedFrom(GeomLineSegment::getClassTypeId())) {
pt1 = BRep_Tool::Pnt(TopExp::FirstVertex(TopoDS::Edge(e)));
pt2 = BRep_Tool::Pnt(TopExp::LastVertex(TopoDS::Edge(e)));
isLine = true;
if (geom2->isDerivedFrom(GeomLine::getClassTypeId())
|| geom2->isDerivedFrom(GeomLineSegment::getClassTypeId())) {
pt1 = BRep_Tool::Pnt(TopExp::FirstVertex(TopoDS::Edge(edge)));
pt2 = BRep_Tool::Pnt(TopExp::LastVertex(TopoDS::Edge(edge)));
isLine2 = true;
}
// We will tolerate on edge reordering
bool found = false;
@@ -434,7 +434,7 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
break;
}
}
if (isLine) {
if (isLine2) {
if (g1->isDerivedFrom(GeomLine::getClassTypeId())
|| g1->isDerivedFrom(GeomLineSegment::getClassTypeId())) {
auto p1 =
@@ -452,24 +452,24 @@ std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape&
continue;
}
if (g1->isSame(*g, tol, atol)) {
if (g1->isSame(*geom2, tol, atol)) {
found = true;
break;
}
}
if (!found) {
matched = false;
matched2 = false;
break;
}
}
if (!matched) {
if (!matched2) {
continue;
}
}
if (names) {
names->push_back(shapeName(shapeType) + std::to_string(idx));
}
res.push_back(s);
res.push_back(shape);
}
}
break;
@@ -1880,6 +1880,77 @@ static std::vector<TopoShape> prepareProfiles(const std::vector<TopoShape>& shap
return ret;
}
TopoShape& TopoShape::makeElementPipeShell(const std::vector<TopoShape>& shapes,
const MakeSolid make_solid,
const Standard_Boolean isFrenet,
TransitionMode transition,
const char* op,
double tol3d,
double tolBound,
double tolAngular)
{
if (!op) {
op = Part::OpCodes::PipeShell;
}
if (shapes.size() < 2) {
FC_THROWM(Base::CADKernelError, "Not enough input shapes");
}
auto spine = shapes.front().makeElementWires();
if (spine.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
if (spine.getShape().ShapeType() != TopAbs_WIRE) {
FC_THROWM(Base::CADKernelError, "Spine shape cannot form a single wire");
}
BRepOffsetAPI_MakePipeShell mkPipeShell(TopoDS::Wire(spine.getShape()));
BRepBuilderAPI_TransitionMode transMode;
switch (transition) {
case TransitionMode::RightCorner:
transMode = BRepBuilderAPI_RightCorner;
break;
case TransitionMode::RoundCorner:
transMode = BRepBuilderAPI_RoundCorner;
break;
default:
transMode = BRepBuilderAPI_Transformed;
break;
}
mkPipeShell.SetMode(isFrenet);
mkPipeShell.SetTransitionMode(transMode);
if (tol3d != 0.0 || tolBound != 0.0 || tolAngular != 0.0) {
if (tol3d == 0.0) {
tol3d = 1e-4;
}
if (tolBound == 0.0) {
tolBound = 1e-4;
}
if (tolAngular == 0.0) {
tolAngular = 1e-2;
}
mkPipeShell.SetTolerance(tol3d, tolBound, tolAngular);
}
for (auto& sh : prepareProfiles(shapes, 1)) {
mkPipeShell.Add(sh.getShape());
}
if (!mkPipeShell.IsReady()) {
FC_THROWM(Base::CADKernelError, "shape is not ready to build");
}
else {
mkPipeShell.Build();
}
if (make_solid == MakeSolid::makeSolid) {
mkPipeShell.MakeSolid();
}
return makeElementShape(mkPipeShell, shapes, op);
}
TopoShape& TopoShape::makeElementWires(const std::vector<TopoShape>& shapes,
const char* op,
double tol,
@@ -2348,6 +2419,14 @@ TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape,
return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op);
}
TopoShape &TopoShape::makeElementShape(BRepOffsetAPI_MakePipeShell &mkShape,
const std::vector<TopoShape> &source, const char *op) {
if (!op) {
op = Part::OpCodes::PipeShell;
}
return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), source, op);
}
TopoShape& TopoShape::makeElementLoft(const std::vector<TopoShape>& shapes,
IsSolid isSolid,
IsRuled isRuled,
@@ -2674,8 +2753,8 @@ TopoShape TopoShape::splitWires(std::vector<TopoShape>* inner, SplitWireReorient
{
// ShapeAnalysis::OuterWire() is un-reliable for some reason. OCC source
// code shows it works by creating face using each wire, and then test using
// BRepTopAdaptor_FClass2d::PerformInfinitePoint() to check if it is an out
// bound wire. And practice shows it sometimes returns the incorrect
// BRepTopAdaptor_FClass2d::PerformInfinitePoint() to check if it is an
// outbound wire. And practice shows it sometimes returns the incorrect
// result. Need more investigation. Note that this may be related to
// unreliable solid face orientation
// (https://forum.freecadweb.org/viewtopic.php?p=446006#p445674)
@@ -3258,7 +3337,7 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker,
*this = inputs[0];
if (shapes.size() == 1) {
// _shapes has fewer items than shapes due to compound expansion.
// Only warn if the caller paseses one shape.
// Only warn if the caller passes one shape.
FC_WARN("Boolean operation with only one shape input");
}
return *this;