From b4bb92465f4b5fc8823e56d9f6b90caf9de80355 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 14 Mar 2024 11:31:30 -0400 Subject: [PATCH 1/2] Toponaming/Part: Transfer in FeatureMirroring and FeatureOffset --- src/Mod/Part/App/FeatureMirroring.cpp | 8 ++++++++ src/Mod/Part/App/FeatureOffset.cpp | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/App/FeatureMirroring.cpp b/src/Mod/Part/App/FeatureMirroring.cpp index 9cd077c72cb2..6513c4470dcd 100644 --- a/src/Mod/Part/App/FeatureMirroring.cpp +++ b/src/Mod/Part/App/FeatureMirroring.cpp @@ -256,6 +256,7 @@ App::DocumentObjectExecReturn *Mirroring::execute() if (shape.IsNull()) Standard_Failure::Raise(std::string(std::string(this->getFullLabel()) + ": Cannot mirror empty shape").c_str()); gp_Ax2 ax2(gp_Pnt(base.x,base.y,base.z), gp_Dir(norm.x,norm.y,norm.z)); +#ifndef FC_USE_TNP_FIX gp_Trsf mat; mat.SetMirror(ax2); TopLoc_Location loc = shape.Location(); @@ -264,6 +265,13 @@ App::DocumentObjectExecReturn *Mirroring::execute() BRepBuilderAPI_Transform mkTrf(shape, mat); this->Shape.setValue(mkTrf.Shape()); return App::DocumentObject::StdReturn; +#else +// auto shape = Feature::getTopoShape(link); +// if (shape.isNull()) +// Standard_Failure::Raise("Cannot mirror empty shape"); + this->Shape.setValue(TopoShape(0).makeElementMirror(shape,ax2)); + return Part::Feature::execute(); +#endif } catch (Standard_Failure& e) { return new App::DocumentObjectExecReturn(e.GetMessageString()); diff --git a/src/Mod/Part/App/FeatureOffset.cpp b/src/Mod/Part/App/FeatureOffset.cpp index 095d75dd4941..59f6c6e6417e 100644 --- a/src/Mod/Part/App/FeatureOffset.cpp +++ b/src/Mod/Part/App/FeatureOffset.cpp @@ -83,13 +83,22 @@ App::DocumentObjectExecReturn *Offset::execute() bool inter = Intersection.getValue(); bool self = SelfIntersection.getValue(); short mode = (short)Mode.getValue(); - short join = (short)Join.getValue(); bool fill = Fill.getValue(); +#ifndef FC_USE_TNP_FIX + short join = (short)Join.getValue(); const TopoShape& shape = Feature::getShape(source); if (fabs(offset) > 2*tol) this->Shape.setValue(shape.makeOffsetShape(offset, tol, inter, self, mode, join, fill)); else this->Shape.setValue(shape); +#else + auto shape = Feature::getTopoShape(source); + if(shape.isNull()) + return new App::DocumentObjectExecReturn("Invalid source link"); + auto join = static_cast(Join.getValue()); + this->Shape.setValue(TopoShape(0).makeElementOffset( + shape,offset,tol,inter,self,mode,join,fill)); +#endif return App::DocumentObject::StdReturn; } From 7a520a4ea6a8ab22e225ceb96e47f3e0f0ebd4f6 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Fri, 15 Mar 2024 08:09:34 -0400 Subject: [PATCH 2/2] Toponaming/Part: Clean and add tests for FeatureMirroring and FeatureOffset --- src/Mod/Part/App/FeatureMirroring.cpp | 10 +- src/Mod/Part/App/FeatureOffset.cpp | 4 +- tests/src/Mod/Part/App/CMakeLists.txt | 2 + tests/src/Mod/Part/App/FeatureMirroring.cpp | 130 +++++++++++++++++ tests/src/Mod/Part/App/FeatureOffset.cpp | 149 ++++++++++++++++++++ tests/src/Mod/Part/App/PartTestHelpers.cpp | 8 +- 6 files changed, 295 insertions(+), 8 deletions(-) create mode 100644 tests/src/Mod/Part/App/FeatureMirroring.cpp create mode 100644 tests/src/Mod/Part/App/FeatureOffset.cpp diff --git a/src/Mod/Part/App/FeatureMirroring.cpp b/src/Mod/Part/App/FeatureMirroring.cpp index 6513c4470dcd..3ceea6ea1132 100644 --- a/src/Mod/Part/App/FeatureMirroring.cpp +++ b/src/Mod/Part/App/FeatureMirroring.cpp @@ -252,11 +252,11 @@ App::DocumentObjectExecReturn *Mirroring::execute() Base::Vector3d norm = Normal.getValue(); try { + gp_Ax2 ax2(gp_Pnt(base.x,base.y,base.z), gp_Dir(norm.x,norm.y,norm.z)); +#ifndef FC_USE_TNP_FIX const TopoDS_Shape& shape = Feature::getShape(link); if (shape.IsNull()) Standard_Failure::Raise(std::string(std::string(this->getFullLabel()) + ": Cannot mirror empty shape").c_str()); - gp_Ax2 ax2(gp_Pnt(base.x,base.y,base.z), gp_Dir(norm.x,norm.y,norm.z)); -#ifndef FC_USE_TNP_FIX gp_Trsf mat; mat.SetMirror(ax2); TopLoc_Location loc = shape.Location(); @@ -266,9 +266,9 @@ App::DocumentObjectExecReturn *Mirroring::execute() this->Shape.setValue(mkTrf.Shape()); return App::DocumentObject::StdReturn; #else -// auto shape = Feature::getTopoShape(link); -// if (shape.isNull()) -// Standard_Failure::Raise("Cannot mirror empty shape"); + auto shape = Feature::getTopoShape(link); + if (shape.isNull()) + Standard_Failure::Raise("Cannot mirror empty shape"); this->Shape.setValue(TopoShape(0).makeElementMirror(shape,ax2)); return Part::Feature::execute(); #endif diff --git a/src/Mod/Part/App/FeatureOffset.cpp b/src/Mod/Part/App/FeatureOffset.cpp index 59f6c6e6417e..103b44b2327e 100644 --- a/src/Mod/Part/App/FeatureOffset.cpp +++ b/src/Mod/Part/App/FeatureOffset.cpp @@ -95,9 +95,9 @@ App::DocumentObjectExecReturn *Offset::execute() auto shape = Feature::getTopoShape(source); if(shape.isNull()) return new App::DocumentObjectExecReturn("Invalid source link"); - auto join = static_cast(Join.getValue()); + auto join = static_cast(Join.getValue()); this->Shape.setValue(TopoShape(0).makeElementOffset( - shape,offset,tol,inter,self,mode,join,fill)); + shape,offset,tol,inter,self,mode,join,fill ? FillType::fill : FillType::noFill)); #endif return App::DocumentObject::StdReturn; } diff --git a/tests/src/Mod/Part/App/CMakeLists.txt b/tests/src/Mod/Part/App/CMakeLists.txt index 8aa815b1b9b6..b6bd2b7be2c4 100644 --- a/tests/src/Mod/Part/App/CMakeLists.txt +++ b/tests/src/Mod/Part/App/CMakeLists.txt @@ -8,6 +8,8 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/FeatureCompound.cpp ${CMAKE_CURRENT_SOURCE_DIR}/FeatureExtrusion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/FeatureFillet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/FeatureMirroring.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/FeatureOffset.cpp ${CMAKE_CURRENT_SOURCE_DIR}/FeaturePartBoolean.cpp ${CMAKE_CURRENT_SOURCE_DIR}/FeaturePartCommon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/FeaturePartCut.cpp diff --git a/tests/src/Mod/Part/App/FeatureMirroring.cpp b/tests/src/Mod/Part/App/FeatureMirroring.cpp new file mode 100644 index 000000000000..1e4bd70a2f47 --- /dev/null +++ b/tests/src/Mod/Part/App/FeatureMirroring.cpp @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "gtest/gtest.h" + +#include +#include + +#include "PartTestHelpers.h" + +using namespace PartTestHelpers; + +// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) +class FeatureMirroringTest: public ::testing::Test, public PartTestHelperClass +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + createTestDoc(); + _mirror = dynamic_cast(_doc->addObject("Part::Mirroring")); + _mirror->Source.setValue(_boxes[0]); + _mirror->Base.setValue(1, 0, 0); + _mirror->execute(); + } + + void TearDown() override + {} + + Part::Mirroring* _mirror = nullptr; // NOLINT Can't be private in a test framework +}; + +TEST_F(FeatureMirroringTest, testXMirror) +{ + // Arrange + Base::BoundBox3d bb = _mirror->Shape.getShape().getBoundBox(); + // Assert size and position + EXPECT_EQ(getVolume(_mirror->Shape.getShape().getShape()), 6); + // Mirrored it around X from 0,0,0 -> 1,2,3 to 0,0,-3 -> 1,2,0 + EXPECT_TRUE(boxesMatch(bb, Base::BoundBox3d(0, 0, -3, 1, 2, 0))); + // Assert correct element Map +#ifdef FC_USE_TNP_FIX + EXPECT_TRUE(allElementsMatch( + _mirror->Shape.getShape(), + { + "Edge10;:M;MIR;:H70c:7,E", "Edge11;:M;MIR;:H70c:7,E", "Edge12;:M;MIR;:H70c:7,E", + "Edge1;:M;MIR;:H70c:7,E", "Edge2;:M;MIR;:H70c:7,E", "Edge3;:M;MIR;:H70c:7,E", + "Edge4;:M;MIR;:H70c:7,E", "Edge5;:M;MIR;:H70c:7,E", "Edge6;:M;MIR;:H70c:7,E", + "Edge7;:M;MIR;:H70c:7,E", "Edge8;:M;MIR;:H70c:7,E", "Edge9;:M;MIR;:H70c:7,E", + "Face1;:M;MIR;:H70c:7,F", "Face2;:M;MIR;:H70c:7,F", "Face3;:M;MIR;:H70c:7,F", + "Face4;:M;MIR;:H70c:7,F", "Face5;:M;MIR;:H70c:7,F", "Face6;:M;MIR;:H70c:7,F", + "Vertex1;:M;MIR;:H70c:7,V", "Vertex2;:M;MIR;:H70c:7,V", "Vertex3;:M;MIR;:H70c:7,V", + "Vertex4;:M;MIR;:H70c:7,V", "Vertex5;:M;MIR;:H70c:7,V", "Vertex6;:M;MIR;:H70c:7,V", + "Vertex7;:M;MIR;:H70c:7,V", "Vertex8;:M;MIR;:H70c:7,V", + })); +#else + EXPECT_EQ(_mirror->Shape.getShape().getElementMapSize(), 0); +#endif +} + +TEST_F(FeatureMirroringTest, testYMirrorWithExistingElementMap) +{ + // Arrange + Part::Fuse* _fuse = nullptr; // NOLINT Can't be private in a test framework + _fuse = dynamic_cast(_doc->addObject("Part::Fuse")); + _fuse->Base.setValue(_boxes[0]); + _fuse->Tool.setValue(_boxes[1]); + // Act + _fuse->execute(); + _mirror->Source.setValue(_fuse); + _mirror->Base.setValue(0, 1, 0); // Y Axis + Part::TopoShape ts = _fuse->Shape.getValue(); + double volume = getVolume(ts.getShape()); + Base::BoundBox3d bb = _mirror->Shape.getShape().getBoundBox(); + // Assert size and position + EXPECT_EQ(getVolume(_mirror->Shape.getShape().getShape()), volume); + // Mirrored it around X from 0,0,0 -> 1,2,3 to 0,0,-3 -> 1,2,0 + EXPECT_TRUE(boxesMatch(bb, Base::BoundBox3d(0, 0, -3, 1, 3, 0))); + // Assert correct element Map +#ifdef FC_USE_TNP_FIX + EXPECT_TRUE(elementsMatch( + _mirror->Shape.getShape(), + { + "Edge10;:M;FUS;:H30a:7,E;:M;MIR;:H310:7,E", + "Edge11;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge12;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge1;:M;FUS;:H30a:7,E;:M;MIR;:H310:7,E", + "Edge1;:M;FUS;:H30a:7,E;:U2;FUS;:H30a:8,V;:M;MIR;:H310:7,V", + "Edge1;:M;FUS;:H30a:7,E;:U;FUS;:H30a:7,V;:M;MIR;:H310:7,V", + "Edge2;:M2(Edge2;:H30a,E);FUS;:H309:17,E;:M;MIR;:H310:7,E", + "Edge2;:M2(Edge2;:H30a,E);FUS;:H309:17,E;:U2;FUS;:H309:8,V;:M;MIR;:H310:7,V", + "Edge2;:M2;FUS;:H30a:8,E;:M;MIR;:H310:7,E", + "Edge2;:M2;FUS;:H30a:8,E;:U2;FUS;:H30a:8,V;:M;MIR;:H310:7,V", + "Edge2;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge2;:M;FUS;:H309:7,E;:U;FUS;:H309:7,V;:M;MIR;:H310:7,V", + "Edge3;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge3;:M;FUS;:H309:7,E;:U2;FUS;:H309:8,V;:M;MIR;:H310:7,V", + "Edge4;:M2(Edge4;:H30a,E);FUS;:H309:17,E;:M;MIR;:H310:7,E", + "Edge4;:M2;FUS;:H30a:8,E;:M;MIR;:H310:7,E", + "Edge4;:M2;FUS;:H30a:8,E;:U2;FUS;:H30a:8,V;:M;MIR;:H310:7,V", + "Edge4;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge4;:M;FUS;:H309:7,E;:U;FUS;:H309:7,V;:M;MIR;:H310:7,V", + "Edge5;:M;FUS;:H30a:7,E;:M;MIR;:H310:7,E", + "Edge5;:M;FUS;:H30a:7,E;:U2;FUS;:H30a:8,V;:M;MIR;:H310:7,V", + "Edge5;:M;FUS;:H30a:7,E;:U;FUS;:H30a:7,V;:M;MIR;:H310:7,V", + "Edge6;:M2(Edge6;:H30a,E);FUS;:H309:17,E;:M;MIR;:H310:7,E", + "Edge6;:M2(Edge6;:H30a,E);FUS;:H309:17,E;:U2;FUS;:H309:8,V;:M;MIR;:H310:7,V", + "Edge6;:M2;FUS;:H30a:8,E;:M;MIR;:H310:7,E", + "Edge6;:M2;FUS;:H30a:8,E;:U2;FUS;:H30a:8,V;:M;MIR;:H310:7,V", + "Edge6;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge6;:M;FUS;:H309:7,E;:U;FUS;:H309:7,V;:M;MIR;:H310:7,V", + "Edge7;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge7;:M;FUS;:H309:7,E;:U2;FUS;:H309:8,V;:M;MIR;:H310:7,V", + "Edge8;:M2(Edge8;:H30a,E);FUS;:H309:17,E;:M;MIR;:H310:7,E", + "Edge8;:M2;FUS;:H30a:8,E;:M;MIR;:H310:7,E", + "Edge8;:M2;FUS;:H30a:8,E;:U2;FUS;:H30a:8,V;:M;MIR;:H310:7,V", + "Edge8;:M;FUS;:H309:7,E;:M;MIR;:H310:7,E", + "Edge8;:M;FUS;:H309:7,E;:U;FUS;:H309:7,V;:M;MIR;:H310:7,V", + "Edge9;:M;FUS;:H30a:7,E;:M;MIR;:H310:7,E", + // TODO: Testing the Faces here was non-deterministic from run to run. Is that okay? + })); +#else + EXPECT_EQ(_mirror->Shape.getShape().getElementMapSize(), 0); +#endif +} + +// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) diff --git a/tests/src/Mod/Part/App/FeatureOffset.cpp b/tests/src/Mod/Part/App/FeatureOffset.cpp new file mode 100644 index 000000000000..5085cbeef2c1 --- /dev/null +++ b/tests/src/Mod/Part/App/FeatureOffset.cpp @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "gtest/gtest.h" + +#include + +#include "PartTestHelpers.h" +#include "Mod/Part/App/FeatureOffset.h" + +using namespace PartTestHelpers; + +// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) +class FeatureOffsetTest: public ::testing::Test, public PartTestHelperClass +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + createTestDoc(); + _offset = dynamic_cast(_doc->addObject("Part::Offset")); + _offset->Source.setValue(_boxes[0]); + _offset->Value.setValue(1); + _offset->Join.setValue((int)JoinType::intersection); + _offset->execute(); + } + + void TearDown() override + {} + + Part::Offset* _offset = nullptr; // NOLINT Can't be private in a test framework +}; + +TEST_F(FeatureOffsetTest, testOffset3D) +{ + // Arrange + Base::BoundBox3d bb = _offset->Shape.getShape().getBoundBox(); + // Assert size and position + // a 1x2x3 box 3doffset by 1 becomes a 3x4x5 box, so volume is 60. + EXPECT_EQ(getVolume(_offset->Shape.getShape().getShape()), 60); + EXPECT_TRUE(boxesMatch(bb, Base::BoundBox3d(-1, -1, -1, 2, 3, 4))); + // Assert correct element Map +#ifdef FC_USE_TNP_FIX + EXPECT_TRUE(allElementsMatch( + _offset->Shape.getShape(), + { + "Edge10;:G;OFS;:H47b:7,E", "Edge11;:G;OFS;:H47b:7,E", "Edge12;:G;OFS;:H47b:7,E", + "Edge1;:G;OFS;:H47b:7,E", "Edge2;:G;OFS;:H47b:7,E", "Edge3;:G;OFS;:H47b:7,E", + "Edge4;:G;OFS;:H47b:7,E", "Edge5;:G;OFS;:H47b:7,E", "Edge6;:G;OFS;:H47b:7,E", + "Edge7;:G;OFS;:H47b:7,E", "Edge8;:G;OFS;:H47b:7,E", "Edge9;:G;OFS;:H47b:7,E", + "Face1;:G;OFS;:H47b:7,F", "Face2;:G;OFS;:H47b:7,F", "Face3;:G;OFS;:H47b:7,F", + "Face4;:G;OFS;:H47b:7,F", "Face5;:G;OFS;:H47b:7,F", "Face6;:G;OFS;:H47b:7,F", + "Vertex1;:G;OFS;:H47b:7,V", "Vertex2;:G;OFS;:H47b:7,V", "Vertex3;:G;OFS;:H47b:7,V", + "Vertex4;:G;OFS;:H47b:7,V", "Vertex5;:G;OFS;:H47b:7,V", "Vertex6;:G;OFS;:H47b:7,V", + "Vertex7;:G;OFS;:H47b:7,V", "Vertex8;:G;OFS;:H47b:7,V", + })); +#else + EXPECT_EQ(_offset->Shape.getShape().getElementMapSize(), 0); +#endif +} + +TEST_F(FeatureOffsetTest, testOffset3DWithExistingElementMap) +{ + // Arrange + Part::Fuse* _fuse = nullptr; // NOLINT Can't be private in a test framework + _fuse = dynamic_cast(_doc->addObject("Part::Fuse")); + _fuse->Base.setValue(_boxes[0]); + _fuse->Tool.setValue(_boxes[1]); + _fuse->Refine.setValue(true); + // Act + _fuse->execute(); + _offset->Source.setValue(_fuse); + _offset->Value.setValue(2); + _offset->execute(); + Base::BoundBox3d bb = _offset->Shape.getShape().getBoundBox(); + // Assert size and position + // A 1x3x3 box 3doffset by 2 becomes a 5x7x7 box with volume of 245 + EXPECT_EQ(getVolume(_fuse->Shape.getShape().getShape()), 9); + EXPECT_EQ(getVolume(_offset->Shape.getShape().getShape()), 245); + EXPECT_TRUE(boxesMatch(bb, Base::BoundBox3d(-2, -2, -2, 3, 5, 5))); + // Assert correct element Map +#ifdef FC_USE_TNP_FIX + EXPECT_TRUE(elementsMatch( + _offset->Shape.getShape(), + { + "Edge2;:M2(Edge2;:H366,E);FUS;:H365:17,E;:G(Edge2;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge2;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:G;OFS;:H36c:7,E;SLD;:H36c:4,E", + "Edge2;:M2(Edge2;:H366,E);FUS;:H365:17,E;:G(Edge2;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge2;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U2;RFI;:H365:8,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + "Edge2;:M2(Edge2;:H366,E);FUS;:H365:17,E;:G(Edge2;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge2;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U;RFI;:H365:7,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + "Edge4;:M2(Edge4;:H366,E);FUS;:H365:17,E;:G(Edge4;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge4;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:G;OFS;:H36c:7,E;SLD;:H36c:4,E", + "Edge4;:M2(Edge4;:H366,E);FUS;:H365:17,E;:G(Edge4;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge4;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U2;RFI;:H365:8,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + "Edge4;:M2(Edge4;:H366,E);FUS;:H365:17,E;:G(Edge4;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge4;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U;RFI;:H365:7,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + "Edge6;:M2(Edge6;:H366,E);FUS;:H365:17,E;:G(Edge6;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge6;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:G;OFS;:H36c:7,E;SLD;:H36c:4,E", + "Edge6;:M2(Edge6;:H366,E);FUS;:H365:17,E;:G(Edge6;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge6;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U2;RFI;:H365:8,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + "Edge6;:M2(Edge6;:H366,E);FUS;:H365:17,E;:G(Edge6;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge6;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U;RFI;:H365:7,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + "Edge8;:M2(Edge8;:H366,E);FUS;:H365:17,E;:G(Edge8;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge8;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:G;OFS;:H36c:7,E;SLD;:H36c:4,E", + "Edge8;:M2(Edge8;:H366,E);FUS;:H365:17,E;:G(Edge8;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge8;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U2;RFI;:H365:8,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + "Edge8;:M2(Edge8;:H366,E);FUS;:H365:17,E;:G(Edge8;:M2;FUS;:H366:8,E;K-1;:H366:4,E|" + "Edge8;:M;FUS;:H365:7,E;K-1;:H365:4,E);RFI;:H365:53,E;:U;RFI;:H365:7,V;:G;OFS;:H36c:7," + "V;SLD;:H36c:4,V", + // TODO: Testing the Faces here was non-deterministic from run to run. Is that okay? + })); +#else + EXPECT_EQ(_offset->Shape.getShape().getElementMapSize(), 0); +#endif +} + +TEST_F(FeatureOffsetTest, testOffset2D) +{ + // Arrange + Part::Offset2D* _offset2 = dynamic_cast(_doc->addObject("Part::Offset2D")); + Part::Plane* _pln = dynamic_cast(_doc->addObject("Part::Plane")); + _pln->Length.setValue(2); + _pln->Width.setValue(3); + _offset2->Source.setValue(_pln); + _offset2->Value.setValue(1); + _offset2->Join.setValue((int)JoinType::intersection); + // Act + _offset2->execute(); + Base::BoundBox3d bb = _offset2->Shape.getShape().getBoundBox(); + // Assert size and position + // a 2x3 face 2doffset by 1 becomes a 4x5 face, so area is 20. + EXPECT_EQ(getArea(_offset2->Shape.getShape().getShape()), 20); + EXPECT_TRUE(boxesMatch(bb, Base::BoundBox3d(-1, -1, 0, 3, 4, 0))); + // Assert correct element Map + EXPECT_EQ(_offset2->Shape.getShape().getElementMapSize(), 0); +} + +// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index 76a8d6284a8a..a837d16207a7 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -175,7 +175,13 @@ testing::AssertionResult elementsMatch(const TopoShape& shape, [&, name](const Data::MappedElement& element) { return matchStringsWithoutClause(element.name.toString(), name, - ";D[a-fA-F0-9]+"); + "(;D|;:H|;K)-?[a-fA-F0-9]+"); + // ;D ;:H and ;K are the sections of an encoded name for + // Duplicate, Tag and a Face name in slices. All three of these + // can vary from run to run or platform to platform, as they are + // based on either explicit random numbers or memory addresses. + // Thus we remove the value from comparisons and just check that + // they exist. }) == elements.end()) { return testing::AssertionFailure() << mappedElementVectorToString(elements);