Toponaming/Part: Clean and add tests for elementTransform methods

This commit is contained in:
bgbsww
2024-02-20 11:40:24 -05:00
parent a90ad10d60
commit b2cad5297a
4 changed files with 211 additions and 78 deletions

View File

@@ -202,6 +202,18 @@ enum class JoinType
Intersection,
};
enum class CheckScale
{
noScaleCheck,
checkScale
};
enum class Copy
{
noCopy,
copy
};
/** The representation for a CAD Shape
*/
// NOLINTNEXTLINE cppcoreguidelines-special-member-functions
@@ -790,7 +802,7 @@ public:
/** Make a hollowed solid by removing some faces from a given solid
*
* @param source: input shape
* @param shape: input shape
* @param faces: list of faces to remove, must be sub shape of the input shape
* @param offset: thickness of the walls
* @param tol: tolerance criterion for coincidence in generated shapes
@@ -806,13 +818,12 @@ public:
* a self reference so that multiple operations can be carried out
* for the same shape in the same line of code.
*/
TopoShape &makeElementThickSolid(const TopoShape &source, const std::vector<TopoShape> &faces,
TopoShape &makeElementThickSolid(const TopoShape &shape, const std::vector<TopoShape> &faces,
double offset, double tol, bool intersection = false, bool selfInter = false,
short offsetMode = 0, JoinType join = JoinType::Arc, const char *op=nullptr);
/** Make a hollowed solid by removing some faces from a given solid
*
* @param source: input shape
* @param faces: list of faces to remove, must be sub shape of the input shape
* @param offset: thickness of the walls
* @param tol: tolerance criterion for coincidence in generated shapes
@@ -1224,7 +1235,6 @@ public:
return TopoShape(0, Hasher).makeElementWires(*this, op, tol, policy, output);
}
/** Make a new shape with transformation that may contain non-uniform scaling
*
* @param source: input shape
@@ -1239,8 +1249,10 @@ public:
* 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 &makeElementGTransform(const TopoShape &source, const Base::Matrix4D &mat,
const char *op=nullptr, bool copy=false);
TopoShape& makeElementGTransform(const TopoShape& source,
const Base::Matrix4D& mat,
const char* op = nullptr,
Copy copy = Copy::noCopy);
/** Make a new shape with transformation that may contain non-uniform scaling
*
@@ -1254,8 +1266,11 @@ public:
* @return Return a new shape with transformation. The shape itself is not
* modified
*/
TopoShape makeElementGTransform(const Base::Matrix4D &mat, const char *op=nullptr, bool copy=false) const {
return TopoShape(Tag,Hasher).makeElementGTransform(*this,mat,op,copy);
TopoShape makeElementGTransform(const Base::Matrix4D& mat,
const char* op = nullptr,
Copy copy = Copy::noCopy) const
{
return TopoShape(Tag, Hasher).makeElementGTransform(*this, mat, op, copy);
}
/** Make a deep copy of the shape
@@ -1355,8 +1370,8 @@ public:
* the operation
* @param checkScale: whether to check if the transformation matrix
* contains scaling factor.
* @param copy: whether to perform deep copy of the shape. If false, and
* checkScale is true, then the shape will be copied if there
* @param copy: whether to perform deep copy of the shape. If noCopy, and
* checkScale, then the shape will be copied if there
* is scaling.
*
* @return Returns true if scaling is performed.
@@ -1364,8 +1379,11 @@ public:
* The original content of this TopoShape is discarded and replaced with
* the new transformed shape.
*/
bool _makeElementTransform(const TopoShape &source, const Base::Matrix4D &mat,
const char *op=nullptr, bool checkScale=false, bool copy=false);
bool _makeElementTransform(const TopoShape& source,
const Base::Matrix4D& mat,
const char* op = nullptr,
CheckScale checkScale = CheckScale::noScaleCheck,
Copy copy = Copy::noCopy);
/** Make a new shape with transformation
*
@@ -1375,8 +1393,8 @@ public:
* the operation
* @param checkScale: whether to check if the transformation matrix
* contains scaling factor.
* @param copy: whether to perform deep copy of the shape. If false, and
* checkScale is true, then the shape will be copied if there
* @param copy: whether to perform deep copy of the shape. If noCopy, and
* checkScale, then the shape will be copied if there
* is scaling.
*
* @return The original content of this TopoShape is discarded and replaced
@@ -1384,9 +1402,13 @@ public:
* 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 &makeElementTransform(const TopoShape &source, const Base::Matrix4D &mat,
const char *op=nullptr, bool checkScale=false, bool copy=false) {
_makeElementTransform(source,mat,op,checkScale,copy);
TopoShape& makeElementTransform(const TopoShape& source,
const Base::Matrix4D& mat,
const char* op = nullptr,
CheckScale checkScale = CheckScale::noScaleCheck,
Copy copy = Copy::noCopy)
{
_makeElementTransform(source, mat, op, checkScale, copy);
return *this;
}
@@ -1398,16 +1420,19 @@ public:
* the operation
* @param checkScale: whether to check if the transformation matrix
* contains scaling factor.
* @param copy: whether to perform deep copy of the shape. If false, and
* checkScale is true, then the shape will be copied if there
* @param copy: whether to perform deep copy of the shape. If noCopy, and
* checkScale, then the shape will be copied if there
* is scaling.
*
* @return Return a new shape with transformation. The shape itself is not
* modified
*/
TopoShape makeElementTransform(const Base::Matrix4D &mat, const char *op=nullptr,
bool checkScale=false, bool copy=false) const {
return TopoShape(Tag,Hasher).makeElementTransform(*this,mat,op,checkScale,copy);
TopoShape makeElementTransform(const Base::Matrix4D& mat,
const char* op = nullptr,
CheckScale checkScale = CheckScale::noScaleCheck,
Copy copy = Copy::noCopy)
{
return TopoShape(Tag, Hasher).makeElementTransform(*this, mat, op, checkScale, copy);
}
/** Make a new shape with transformation
@@ -1416,19 +1441,17 @@ public:
* @param trsf: OCCT transformation matrix
* @param op: optional string to be encoded into topo naming for indicating
* the operation
* @param checkScale: whether to check if the transformation matrix
* contains scaling factor.
* @param copy: whether to perform deep copy of the shape. If false, and
* checkScale is true, then the shape will be copied if there
* is scaling.
* @param copy: whether to perform deep copy of the shape.
*
* @return The original content of this TopoShape is discarded and replaced
* with the new transformed 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 &makeElementTransform(const TopoShape &shape, const gp_Trsf &trsf,
const char *op=nullptr, bool copy=false);
TopoShape& makeElementTransform(const TopoShape& shape,
const gp_Trsf& trsf,
const char* op = nullptr,
Copy copy = Copy::noCopy);
/** Make a new shape with transformation
*
@@ -1436,17 +1459,15 @@ public:
* @param trsf: OCCT transformation matrix
* @param op: optional string to be encoded into topo naming for indicating
* the operation
* @param checkScale: whether to check if the transformation matrix
* contains scaling factor.
* @param copy: whether to perform deep copy of the shape. If false, and
* checkScale is true, then the shape will be copied if there
* is scaling.
* @param copy: whether to perform deep copy of the shape.
*
* @return Return a new shape with transformation. The shape itself is not
* modified
*/
TopoShape makeElementTransform(const gp_Trsf &trsf, const char *op=nullptr, bool copy=false) const {
return TopoShape(Tag,Hasher).makeElementTransform(*this,trsf,op,copy);
TopoShape
makeElementTransform(const gp_Trsf& trsf, const char* op = nullptr, Copy copy = Copy::noCopy)
{
return TopoShape(Tag, Hasher).makeElementTransform(*this, trsf, op, copy);
}
/* Make draft shape
@@ -1687,7 +1708,7 @@ public:
* makeShapeWithElementMap directly. For example:
* makeElementShape(sewer, sources)
* makeShapeWithElementMap(sewer.SewedShape(), MapperSewing(sewer), sources, OpCodes::Sewing);
* Note that if op exists in the method, it should be checked for null and overriden with
* Note that if op exists in the method, it should be checked for null and overridden with
* the appropriate operation if so.
*/

View File

@@ -48,7 +48,9 @@
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepAlgoAPI_Section.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_GTransform.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepLib.hxx>
#include <BRepOffsetAPI_DraftAngle.hxx>
#include <BRepOffsetAPI_MakePipe.hxx>
@@ -81,10 +83,6 @@
#include "Geometry.h"
#include <App/ElementNamingUtils.h>
#include <BRepLib.hxx>
#include <BRepBuilderAPI_GTransform.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include "Geometry.h"
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
@@ -2452,86 +2450,105 @@ TopoShape& TopoShape::makeElementOrderedWires(const std::vector<TopoShape>& shap
return makeElementCompound(wires, nullptr, SingleShapeCompoundCreationPolicy::returnShape);
}
bool TopoShape::_makeElementTransform(const TopoShape &shape,
const Base::Matrix4D &rclTrf, const char *op, bool checkScale, bool copy)
bool TopoShape::_makeElementTransform(const TopoShape& shape,
const Base::Matrix4D& rclTrf,
const char* op,
CheckScale checkScale,
Copy copy)
{
if(checkScale) {
if (checkScale == CheckScale::checkScale) {
auto scaleType = rclTrf.hasScale();
if (scaleType != Base::ScaleType::NoScaling && scaleType != Base::ScaleType::Uniform) {
makeElementGTransform(shape,rclTrf,op,copy);
makeElementGTransform(shape, rclTrf, op, copy);
return true;
}
}
makeElementTransform(shape,convert(rclTrf),op,copy);
makeElementTransform(shape, convert(rclTrf), op, copy);
return false;
}
TopoShape &TopoShape::makeElementTransform(const TopoShape &shape, const gp_Trsf &trsf, const char *op, bool copy) {
if(!copy) {
TopoShape& TopoShape::makeElementTransform(const TopoShape& shape,
const gp_Trsf& trsf,
const char* op,
Copy copy)
{
if (copy == Copy::noCopy) {
// OCCT checks the ScaleFactor against gp::Resolution() which is DBL_MIN!!!
copy = trsf.ScaleFactor()*trsf.HVectorialPart().Determinant() < 0. ||
Abs(Abs(trsf.ScaleFactor()) - 1) > Precision::Confusion();
copy = trsf.ScaleFactor() * trsf.HVectorialPart().Determinant() < 0.
|| Abs(Abs(trsf.ScaleFactor()) - 1) > Precision::Confusion() ? Copy::copy : Copy::noCopy;
}
TopoShape tmp(shape);
if(copy) {
if(shape.isNull())
if (copy == Copy::copy) {
if (shape.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
BRepBuilderAPI_Transform mkTrf(shape.getShape(), trsf, Standard_True);
// TODO: calling Moved() is to make sure the shape has some Location,
// which is necessary for STEP export to work. However, if we reach
// here, it porabably means BRepBuilderAPI_Transform has modified
// here, it probably means BRepBuilderAPI_Transform has modified
// underlying shapes (because of scaling), it will break compound child
// parent relationship anyway. In short, STEP import/export will most
// likely break badly if there is any scaling involved
tmp.setShape(mkTrf.Shape().Moved(gp_Trsf()), false);
} else
}
else {
tmp.move(trsf);
}
if(op || (shape.Tag && shape.Tag!=Tag)) {
if (op || (shape.Tag && shape.Tag != Tag)) {
setShape(tmp._Shape);
initCache();
if (!Hasher)
if (!Hasher) {
Hasher = tmp.Hasher;
}
copyElementMap(tmp, op);
} else
}
else {
*this = tmp;
}
return *this;
}
TopoShape &TopoShape::makeElementGTransform(const TopoShape &shape,
const Base::Matrix4D &rclTrf, const char *op, bool copy)
TopoShape& TopoShape::makeElementGTransform(const TopoShape& shape,
const Base::Matrix4D& rclTrf,
const char* op,
Copy copy)
{
if(shape.isNull())
if (shape.isNull()) {
FC_THROWM(NullShapeException, "Null input shape");
}
// if(!op) op = Part::OpCodes::Gtransform;
gp_GTrsf mat;
mat.SetValue(1,1,rclTrf[0][0]);
mat.SetValue(2,1,rclTrf[1][0]);
mat.SetValue(3,1,rclTrf[2][0]);
mat.SetValue(1,2,rclTrf[0][1]);
mat.SetValue(2,2,rclTrf[1][1]);
mat.SetValue(3,2,rclTrf[2][1]);
mat.SetValue(1,3,rclTrf[0][2]);
mat.SetValue(2,3,rclTrf[1][2]);
mat.SetValue(3,3,rclTrf[2][2]);
mat.SetValue(1,4,rclTrf[0][3]);
mat.SetValue(2,4,rclTrf[1][3]);
mat.SetValue(3,4,rclTrf[2][3]);
mat.SetValue(1, 1, rclTrf[0][0]);
mat.SetValue(2, 1, rclTrf[1][0]);
mat.SetValue(3, 1, rclTrf[2][0]);
mat.SetValue(1, 2, rclTrf[0][1]);
mat.SetValue(2, 2, rclTrf[1][1]);
mat.SetValue(3, 2, rclTrf[2][1]);
mat.SetValue(1, 3, rclTrf[0][2]);
mat.SetValue(2, 3, rclTrf[1][2]);
mat.SetValue(3, 3, rclTrf[2][2]);
mat.SetValue(1, 4, rclTrf[0][3]);
mat.SetValue(2, 4, rclTrf[1][3]);
mat.SetValue(3, 4, rclTrf[2][3]);
// geometric transformation
TopoShape tmp(shape);
BRepBuilderAPI_GTransform mkTrf(shape.getShape(), mat, copy);
BRepBuilderAPI_GTransform mkTrf(shape.getShape(), mat, copy == Copy::copy);
tmp.setShape(mkTrf.Shape(), false);
if(op || (shape.Tag && shape.Tag!=Tag)) {
if (op || (shape.Tag && shape.Tag != Tag)) {
setShape(tmp._Shape);
initCache();
if (!Hasher)
if (!Hasher) {
Hasher = tmp.Hasher;
}
copyElementMap(tmp, op);
} else
}
else {
*this = tmp;
}
return *this;
}

View File

@@ -1491,7 +1491,6 @@ TEST_F(TopoShapeExpansionTest, makeElementThickSolid)
EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge11;THK;:H1:4,E"));
}
TEST_F(TopoShapeExpansionTest, makeElementGeneralFuse)
{
// Arrange
@@ -1543,6 +1542,7 @@ TEST_F(TopoShapeExpansionTest, makeElementFuse)
"FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;"
"FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F"));
}
TEST_F(TopoShapeExpansionTest, makeElementCut)
{
// Arrange
@@ -1569,4 +1569,99 @@ TEST_F(TopoShapeExpansionTest, makeElementCut)
"CUT;:H1:7,V);CUT;:H1:3c,E|Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E);CUT;:H1:cb,F"));
}
TEST_F(TopoShapeExpansionTest, makeElementTransformWithoutMap)
{
// Arrange
auto [cube1, cube2] = CreateTwoCubes();
auto tr {gp_Trsf()};
tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0)));
TopoShape topoShape1 {cube1, 1L};
// Act
TopoShape& result = topoShape1.makeElementTransform(topoShape1, tr);
auto elements = elementMap(result);
Base::BoundBox3d bb = result.getBoundBox();
// Assert shape is correct
EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -0.5, 0.0, 0.5, 0.5, 1.0)));
EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1);
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 0);
}
TEST_F(TopoShapeExpansionTest, makeElementTransformWithMap)
{
// Arrange
auto [cube1, cube2] = CreateTwoCubes();
auto tr {gp_Trsf()};
tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0)));
cube2.Move(TopLoc_Location(tr));
TopoShape topoShape1 {cube1, 1L};
TopoShape topoShape2 {cube2, 2L};
// Act
TopoShape& result = topoShape1.makeElementFuse({topoShape1, topoShape2}); // op, tolerance
topoShape1.makeElementTransform(result, tr);
auto elements = elementMap(result);
Base::BoundBox3d bb = result.getBoundBox();
// Assert shape is correct
EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -1.0, 0.0, 1.0, 0.5, 1.0)));
EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1.75);
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 66);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;"
"FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;"
"FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F"));
}
TEST_F(TopoShapeExpansionTest, makeElementGTransformWithoutMap)
{
// Arrange
auto [cube1, cube2] = CreateTwoCubes();
auto tr {gp_Trsf()};
tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0)));
TopoShape topoShape1 {cube1, 1L};
// Act
TopoShape& result = topoShape1.makeElementGTransform(topoShape1, TopoShape::convert(tr));
auto elements = elementMap(result);
Base::BoundBox3d bb = result.getBoundBox();
// Assert shape is correct
EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -0.5, 0.0, 0.5, 0.5, 1.0)));
EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1);
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 0);
}
TEST_F(TopoShapeExpansionTest, makeElementGTransformWithMap)
{
// Arrange
auto [cube1, cube2] = CreateTwoCubes();
auto tr {gp_Trsf()};
tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0)));
cube2.Move(TopLoc_Location(tr));
TopoShape topoShape1 {cube1, 1L};
TopoShape topoShape2 {cube2, 2L};
// Act
TopoShape& result = topoShape1.makeElementFuse({topoShape1, topoShape2}); // op, tolerance
topoShape1.makeElementGTransform(result, TopoShape::convert(tr));
auto elements = elementMap(result);
Base::BoundBox3d bb = result.getBoundBox();
// Assert shape is correct
EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -1.0, 0.0, 1.0, 0.5, 1.0)));
EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1.75);
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 66);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;"
"FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;"
"FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F"));
}
// Not testing _makeElementTransform as it is a thin wrapper that calls the same places as the four
// preceding tests.
// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)

View File

@@ -48,6 +48,6 @@ TEST_F(FeaturePartMakeElementRefineTest, makeElementRefineBoxes)
EXPECT_EQ(refined.countSubElements("Face"), 6); // After refining it is one box
EXPECT_EQ(refined.countSubElements("Edge"), 12); // 12 edges in a box
// TODO: Make sure we have an elementMap for the refine.
// Refine doesn't work on compounds, so we're going to need a binary operation or the
// TODO: Refine doesn't work on compounds, so we're going to need a binary operation or the
// like, and those don't exist yet. Once they do, this test can be expanded
}