Merge pull request #12014 from bgbsww/bgbsww-patch-29

Toponaming ShapeMapper
This commit is contained in:
Chris Hennes
2024-01-22 11:23:56 -06:00
committed by GitHub
6 changed files with 587 additions and 0 deletions

View File

@@ -533,6 +533,8 @@ SET(Part_SRCS
TopoShapeCache.cpp
TopoShapeCache.h
TopoShapeExpansion.cpp
TopoShapeMapper.h
TopoShapeMapper.cpp
TopoShapeOpCode.h
edgecluster.cpp
edgecluster.h

View File

@@ -613,6 +613,27 @@ public:
bool hasPendingElementMap() const;
/** Helper class to return the generated and modified shape given an input shape
*
* Shape history information is extracted using OCCT APIs
* BRepBuilderAPI_MakeShape::Generated/Modified(). However, there is often
* some glitches in various derived class. So we use this class as an
* abstraction, and create various derived classes to deal with the glitches.
*/
struct PartExport Mapper {
/// Helper vector for temporary storage of both generated and modified shapes
mutable std::vector<TopoDS_Shape> _res;
virtual ~Mapper() {}
/// Return a list of shape generated from the given input shape
virtual const std::vector<TopoDS_Shape> &generated(const TopoDS_Shape &) const {
return _res;
}
/// Return a list of shape modified from the given input shape
virtual const std::vector<TopoDS_Shape> &modified(const TopoDS_Shape &) const {
return _res;
}
};
/** Make a compound shape
*
* @param shapes: input shapes
@@ -629,6 +650,41 @@ public:
*/
TopoShape &makeElementCompound(const std::vector<TopoShape> &shapes, const char *op=nullptr, bool force=true);
struct BRepFillingParams;
/** Provides information about the continuity of a curve.
* Corresponds to OCCT type GeomAbs_Shape
*/
enum class Continuity {
/// Only geometric continuity
C0,
/** for each point on the curve, the tangent vectors 'on the right' and 'on
* the left' are collinear with the same orientation.
*/
G1,
/** Continuity of the first derivative. The 'C1' curve is also 'G1' but, in
* addition, the tangent vectors 'on the right' and 'on the left' are equal.
*/
C1,
/** For each point on the curve, the normalized normal vectors 'on the
* right' and 'on the left' are equal.
*/
G2,
/// Continuity of the second derivative.
C2,
/// Continuity of the third derivative.
C3,
/** Continuity of the N-th derivative, whatever is the value given for N
* (infinite order of continuity). Also provides information about the
* continuity of a surface.
*/
CN,
};
friend class TopoShapeCache;
private:

View File

@@ -0,0 +1,97 @@
#include "TopoShapeMapper.h"
namespace Part
{
void ShapeMapper::expand(const TopoDS_Shape& d, std::vector<TopoDS_Shape>& shapes)
{
if (d.IsNull()) {
return;
}
for (TopExp_Explorer xp(d, TopAbs_FACE); xp.More(); xp.Next()) {
shapes.push_back(xp.Current());
}
for (TopExp_Explorer xp(d, TopAbs_EDGE, TopAbs_FACE); xp.More(); xp.Next()) {
shapes.push_back(xp.Current());
}
for (TopExp_Explorer xp(d, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) {
shapes.push_back(xp.Current());
}
}
void ShapeMapper::populate(MappingStatus status,
const TopTools_ListOfShape& src,
const TopTools_ListOfShape& dst)
{
for (TopTools_ListIteratorOfListOfShape it(src); it.More(); it.Next()) {
populate(status, it.Value(), dst);
}
}
void ShapeMapper::populate(MappingStatus status,
const TopoShape& src,
const TopTools_ListOfShape& dst)
{
if (src.isNull()) {
return;
}
std::vector<TopoDS_Shape> dstShapes;
for (TopTools_ListIteratorOfListOfShape it(dst); it.More(); it.Next()) {
expand(it.Value(), dstShapes);
}
insert(status, src.getShape(), dstShapes);
}
void ShapeMapper::insert(MappingStatus status, const TopoDS_Shape& s, const TopoDS_Shape& d)
{
if (s.IsNull() || d.IsNull()) {
return;
}
// Prevent an element shape from being both generated and modified
if (status == MappingStatus::Generated) {
if (_modifiedShapes.count(d)) {
return;
}
_generatedShapes.insert(d);
}
else {
if (_generatedShapes.count(d)) {
return;
}
_modifiedShapes.insert(d);
}
auto& entry = (status == MappingStatus::Generated) ? _generated[s] : _modified[s];
if (entry.shapeSet.insert(d).second) {
entry.shapes.push_back(d);
}
};
void ShapeMapper::insert(MappingStatus status,
const TopoDS_Shape& s,
const std::vector<TopoDS_Shape>& d)
{
if (s.IsNull() || d.empty()) {
return;
}
auto& entry = (status == MappingStatus::Generated) ? _generated[s] : _modified[s];
for (auto& shape : d) {
// Prevent an element shape from being both generated and modified
if (status == MappingStatus::Generated) {
if (_modifiedShapes.count(shape)) {
continue;
}
_generatedShapes.insert(shape);
}
else {
if (_generatedShapes.count(shape)) {
continue;
}
_modifiedShapes.insert(shape);
}
if (entry.shapeSet.insert(shape).second) {
entry.shapes.push_back(shape);
}
}
};
} // namespace Part

View File

@@ -0,0 +1,273 @@
#include <map>
#include <unordered_set>
#include <vector>
#include <Standard_Version.hxx>
#include <TopoDS_Shape.hxx>
#include <TopExp_Explorer.hxx>
#include "TopoShape.h"
class BRepBuilderAPI_MakeShape;
class BRepTools_History;
class BRepTools_ReShape;
class ShapeFix_Root;
namespace Part
{
/// Shape hasher that ignore orientation
struct ShapeHasher
{
inline size_t operator()(const TopoShape& s) const
{
#if OCC_VERSION_HEX >= 0x070800
return std::hash<TopoDS_Shape> {}(s.getShape());
#else
return s.getShape().HashCode(INT_MAX);
#endif
}
inline size_t operator()(const TopoDS_Shape& s) const
{
#if OCC_VERSION_HEX >= 0x070800
return std::hash<TopoDS_Shape> {}(s);
#else
return s.HashCode(INT_MAX);
#endif
}
inline bool operator()(const TopoShape& a, const TopoShape& b) const
{
return a.getShape().IsSame(b.getShape());
}
inline bool operator()(const TopoDS_Shape& a, const TopoDS_Shape& b) const
{
return a.IsSame(b);
}
template<class T>
static inline void hash_combine(std::size_t& seed, const T& v)
{
// copied from boost::hash_combine
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
inline size_t operator()(const std::pair<TopoShape, TopoShape>& s) const
{
#if OCC_VERSION_HEX >= 0x070800
size_t res = std::hash<TopoDS_Shape> {}(s.first.getShape());
hash_combine(res, std::hash<TopoDS_Shape> {}(s.second.getShape()));
#else
size_t res = s.first.getShape().HashCode(INT_MAX);
hash_combine(res, s.second.getShape().HashCode(INT_MAX));
#endif
return res;
}
inline size_t operator()(const std::pair<TopoDS_Shape, TopoDS_Shape>& s) const
{
#if OCC_VERSION_HEX >= 0x070800
size_t res = std::hash<TopoDS_Shape> {}(s.first);
hash_combine(res, std::hash<TopoDS_Shape> {}(s.second));
#else
size_t res = s.first.HashCode(INT_MAX);
hash_combine(res, s.second.HashCode(INT_MAX));
#endif
return res;
}
inline bool operator()(const std::pair<TopoShape, TopoShape>& a,
const std::pair<TopoShape, TopoShape>& b) const
{
return a.first.getShape().IsSame(b.first.getShape())
&& a.second.getShape().IsSame(b.second.getShape());
}
inline bool operator()(const std::pair<TopoDS_Shape, TopoDS_Shape>& a,
const std::pair<TopoDS_Shape, TopoDS_Shape>& b) const
{
return a.first.IsSame(b.first) && a.second.IsSame(b.second);
}
};
enum class MappingStatus
{
Generated,
Modified
};
/** Shape mapper for user defined shape mapping
*/
struct PartExport ShapeMapper: TopoShape::Mapper
{
virtual ~ShapeMapper() noexcept = default;
/** Populate mapping from a source shape to a list of shape
*
* @param status: whether the shape is generated
* @param src: source shape
* @param dst: a list of sub shapes in the new shape
*
* The source will be expanded into sub shapes of faces, edges and vertices
* before being inserted into the map.
*/
void populate(MappingStatus status, const TopoShape& src, const TopTools_ListOfShape& dst);
/** Populate mapping from a source sub shape to a list of shape
*
* @param status: whether the shape is generated
* @param src: a list of sub shapes in the source shape
* @param dst: a list of sub shapes in the new shape
*
* The source will be expanded into sub shapes of faces, edges and vertices
* before being inserted into the map.
*/
void populate(MappingStatus status,
const TopTools_ListOfShape& src,
const TopTools_ListOfShape& dst);
/** Populate mapping from a source sub shape to a list of shape
*
* @param status: whether the shape is generated
* @param src: a list of sub shapes in the source shape
* @param dst: a list of sub shapes in the new shape
*
* The source will be expanded into sub shapes of faces, edges and vertices
* before being inserted into the map.
*/
void populate(MappingStatus status,
const std::vector<TopoShape>& src,
const std::vector<TopoShape>& dst)
{
for (auto& s : src) {
populate(status, s, dst);
}
}
/** Populate mapping from a source sub shape to a list of shape
*
* @param status: whether the shape is generated
* @param src: a sub shape of the source shape
* @param dst: a list of sub shapes in the new shape
*
* The source will be expanded into sub shapes of faces, edges and vertices
* before being inserted into the map.
*/
void populate(MappingStatus status, const TopoShape& src, const std::vector<TopoShape>& dst)
{
if (src.isNull()) {
return;
}
std::vector<TopoDS_Shape> dstShapes;
for (auto& d : dst) {
expand(d.getShape(), dstShapes);
}
insert(status, src.getShape(), dstShapes);
}
/** Expand a shape into faces, edges and vertices
* @params d: shape to expand
* @param shapes: output sub shapes of faces, edges and vertices
*/
void expand(const TopoDS_Shape& d, std::vector<TopoDS_Shape>& shapes);
/** Insert a map entry from a sub shape in the source to a list of sub shapes in the new shape
*
* @params status: whether the sub shapes are generated or modified
* @param s: a sub shape in the source
* @param d: a list of sub shapes in the new shape
*/
void insert(MappingStatus status, const TopoDS_Shape& s, const std::vector<TopoDS_Shape>& d);
/** Insert a map entry from a sub shape in the source to a sub shape in the new shape
*
* @params status: whether the sub shapes are generated or modified
* @param s: a sub shape in the source
* @param d: a list of sub shapes in the new shape
*/
void insert(MappingStatus status, const TopoDS_Shape& s, const TopoDS_Shape& d);
const std::vector<TopoDS_Shape>& generated(const TopoDS_Shape& s) const override
{
auto iter = _generated.find(s);
if (iter != _generated.end()) {
return iter->second.shapes;
}
return _res;
}
const std::vector<TopoDS_Shape>& modified(const TopoDS_Shape& s) const override
{
auto iter = _modified.find(s);
if (iter != _modified.end()) {
return iter->second.shapes;
}
return _res;
}
std::vector<TopoShape> shapes;
std::unordered_set<TopoDS_Shape, ShapeHasher, ShapeHasher> shapeSet;
struct ShapeValue
{
std::vector<TopoDS_Shape> shapes;
std::unordered_set<TopoDS_Shape, ShapeHasher, ShapeHasher> shapeSet;
};
typedef std::unordered_map<TopoDS_Shape, ShapeValue, ShapeHasher, ShapeHasher> ShapeMap;
ShapeMap _generated;
std::unordered_set<TopoDS_Shape, ShapeHasher, ShapeHasher> _generatedShapes;
ShapeMap _modified;
std::unordered_set<TopoDS_Shape, ShapeHasher, ShapeHasher> _modifiedShapes;
};
/// Parameters for TopoShape::makeElementFilledFace()
struct PartExport TopoShape::BRepFillingParams
{
/** Optional initial surface to begin the construction of the surface for the filled face.
*
* It is useful if the surface resulting from construction for the
* algorithm is likely to be complex. The support surface of the face
* under construction is computed by a deformation of Surf which satisfies
* the given constraints. The set of bounding edges defines the wire of
* the face. If no initial surface is given, the algorithm computes it
* automatically. If the set of edges is not connected (Free constraint),
* missing edges are automatically computed. Important: the initial
* surface must have orthogonal local coordinates, i.e. partial
* derivatives dS/du and dS/dv must be orthogonal at each point of
* surface. If this condition breaks, distortions of resulting surface are
* possible
*/
TopoShape surface;
/** Optional map from input edge to continutity order. The default
* continuity order is TopoShape::Continuity::C0.
*/
std::unordered_map<TopoDS_Shape, TopoShape::Continuity, ShapeHasher, ShapeHasher> orders;
/// Optional map from input shape to face used as support
std::unordered_map<TopoDS_Shape, TopoDS_Shape, ShapeHasher, ShapeHasher> supports;
/// Optional begin index to the input shapes to be used as the boundary of the filled face.
int boundary_begin = -1;
/// Optional end index (last index + 1) to the input shapes to be used as the boundary of the
/// filled face.
int boundary_end = -1;
/// The energe minimizing criterion degree;
unsigned int degree = 3;
/// The number of points on the curve NbPntsOnCur
unsigned int ptsoncurve = 15;
/// The number of iterations NbIter
unsigned int numiter = 2;
/// The Boolean Anisotropie
bool anisotropy = false;
/// The 2D tolerance Tol2d
double tol2d = 1e-5;
/// The 3D tolerance Tol3d
double tol3d = 1e-4;
/// The angular tolerance TolAng
double tolG1 = 0.01;
/// The tolerance for curvature TolCur
double tolG2 = 0.1;
/// The highest polynomial degree MaxDeg
unsigned int maxdeg = 8;
/** The greatest number of segments MaxSeg.
*
* If the Boolean Anistropie is true, the algorithm's performance is better
* in cases where the ratio of the length U and the length V indicate a
* great difference between the two. In other words, when the surface is,
* for example, extremely long.
*/
unsigned int maxseg = 9;
};
} // namespace Part

View File

@@ -15,4 +15,5 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/TopoShape.cpp
${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeExpansion.cpp
${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMapper.cpp
)

View File

@@ -0,0 +1,158 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "gtest/gtest.h"
#include "src/App/InitApplication.h"
#include <Mod/Part/App/TopoShape.h>
#include "Mod/Part/App/TopoShapeMapper.h"
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeShape.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepTools_History.hxx>
#include <BRepTools_ReShape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopoDS_Edge.hxx>
// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)
class TopoShapeMapperTest: public ::testing::Test
{
protected:
static void SetUpTestSuite()
{
tests::initApplication();
}
void SetUp() override
{
_docName = App::GetApplication().getUniqueDocumentName("test");
App::GetApplication().newDocument(_docName.c_str(), "testUser");
}
void TearDown() override
{
App::GetApplication().closeDocument(_docName.c_str());
}
private:
std::string _docName;
};
TEST_F(TopoShapeMapperTest, shapeHasherSingle)
{
// Arrange
auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge();
Part::TopoShape topoShape {edge};
auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge();
Part::TopoShape topoShape2 {edge2};
struct Part::ShapeHasher hasher;
// Act
size_t hash1 = hasher(topoShape);
size_t hash2 = hasher(topoShape2);
size_t hash3 = hasher(edge);
size_t hash4 = hasher(edge2);
// Assert
EXPECT_EQ(hash1, hash3);
EXPECT_EQ(hash2, hash4);
EXPECT_NE(hash1, hash2);
}
TEST_F(TopoShapeMapperTest, shapeHasherDual)
{
// Arrange
auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge();
Part::TopoShape topoShape {edge};
auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge();
Part::TopoShape topoShape2 {edge2};
struct Part::ShapeHasher hasher;
// Act
size_t hash5 = hasher(topoShape, topoShape);
size_t hash6 = hasher(topoShape, topoShape2);
size_t hash7 = hasher(edge, edge);
size_t hash8 = hasher(edge, edge2);
// Assert
EXPECT_TRUE(hash5);
EXPECT_FALSE(hash6);
EXPECT_TRUE(hash7);
EXPECT_FALSE(hash8);
}
TEST_F(TopoShapeMapperTest, shapeHasherPair)
{
// Arrange
auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge();
Part::TopoShape topoShape {edge};
auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge();
Part::TopoShape topoShape2 {edge2};
std::pair<Part::TopoShape, Part::TopoShape> pair1(topoShape, topoShape2);
std::pair<Part::TopoShape, Part::TopoShape> pair2(topoShape, topoShape);
std::pair<TopoDS_Shape, TopoDS_Shape> pair3(edge, edge2);
std::pair<TopoDS_Shape, TopoDS_Shape> pair4(edge, edge);
struct Part::ShapeHasher hasher;
// Act
size_t hash9 = hasher(pair1);
size_t hash10 = hasher(pair2);
size_t hash11 = hasher(pair3);
size_t hash12 = hasher(pair4);
// Assert
EXPECT_EQ(hash9, hash11);
EXPECT_EQ(hash10, hash12);
EXPECT_NE(hash9, hash10);
}
TEST_F(TopoShapeMapperTest, shapeHasherPairs)
{
// Arrange
auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge();
Part::TopoShape topoShape {edge};
auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge();
Part::TopoShape topoShape2 {edge2};
std::pair<Part::TopoShape, Part::TopoShape> pair1(topoShape, topoShape2);
std::pair<Part::TopoShape, Part::TopoShape> pair2(topoShape, topoShape);
std::pair<TopoDS_Shape, TopoDS_Shape> pair3(edge, edge2);
std::pair<TopoDS_Shape, TopoDS_Shape> pair4(edge, edge);
struct Part::ShapeHasher hasher;
// Act
size_t hash13 = hasher(pair1, pair1);
size_t hash14 = hasher(pair1, pair2);
size_t hash15 = hasher(pair3, pair3);
size_t hash16 = hasher(pair3, pair4);
// Assert
EXPECT_TRUE(hash13);
EXPECT_FALSE(hash14);
EXPECT_TRUE(hash15);
EXPECT_FALSE(hash16);
}
TEST_F(TopoShapeMapperTest, shapeMapperTests)
{
// Arrange
auto mapper = Part::ShapeMapper();
auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0);
boxMaker1.Build();
auto box1 = boxMaker1.Shape();
Part::TopoShape topoShape1 {box1};
// Act
auto e = topoShape1.getSubTopoShapes(TopAbs_EDGE);
mapper.populate(Part::MappingStatus::Modified, box1, {e[0], e[1], e[2], e[3]});
mapper.populate(Part::MappingStatus::Generated, box1, {e[4], e[5], e[6]});
std::vector<TopoDS_Shape> vec1 = mapper.modified(box1);
std::vector<TopoDS_Shape> vec2 = mapper.generated(box1);
// Assert
EXPECT_EQ(vec1.size(), 4);
EXPECT_EQ(vec2.size(), 3);
}
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)