Merge pull request #12414 from bgbsww/bgbsww-toponamingSearchSubShape
Toponaming/Part: Add searchSubShape as findSubShapesWithSharedVertex
This commit is contained in:
@@ -118,6 +118,12 @@ enum class RefineFail
|
||||
throwException
|
||||
};
|
||||
|
||||
/// Behavior of findSubShapesWithSharedVertex.
|
||||
enum class CheckGeometry
|
||||
{
|
||||
ignoreGeometry,
|
||||
checkGeometry
|
||||
};
|
||||
/** The representation for a CAD Shape
|
||||
*/
|
||||
class PartExport TopoShape: public Data::ComplexGeoData
|
||||
@@ -703,10 +709,13 @@ public:
|
||||
std::vector<int> findAncestors(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const;
|
||||
std::vector<TopoDS_Shape> findAncestorsShapes(const TopoDS_Shape& subshape,
|
||||
TopAbs_ShapeEnum type) const;
|
||||
/** Search sub shape
|
||||
/** Find sub shapes with shared Vertexes.
|
||||
*
|
||||
* Renamed: searchSubShape -> findSubShapesWithSharedVertex
|
||||
*
|
||||
* unlike findShape(), the input shape does not have to be an actual
|
||||
* sub-shape of this shape. The sub-shape is searched by shape geometry
|
||||
* Note that subshape must be a Vertex, Edge, or Face.
|
||||
*
|
||||
* @param subshape: a sub shape to search
|
||||
* @param names: optional output of found sub shape indexed based name
|
||||
@@ -714,11 +723,10 @@ public:
|
||||
* @param tol: tolerance to check coincident vertices
|
||||
* @param atol: tolerance to check for same angles
|
||||
*/
|
||||
// TODO: Implement this method and its tests later in Toponaming Phase 3.
|
||||
// std::vector<TopoShape> searchSubShape(const TopoShape &subshape,
|
||||
// std::vector<std::string> *names=nullptr,
|
||||
// bool checkGeometry=true,
|
||||
// double tol=1e-7, double atol=1e-12) const;
|
||||
std::vector<TopoShape> findSubShapesWithSharedVertex(const TopoShape &subshape,
|
||||
std::vector<std::string> *names=nullptr,
|
||||
CheckGeometry checkGeometry=CheckGeometry::checkGeometry,
|
||||
double tol=1e-7, double atol=1e-12) const;
|
||||
//@}
|
||||
|
||||
void copyElementMap(const TopoShape & topoShape, const char *op=nullptr);
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
#include "TopoShapeCache.h"
|
||||
#include "TopoShapeMapper.h"
|
||||
#include "FaceMaker.h"
|
||||
#include "Geometry.h"
|
||||
|
||||
#include <App/ElementNamingUtils.h>
|
||||
|
||||
@@ -221,6 +222,248 @@ TopoDS_Shape TopoShape::findShape(TopAbs_ShapeEnum type, int idx) const
|
||||
return _cache->findShape(_Shape, type, idx);
|
||||
}
|
||||
|
||||
std::vector<TopoShape> TopoShape::findSubShapesWithSharedVertex(const TopoShape& subshape,
|
||||
std::vector<std::string>* names,
|
||||
CheckGeometry checkGeometry,
|
||||
double tol,
|
||||
double atol) const
|
||||
{
|
||||
std::vector<TopoShape> res;
|
||||
if (subshape.isNull() || this->isNull()) {
|
||||
return res;
|
||||
}
|
||||
double tol2 = tol * tol;
|
||||
int i = 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()))
|
||||
.SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(subshape.getShape())))
|
||||
<= tol2) {
|
||||
if (names) {
|
||||
names->push_back(std::string("Vertex") + std::to_string(i));
|
||||
}
|
||||
res.push_back(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TopAbs_EDGE:
|
||||
case TopAbs_FACE: {
|
||||
std::unique_ptr<Geometry> g;
|
||||
bool isLine = false;
|
||||
bool isPlane = false;
|
||||
|
||||
std::vector<TopoDS_Shape> vertices;
|
||||
TopoShape wire;
|
||||
if (shapeType == TopAbs_FACE) {
|
||||
wire = subshape.splitWires();
|
||||
vertices = wire.getSubShapes(TopAbs_VERTEX);
|
||||
}
|
||||
else {
|
||||
vertices = subshape.getSubShapes(TopAbs_VERTEX);
|
||||
}
|
||||
|
||||
if (vertices.empty() || checkGeometry == CheckGeometry::checkGeometry) {
|
||||
g = Geometry::fromShape(subshape.getShape());
|
||||
if (!g) {
|
||||
return res;
|
||||
}
|
||||
if (shapeType == TopAbs_EDGE) {
|
||||
isLine = (g->isDerivedFrom(GeomLine::getClassTypeId())
|
||||
|| g->isDerivedFrom(GeomLineSegment::getClassTypeId()));
|
||||
}
|
||||
else {
|
||||
isPlane = g->isDerivedFrom(GeomPlane::getClassTypeId());
|
||||
}
|
||||
}
|
||||
|
||||
auto compareGeometry = [&](const TopoShape& s, bool strict) {
|
||||
std::unique_ptr<Geometry> g2(Geometry::fromShape(s.getShape()));
|
||||
if (!g2) {
|
||||
return false;
|
||||
}
|
||||
if (isLine && !strict) {
|
||||
// For lines, don't compare geometry, just check the
|
||||
// vertices below instead, because the exact same edge
|
||||
// may have different geometrical representation.
|
||||
if (!g2->isDerivedFrom(GeomLine::getClassTypeId())
|
||||
&& !g2->isDerivedFrom(GeomLineSegment::getClassTypeId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (isPlane && !strict) {
|
||||
// For planes, don't compare geometry either, so that
|
||||
// we don't need to worry about orientation and so on.
|
||||
// Just check the edges.
|
||||
if (!g2->isDerivedFrom(GeomPlane::getClassTypeId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!g2 || !g2->isSame(*g, tol, atol)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (vertices.empty()) {
|
||||
// Probably an infinite shape, so we have to search by geometry
|
||||
int idx = 0;
|
||||
for (auto& s : getSubTopoShapes(shapeType)) {
|
||||
++idx;
|
||||
if (!s.countSubShapes(TopAbs_VERTEX) && compareGeometry(s, true)) {
|
||||
if (names) {
|
||||
names->push_back(shapeName(shapeType) + std::to_string(idx));
|
||||
}
|
||||
res.push_back(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// The basic idea of shape search is about the same for both edge and face.
|
||||
// * Search the first vertex, which is done with tolerance.
|
||||
// * 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.
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
TopoShape otherWire;
|
||||
std::vector<TopoDS_Shape> otherVertices;
|
||||
if (shapeType == TopAbs_FACE) {
|
||||
otherWire = s.splitWires();
|
||||
if (wire.countSubShapes(TopAbs_EDGE)
|
||||
!= otherWire.countSubShapes(TopAbs_EDGE)) {
|
||||
continue;
|
||||
}
|
||||
otherVertices = otherWire.getSubShapes(TopAbs_VERTEX);
|
||||
}
|
||||
else {
|
||||
otherVertices = s.getSubShapes(TopAbs_VERTEX);
|
||||
}
|
||||
if (otherVertices.size() != vertices.size()) {
|
||||
continue;
|
||||
}
|
||||
if (checkGeometry == CheckGeometry::checkGeometry && !compareGeometry(s, false)) {
|
||||
continue;
|
||||
}
|
||||
unsigned i = 0;
|
||||
bool matched = true;
|
||||
for (auto& v : vertices) {
|
||||
bool found = false;
|
||||
for (unsigned j = 0; j < otherVertices.size(); ++j) {
|
||||
auto& v1 = otherVertices[i];
|
||||
if (++i == otherVertices.size()) {
|
||||
i = 0;
|
||||
}
|
||||
if (BRep_Tool::Pnt(TopoDS::Vertex(v))
|
||||
.SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(v1)))
|
||||
<= tol2) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shapeType == TopAbs_FACE && checkGeometry == CheckGeometry::checkGeometry) {
|
||||
// Is it really necessary to check geometries of each edge of a face?
|
||||
// Right now we only do outer wire check
|
||||
auto otherEdges = otherWire.getSubShapes(TopAbs_EDGE);
|
||||
std::vector<std::unique_ptr<Geometry>> geos;
|
||||
geos.resize(otherEdges.size());
|
||||
bool matched = 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;
|
||||
break;
|
||||
}
|
||||
bool isLine = 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;
|
||||
}
|
||||
// We will tolerate on edge reordering
|
||||
bool found = false;
|
||||
for (unsigned j = 0; j < otherEdges.size(); j++) {
|
||||
auto& e1 = otherEdges[i];
|
||||
auto& g1 = geos[i];
|
||||
if (++i >= otherEdges.size()) {
|
||||
i = 0;
|
||||
}
|
||||
if (!g1) {
|
||||
g1 = Geometry::fromShape(e1);
|
||||
if (!g1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isLine) {
|
||||
if (g1->isDerivedFrom(GeomLine::getClassTypeId())
|
||||
|| g1->isDerivedFrom(GeomLineSegment::getClassTypeId())) {
|
||||
auto p1 =
|
||||
BRep_Tool::Pnt(TopExp::FirstVertex(TopoDS::Edge(e1)));
|
||||
auto p2 =
|
||||
BRep_Tool::Pnt(TopExp::LastVertex(TopoDS::Edge(e1)));
|
||||
if ((p1.SquareDistance(pt1) <= tol2
|
||||
&& p2.SquareDistance(pt2) <= tol2)
|
||||
|| (p1.SquareDistance(pt2) <= tol2
|
||||
&& p2.SquareDistance(pt1) <= tol2)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g1->isSame(*g, tol, atol)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (names) {
|
||||
names->push_back(shapeName(shapeType) + std::to_string(idx));
|
||||
}
|
||||
res.push_back(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int TopoShape::findAncestor(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const
|
||||
{
|
||||
initCache();
|
||||
|
||||
@@ -746,6 +746,129 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestors)
|
||||
EXPECT_TRUE(ancestorShapeList.back().IsEqual(topoShape6.getShape()));
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, findSubShapesWithSharedVertexEverything)
|
||||
{
|
||||
// Arrange
|
||||
auto [box1, box2] = CreateTwoCubes();
|
||||
TopoShape box1TS {box1};
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::string> names1;
|
||||
std::vector<std::string> names2;
|
||||
double tol {2}; // Silly big tolerance to get everything
|
||||
double atol {2};
|
||||
|
||||
TopExp_Explorer exp(box1, TopAbs_FACE);
|
||||
auto face = exp.Current();
|
||||
exp.Init(box1, TopAbs_EDGE);
|
||||
auto edge = exp.Current();
|
||||
exp.Init(box1, TopAbs_VERTEX);
|
||||
auto vertex = exp.Current();
|
||||
// Act
|
||||
auto shapes =
|
||||
box1TS.findSubShapesWithSharedVertex(face, &names, CheckGeometry::checkGeometry, tol, atol);
|
||||
auto shapes1 = box1TS.findSubShapesWithSharedVertex(edge,
|
||||
&names1,
|
||||
CheckGeometry::checkGeometry,
|
||||
tol,
|
||||
atol);
|
||||
auto shapes2 = box1TS.findSubShapesWithSharedVertex(vertex,
|
||||
&names2,
|
||||
CheckGeometry::checkGeometry,
|
||||
tol,
|
||||
atol);
|
||||
// Assert
|
||||
EXPECT_EQ(shapes.size(), 6);
|
||||
EXPECT_EQ(names.size(), 6);
|
||||
EXPECT_STREQ(names[0].c_str(), "Face1");
|
||||
EXPECT_STREQ(names[1].c_str(), "Face3");
|
||||
EXPECT_STREQ(names[2].c_str(), "Face6");
|
||||
EXPECT_STREQ(names[3].c_str(), "Face5");
|
||||
EXPECT_STREQ(names[4].c_str(), "Face4");
|
||||
EXPECT_STREQ(names[5].c_str(), "Face2");
|
||||
EXPECT_EQ(shapes1.size(), 12);
|
||||
EXPECT_EQ(names1.size(), 12);
|
||||
EXPECT_EQ(shapes2.size(), 8);
|
||||
EXPECT_EQ(names2.size(), 8);
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, findSubShapesWithSharedVertexMid)
|
||||
{
|
||||
// Arrange
|
||||
auto [box1, box2] = CreateTwoCubes();
|
||||
TopoShape box1TS {box1};
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::string> names1;
|
||||
std::vector<std::string> names2;
|
||||
double tol {1e-0};
|
||||
double atol {1e-04};
|
||||
|
||||
TopExp_Explorer exp(box1, TopAbs_FACE);
|
||||
auto face = exp.Current();
|
||||
exp.Init(box1, TopAbs_EDGE);
|
||||
auto edge = exp.Current();
|
||||
exp.Init(box1, TopAbs_VERTEX);
|
||||
auto vertex = exp.Current();
|
||||
// Act
|
||||
auto shapes =
|
||||
box1TS.findSubShapesWithSharedVertex(face, &names, CheckGeometry::checkGeometry, tol, atol);
|
||||
auto shapes1 = box1TS.findSubShapesWithSharedVertex(edge,
|
||||
&names1,
|
||||
CheckGeometry::checkGeometry,
|
||||
tol,
|
||||
atol);
|
||||
auto shapes2 = box1TS.findSubShapesWithSharedVertex(vertex,
|
||||
&names2,
|
||||
CheckGeometry::checkGeometry,
|
||||
tol,
|
||||
atol);
|
||||
// Assert
|
||||
EXPECT_EQ(shapes.size(), 6);
|
||||
EXPECT_EQ(names.size(), 6);
|
||||
EXPECT_EQ(shapes1.size(), 7);
|
||||
EXPECT_EQ(names1.size(), 7);
|
||||
EXPECT_EQ(shapes2.size(), 4);
|
||||
EXPECT_EQ(names2.size(), 4);
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, findSubShapesWithSharedVertexClose)
|
||||
{
|
||||
// Arrange
|
||||
auto [box1, box2] = CreateTwoCubes();
|
||||
TopoShape box1TS {box1};
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::string> names1;
|
||||
std::vector<std::string> names2;
|
||||
double tol {1e-02};
|
||||
double atol {1e-04};
|
||||
|
||||
TopExp_Explorer exp(box1, TopAbs_FACE);
|
||||
auto face = exp.Current();
|
||||
exp.Init(box1, TopAbs_EDGE);
|
||||
auto edge = exp.Current();
|
||||
exp.Init(box1, TopAbs_VERTEX);
|
||||
auto vertex = exp.Current();
|
||||
// Act
|
||||
auto shapes =
|
||||
box1TS.findSubShapesWithSharedVertex(face, &names, CheckGeometry::checkGeometry, tol, atol);
|
||||
auto shapes1 = box1TS.findSubShapesWithSharedVertex(edge,
|
||||
&names1,
|
||||
CheckGeometry::checkGeometry,
|
||||
tol,
|
||||
atol);
|
||||
auto shapes2 = box1TS.findSubShapesWithSharedVertex(vertex,
|
||||
&names2,
|
||||
CheckGeometry::checkGeometry,
|
||||
tol,
|
||||
atol);
|
||||
// Assert
|
||||
EXPECT_EQ(shapes.size(), 1);
|
||||
EXPECT_EQ(names.size(), 1);
|
||||
EXPECT_EQ(shapes1.size(), 1);
|
||||
EXPECT_EQ(names1.size(), 1);
|
||||
EXPECT_EQ(shapes2.size(), 1);
|
||||
EXPECT_EQ(names2.size(), 1);
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementShellInvalid)
|
||||
{
|
||||
// Arrange
|
||||
|
||||
Reference in New Issue
Block a user