// SPDX-License-Identifier: LGPL-2.1-or-later #include #include "src/App/InitApplication.h" #include "Mod/Part/App/WireJoiner.h" #include #include "PartTestHelpers.h" #include // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) using namespace Part; using namespace PartTestHelpers; class WireJoinerTest: public ::testing::Test { protected: static void SetUpTestSuite() { tests::initApplication(); } void SetUp() override { _docName = App::GetApplication().getUniqueDocumentName("test"); App::GetApplication().newDocument(_docName.c_str(), "testUser"); _hasher = Base::Reference(new App::StringHasher); ASSERT_EQ(_hasher.getRefCount(), 1); } void TearDown() override { App::GetApplication().closeDocument(_docName.c_str()); } private: std::string _docName; Data::ElementIDRefs _sid; App::StringHasherRef _hasher; }; TEST_F(WireJoinerTest, addShape) { // Arrange // Create various edges that will be used to define the arguments of the various // WireJoiner::addShape() calls auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 1.0, 0.0)).Edge()}; auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; // A vector of TopoDS_Shape used as argument for wjvTDS.addShape() std::vector edges {edge1, edge2, edge3, edge4}; // Create various TopoShapes used as arguments for wjvTS.addShape() auto edge1TS {TopoShape(edge1, 1)}; auto edge2TS {TopoShape(edge2, 2)}; // A wire TopoShape used as argument for wjTS.addShape() auto wireTS {TopoShape(BRepBuilderAPI_MakeWire(edge1, edge2, edge3).Wire(), 3)}; // An empty TopoShape that will contain the shapes added by wjvTS.addShape() auto wirevTS {TopoShape(4)}; // An empty TopoShape that will contain the shapes added by wjvTDS.addShape() auto wirevTDS {TopoShape(5)}; // Create 3 WireJoiner objects, one for every definition of WireJoiner::addShape() // A WireJoiner object where it will be added a TopoShape without calling WireJoiner::Build() // afterwards auto wjTSNotDone {WireJoiner()}; // A WireJoiner object where it will be added a TopoShape auto wjTS {WireJoiner()}; // A WireJoiner object where it will be added a vector of TopoShapes auto wjvTS {WireJoiner()}; // A WireJoiner object where it will be added a vector of TopoDS_Shapes auto wjvTDS {WireJoiner()}; // Act // Calling only WireJoiner::addShape(). Expected result is that wjTSNotDone.Done() is false wjTSNotDone.addShape(wireTS); // Calling WireJoiner::addShape() and then WireJoiner::Build(). // If we don't call WireJoiner::Build() we can't see the effect of WireJoiner::addShape() as it // adds the shapes in the private member WireJoinerP::sourceEdgeArray wjTS.addShape(wireTS); wjTS.Build(); // The wire in wjTS is open, therefore to see the effect of wjTS.addShape() we must call // wjTS.getOpenWires() and put the result in wireTS wjTS.getOpenWires(wireTS, nullptr, false); wjvTS.addShape({edge1TS, edge2TS}); wjvTS.Build(); // The wire in wjvTS is open, therefore to see the effect of wjvTS.addShape() we must call // wjvTS.getOpenWires() and put the result in wirevTS wjvTS.getOpenWires(wirevTS, nullptr, false); wjvTDS.addShape(edges); wjvTDS.Build(); // The wire in wjvTDS is closed, therefore to see the effect of wjvTDS.addShape() we can simply // call wjvTDS.Shape() to replace the shape in wirevTDS wirevTDS.setShape(wjvTDS.Shape()); // Assert // Check the output of WireJoiner::IsDone(). // It should be true for all the objects that executed also WireJoiner::Build() // (All except wjTSNotDone) EXPECT_FALSE(wjTSNotDone.IsDone()); EXPECT_TRUE(wjTS.IsDone()); EXPECT_TRUE(wjvTS.IsDone()); EXPECT_TRUE(wjvTDS.IsDone()); // wireTS is build with 3 edges. The same quantity should be in the shape built EXPECT_EQ(wireTS.getSubTopoShapes(TopAbs_EDGE).size(), 3); // wirevTS is build with 2 edges. The same quantity should be in the shape built EXPECT_EQ(wirevTS.getSubTopoShapes(TopAbs_EDGE).size(), 2); // wirevTDS is build with 4 edges. The same quantity should be in the shape built EXPECT_EQ(wirevTDS.getSubTopoShapes(TopAbs_EDGE).size(), 4); } TEST_F(WireJoinerTest, setOutline) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, -0.1, 0.0), gp_Pnt(-0.1, 1.1, 0.0)).Edge()}; auto edge5 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 1.1, 0.0), gp_Pnt(1.1, 1.1, 0.0)).Edge()}; auto edge6 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; // A vector of edges used as argument for wjNoOutline.addShape() std::vector edgesNoOutline {edge1, edge2, edge3, edge4, edge5, edge6}; // A vector of edges used as argument for wjOutline.addShape() std::vector edgesOutline {edge1, edge2, edge3, edge4, edge5, edge6}; // To see the effect of setOutline() it is necessary to set the user parameter "Iteration" // to a value, in this case, less than zero before the WireObjects are initialized. // To see the correct value for this parameter refer to method WireJoinerP::canShowShape() auto hParam {App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/WireJoiner" )}; auto catchIteration {hParam->GetInt("Iteration", 0)}; hParam->SetInt("Iteration", -1); // A document where there will WireJoiner.Build() will be called having setOutline() set to // false auto docNoOutline {App::GetApplication().getActiveDocument()}; auto _docNameOutline {App::GetApplication().getUniqueDocumentName("docOutline")}; App::GetApplication().newDocument(_docNameOutline.c_str(), "docOutlineUser"); // A document where there will WireJoiner.Build() will be called having setOutline() set to true auto docOutline {App::GetApplication().getActiveDocument()}; // A WireJoiner object where the value of setOutline() will be changed but no shapes will be // built auto wjNoBuild {WireJoiner()}; // A WireJoiner object where setOutline() will be set to false auto wjNoOutline {WireJoiner()}; // A WireJoiner object where setOutline() will be set to true auto wjOutline {WireJoiner()}; // Reset the parameter to its previous value. hParam->SetInt("Iteration", catchIteration); // Act // Changing only the value of setOutline(). This should set wjNoBuild.IsDone() to false wjNoBuild.setOutline(true); // We can see the effect of setOutline by searching, among all the DocumentObjects created by // the logic, if there are any with a label containing "removed" // Testing first the Document where WireJoiner::Build() will be executed with setOutline set to // false App::GetApplication().setActiveDocument(docNoOutline); wjNoOutline.addShape(edgesNoOutline); wjNoOutline.setOutline(false); wjNoOutline.Build(); auto objsInDoc {docNoOutline->getObjects()}; auto foundRemovedNoOutline {false}; for (const auto& obj : objsInDoc) { foundRemovedNoOutline = (std::string(obj->Label.getValue()).find("removed") != std::string::npos); if (foundRemovedNoOutline) { break; } } // Then testing the Document where WireJoiner::Build() will be executed with setOutline set to // true App::GetApplication().setActiveDocument(docOutline); wjOutline.addShape(edgesOutline); // same as wjOutline.setOutline(true); wjOutline.setOutline(); wjOutline.Build(); objsInDoc = docOutline->getObjects(); auto foundRemovedOutline {false}; for (const auto& obj : objsInDoc) { foundRemovedOutline = (std::string(obj->Label.getValue()).find("removed") != std::string::npos); if (foundRemovedOutline) { break; } } // Assert // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we // only changed the value of setOutline() EXPECT_FALSE(wjNoBuild.IsDone()); // In a document where WireJoiner::Build() is executed with setOutline() set to false there // shouldn't be DocumentObjects with "removed" in their label EXPECT_FALSE(foundRemovedNoOutline); // In a document where WireJoiner::Build() is executed with setOutline() set to true there // should be at least a DocumentObject with "removed" in its label EXPECT_TRUE(foundRemovedOutline); } TEST_F(WireJoinerTest, setTightBound) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; // A vector of edges used as argument for wjNoTightBound.addShape() std::vector edgesNoTightBound {edge1, edge2, edge3, edge4}; // A vector of edges used as argument for wjTightBound.addShape() std::vector edgesTightBound {edge1, edge2, edge3, edge4}; // A WireJoiner object where the value of setTightBound() will be changed but no shapes will be // built auto wjNoBuild {WireJoiner()}; // A WireJoiner object where setTightBound() will be set to false auto wjNoTightBound {WireJoiner()}; // A WireJoiner object where setTightBound() will be set to true auto wjTightBound {WireJoiner()}; // An empty TopoShape that will contain the shapes returned by wjNoTightBound.getOpenWires() auto wireNoTightBound {TopoShape(1)}; // An empty TopoShape that will contain the shapes returned by wjTightBound.getOpenWires() auto wireTightBound {TopoShape(2)}; // Act // Changing only the value of setTightBound(). This should set wjNoBuild.IsDone() to false wjNoBuild.setTightBound(false); // To see the effect of setTightBound() we call WireJoiner::Build() and then // WireJoiner::getOpenWires() wjNoTightBound.addShape(edgesNoTightBound); wjNoTightBound.setTightBound(false); // Calling wjNoTightBound.Build() will put all the edges inside the private object // WireJoiner::WireJoinerP::openWireCompound. wjNoTightBound.Build(); wjNoTightBound.getOpenWires(wireNoTightBound, nullptr, false); wjTightBound.addShape(edgesTightBound); // same as wjTightBound.setTightBound(true); wjTightBound.setTightBound(); // Calling wjTightBound.Build() will put inside the private object // WireJoiner::WireJoinerP::openWireCompound only the edges that don't contribute to the // creation of any closed wire. wjTightBound.Build(); wjTightBound.getOpenWires(wireTightBound, nullptr, false); // Assert // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we // only changed the value of setTightBound() EXPECT_FALSE(wjNoBuild.IsDone()); // In this case the number of edges is equal to 6 because: // edge1 and edge2 aren't modified => 2 edges // edge3 and edge4 are both split in 2 edges => 4 edges // The split is made at the intersection point (0.5, 0.5, 0.0) EXPECT_EQ(wireNoTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 6); // In this case the number of those edges is equal to 1 and that edge is the one with vertexes // at the coordinates (0.5, 0.5, 0.0) - (0.0, 1.0, 0.0) EXPECT_EQ(wireTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 1); } TEST_F(WireJoinerTest, setSplitEdges) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; // A vector of edges used as argument for wjNoSplitEdges.addShape() std::vector edgesNoSplitEdges {edge1, edge2}; // A vector of edges used as argument for wjSplitEdges.addShape() std::vector edgesSplitEdges {edge1, edge2}; // A WireJoiner object where the value of setSplitEdges() will be changed but no shapes will be // built auto wjNoBuild {WireJoiner()}; // A WireJoiner object where setSplitEdges() will be set to false auto wjNoSplitEdges {WireJoiner()}; // To see it's effect it's necessary also to call setTightBound(false) otherwise // WireJoiner::WireJoinerP::splitEdges() will be called in any case wjNoSplitEdges.setTightBound(false); // A WireJoiner object where setSplitEdges() will be set to true auto wjSplitEdges {WireJoiner()}; // To see it's effect it's necessary also to call setTightBound(false) otherwise // WireJoiner::WireJoinerP::splitEdges() will be called in any case wjSplitEdges.setTightBound(false); // An empty TopoShape that will contain the shapes returned by wjNoSplitEdges.getOpenWires() auto wireNoSplitEdges {TopoShape(1)}; // An empty TopoShape that will contain the shapes returned by wjSplitEdges.getOpenWires() auto wireSplitEdges {TopoShape(2)}; // Act // Changing only the value of setSplitEdges(). This should set wjNoBuild.IsDone() to false wjNoBuild.setSplitEdges(false); // To see the effect of setSplitEdges() we call WireJoiner::Build() and then // WireJoiner::getOpenWires() wjNoSplitEdges.addShape(edgesNoSplitEdges); wjNoSplitEdges.setSplitEdges(false); // Calling wjNoSplitEdges.Build() will put all the edges that don't contribute to the creation // of a closed wire inside the private object WireJoiner::WireJoinerP::openWireCompound. wjNoSplitEdges.Build(); wjNoSplitEdges.getOpenWires(wireNoSplitEdges, nullptr, false); wjSplitEdges.addShape(edgesSplitEdges); // same as wjSplitEdges.setSplitEdges(true); wjSplitEdges.setSplitEdges(); // Calling wjSplitEdges.Build() will put inside the private object // WireJoiner::WireJoinerP::openWireCompound all the edges processed by // WireJoiner::WireJoinerP::splitEdges(). wjSplitEdges.Build(); wjSplitEdges.getOpenWires(wireSplitEdges, nullptr, false); // Assert // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we // only changed the value of setSplitEdges() EXPECT_FALSE(wjNoBuild.IsDone()); // In this case the number of edges is equal to the number of edges added with // wjNoSplitEdges.addShape() because none of them is used for the creation of a closed wire. EXPECT_EQ(wireNoSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 2); // In this case the number of those edges is equal to 4 because both the edges added with // wjSplitEdges.addShape() have been split at the intersection point (0.5, 0.5, 0.0) EXPECT_EQ(wireSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 4); } TEST_F(WireJoinerTest, setMergeEdges) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; // A vector of edges used as argument for wjNoMergeEdges.addShape() std::vector edgesNoMergeEdges {edge1, edge2, edge3}; // A vector of edges used as argument for wjMergeEdges.addShape() std::vector edgesMergeEdges {edge1, edge2, edge3}; // A WireJoiner object where the value of setMergeEdges() will be changed but no shapes will be // built auto wjNoBuild {WireJoiner()}; // A WireJoiner object where setMergeEdges() will be set to false auto wjNoMergeEdges {WireJoiner()}; // To see it's effect it's necessary also to call setTightBound(false) otherwise // WireJoiner::WireJoinerP::MergeEdges() will be called in any case wjNoMergeEdges.setTightBound(false); // A WireJoiner object where setMergeEdges() will be set to true auto wjMergeEdges {WireJoiner()}; // To see it's effect it's necessary also to call setTightBound(false) otherwise // WireJoiner::WireJoinerP::MergeEdges() will be called in any case wjMergeEdges.setTightBound(false); // An empty TopoShape that will contain the shapes returned by wjNoMergeEdges.getOpenWires() auto wireNoMergeEdges {TopoShape(1)}; // An empty TopoShape that will contain the shapes returned by wjMergeEdges.getOpenWires() auto wireMergeEdges {TopoShape(2)}; // Act // Changing only the value of setMergeEdges(). This should set wjNoBuild.IsDone() to false wjNoBuild.setMergeEdges(false); // To see the effect of setMergeEdges() we call WireJoiner::Build() and then // WireJoiner::getOpenWires() wjNoMergeEdges.addShape(edgesNoMergeEdges); wjNoMergeEdges.setMergeEdges(false); // Calling wjNoMergeEdges.Build() will put all the edges produced by // WireJoiner::WireJoinerP::splitEdges() in the private object // WireJoiner::WireJoinerP::openWireCompound. wjNoMergeEdges.Build(); wjNoMergeEdges.getOpenWires(wireNoMergeEdges, nullptr, false); wjMergeEdges.addShape(edgesMergeEdges); // same as wjMergeEdges.setMergeEdges(true); wjMergeEdges.setMergeEdges(); // Calling wjMergeEdges.Build() will put, among the edges produced by // WireJoiner::WireJoinerP::splitEdges(), only the edges that are connected to only one of the // others by a single vertex in the private object WireJoiner::WireJoinerP::openWireCompound. // In the code those are called SuperEdges and are // processed by WireJoiner::WireJoinerP::findSuperEdges(). wjMergeEdges.Build(); wjMergeEdges.getOpenWires(wireMergeEdges, nullptr, false); // Assert // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we // only changed the value of setMergeEdges() EXPECT_FALSE(wjNoBuild.IsDone()); // In this case the number of edges is equal to 9 because all the 3 edges intersect the other 2 // and are therefore split in 3 edges each. EXPECT_EQ(wireNoMergeEdges.getSubTopoShapes(TopAbs_EDGE).size(), 9); // In this case the number of edges is equal to 6 because, among the 9 produced by // WireJoiner::WireJoinerP::splitEdges(), 3 of them are connected to more than one other edge // and therefore aren't added by WireJoiner::WireJoinerP::findSuperEdges() EXPECT_EQ(wireMergeEdges.getSubTopoShapes(TopAbs_EDGE).size(), 6); } TEST_F(WireJoinerTest, setTolerance) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto pi {acos(-1)}; auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.1, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.9, 0.0, 0.0)).Edge()}; auto edge5 {BRepBuilderAPI_MakeEdge( gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.9 * std::cos(pi / 18), 0.9 * std::sin(pi / 18), 0.0) ) .Edge()}; // A vector of edges used as argument for wjNegtol.addShape() std::vector edgesNegtol {edge1, edge2, edge3}; // A vector of edges used as argument for wjtol.addShape() std::vector edgestol {edge1, edge2, edge3}; // A vector of edges used as argument for wjNegatol.addShape() std::vector edgesNegatol {edge2, edge3, edge4, edge5}; // A vector of edges used as argument for wjatol.addShape() std::vector edgesatol {edge2, edge3, edge4, edge5}; // A WireJoiner object where the value of setTolerance() will be changed but no shapes will be // built auto wjNoBuild {WireJoiner()}; // A WireJoiner object where setTolerance() will be called passing to the argument tol a // negative value. auto wjNegtol {WireJoiner()}; // A WireJoiner object where setTolerance() will be called passing to the argument tol a // value both positive and not equal to WireJoiner::WireJoinerP::myTol auto wjtol {WireJoiner()}; // A WireJoiner object where setTolerance() will be called passing to the argument atol a // negative value. auto wjNegatol {WireJoiner()}; // A WireJoiner object where setTolerance() will be called passing to the argument atol a // value both positive and not equal to WireJoiner::WireJoinerP::myAngularTol auto wjatol {WireJoiner()}; // An empty TopoShape that will contain the shapes returned by wjNegtol.getOpenWires() auto wireNegtol {TopoShape(1)}; // An empty TopoShape that will contain the shapes returned by wjtol.getOpenWires() auto wiretol {TopoShape(2)}; // An empty TopoShape that will contain the shapes returned by wjNegatol.getOpenWires() auto wireNegatol {TopoShape(3)}; // An empty TopoShape that will contain the shapes returned by wjatol.getOpenWires() auto wireatol {TopoShape(4)}; // Act // Changing only the value of setTolerance(). This should set wjNoBuild.IsDone() to false wjNoBuild.setTolerance(0.1); // To see the effect of setTolerance() we call WireJoiner::Build() and then // WireJoiner::getOpenWires() to get the edges, if any, that aren't used to create a closed wire wjNegtol.addShape(edgesNegtol); // Setting tol to a negative value won't have effect and therefore wjNegtol.pimpl->myTol will // keep the default value. // It's better also to give a negative value for the argument atol otherwise setTolerance() // will set it to 0.0 wjNegtol.setTolerance(-0.1, -pi); wjNegtol.Build(); wjNegtol.getOpenWires(wireNegtol, nullptr, false); wjtol.addShape(edgestol); // Setting tol to a value that will change wjNegtol.pimpl->myTol. // It's better also to give a negative value for the argument atol otherwise setTolerance() // will set it to 0.0 wjtol.setTolerance(0.2, -pi); wjtol.Build(); wjtol.getOpenWires(wiretol, nullptr, false); wjNegatol.addShape(edgesNegatol); // Setting atol to a negative value won't have effect and therefore // wjNegatol.pimpl->myAngularTol will keep the default value. The tol value must be given in any // case. wjNegatol.setTolerance(-0.1, -pi); wjNegatol.Build(); wjNegatol.getOpenWires(wireNegatol, nullptr, false); wjatol.addShape(edgesatol); // Setting atol to a negative value won't have effect and therefore // wjNegatol.pimpl->myAngularTol will keep the default value. We give also the tol value so that // a closed wire can be created. wjatol.setTolerance(0.2, pi / 9); wjatol.Build(); wjatol.getOpenWires(wireatol, nullptr, false); // Assert // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we // only changed the value of setTolerance() EXPECT_FALSE(wjNoBuild.IsDone()); // In this case, as there's a gap between edge1 and edge3, no closed wires are created. EXPECT_TRUE(wjNegtol.Shape().IsNull()); // All the edges added with wjNegtol.addShape() can be extracted with wjNegtol.getOpenWires() EXPECT_EQ(wireNegtol.getSubTopoShapes(TopAbs_EDGE).size(), 3); // In this case, as the gap between edge1 and edge3 is smaller than tol, a closed wire can be // created and it contains all the edges added with wjtol.addShape(). EXPECT_EQ(TopoShape(wjtol.Shape()).getSubTopoShapes(TopAbs_EDGE).size(), 3); // There are no open wires and therefore no edges that create them EXPECT_EQ(wiretol.getSubTopoShapes(TopAbs_EDGE).size(), 0); // In this case, as there's a gap between edge2, edge4 and edge5, no closed wires are created. EXPECT_TRUE(wjNegatol.Shape().IsNull()); // All the edges added with wjNegtol.addShape() can be extracted with wjNegatol.getOpenWires() EXPECT_EQ(wireNegatol.getSubTopoShapes(TopAbs_EDGE).size(), 4); // In this case, as the gap between edge2, edge4 and edge5 is smaller than tol, a closed wire // can be created. // Because of atol, edge4 and edge5 are considerated as duplicates and therefore one of them is // removed by WireJoiner::WireJoinerP::add(). // The closed wire is then created using all the edges added with wjatol.addShape() except the // removed one EXPECT_EQ(TopoShape(wjatol.Shape()).getSubTopoShapes(TopAbs_EDGE).size(), 3); // There are no open wires and therefore no edges that create them EXPECT_EQ(wireatol.getSubTopoShapes(TopAbs_EDGE).size(), 0); } TEST_F(WireJoinerTest, getOpenWires) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.5, 0.5, 0.0), gp_Pnt(1.5, 1.5, 0.0)).Edge()}; // A vector of edges used as argument for wjNoOpenWires.addShape() std::vector edgesNoOpenWires {edge1, edge2, edge3}; // A vector of edges used as argument for wjOriginal.addShape() std::vector edgesOriginal {edge1, edge2, edge4}; // A vector of edges used as argument for wjNoOriginal.addShape() std::vector edgesNoOriginal {edge1, edge2, edge4}; // A vector of TopoShape edges used as argument for wjNoOp.addShape(). A Tag is needed for every // TopoShape, otherwise no element map will be created and no op can be found std::vector edgesNoOp {TopoShape(edge2, 6), TopoShape(edge4, 7)}; // A vector of TopoShape edges used as argument for wjOp.addShape(). A Tag is needed for every // TopoShape, otherwise no element map will be created and no op can be found std::vector edgesOp {TopoShape(edge2, 8), TopoShape(edge4, 9)}; // A WireJoiner object that will create a closed wire and no open wires auto wjNoOpenWires {WireJoiner()}; // A WireJoiner object where the argument noOriginal will be set to false and that will create // an open wire auto wjOriginal {WireJoiner()}; // A WireJoiner object where the argument noOriginal will be set to true and that will create an // open wire auto wjNoOriginal {WireJoiner()}; // A WireJoiner object where the argument op won't be set and that will create an open wire auto wjNoOp {WireJoiner()}; // A WireJoiner object where the argument op will be set and that will create an open wire auto wjOp {WireJoiner()}; // An empty TopoShape that will contain the shapes returned by wjNoOpenWires.getOpenWires() auto wireNoOpenWires {TopoShape(1)}; // An empty TopoShape that will contain the shapes returned by wjOriginal.getOpenWires() auto wireOriginal {TopoShape(2)}; // An empty TopoShape that will contain the shapes returned by wjNoOriginal.getOpenWires() auto wireNoOriginal {TopoShape(3)}; // An empty TopoShape that will contain the shapes returned by wjNoOp.getOpenWires() auto wireNoOp {TopoShape(4)}; // An empty TopoShape that will contain the shapes returned by wjOp.getOpenWires() auto wireOp {TopoShape(5)}; // Act wjNoOpenWires.addShape(edgesNoOpenWires); // wjNoOpenWires.Build() is called by wjNoOpenWires.getOpenWires() wjNoOpenWires.getOpenWires(wireNoOpenWires); wjOriginal.addShape(edgesOriginal); // wjOriginal.Build() is called by wjOriginal.getOpenWires() wjOriginal.getOpenWires(wireOriginal, nullptr, false); wjNoOriginal.addShape(edgesNoOriginal); // wjNoOriginal.Build() is called by wjNoOriginal.getOpenWires() wjNoOriginal.getOpenWires(wireNoOriginal); wjNoOp.addShape(edgesNoOp); // wjNoOp.Build() is called by wjNoOp.getOpenWires() wjNoOp.getOpenWires(wireNoOp, nullptr, false); wjOp.addShape(edgesOp); // wjOp.Build() is called by wjOp.getOpenWires() wjOp.getOpenWires(wireOp, "getOpenWires", false); // Assert // All the edges added with wjNoOpenWires.addShape() are used to create a closed wire, therefore // wireNoOpenWires should be null EXPECT_TRUE(wireNoOpenWires.isNull()); // In this case wireOriginal should contain all the edges added with wjOriginal.addShape(), // except those ones that are split, and all the edges generated by splitting an edge with // another one. // edge1 and edge2 are left untouched, while edge4 is split in two at the intersection point // (1.0, 1.0, 0.0), therefore 4 edges. EXPECT_EQ(wireOriginal.getSubTopoShapes(TopAbs_EDGE).size(), 4); // In this case wireNoOriginal should contain only the edges generated by splitting one of them // with another one. // As edge1 and edge2 are left untouched, the only edges we should find are the ones generated // by splitting edge4 in two at the intersection point (1.0, 1.0, 0.0) EXPECT_EQ(wireNoOriginal.getSubTopoShapes(TopAbs_EDGE).size(), 2); // In this case, as we haven't set a value for op, WireJoiner::WireJoinerP::getOpenWires() will // call TopoShape::makeShapeWithElementMap() which, without a value for op, will use // Part::OpCodes::Maker as value for the various element maps // TODO no longer works // EXPECT_NE(wireNoOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); // In this case WireJoiner::WireJoinerP::getOpenWires() will call // TopoShape::makeShapeWithElementMap() giving "getOpenWires" as value for the op argument. // That value should be found in the various element maps instead of Part::OpCodes::Maker EXPECT_EQ(wireOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); // TODO no longer works // EXPECT_NE(wireOp.getElementMap()[0].name.find("getOpenWires"), -1); } TEST_F(WireJoinerTest, getResultWires) { // Arrange // Create various edges that will be used for the WireJoiner objects tests // Unlike calling WireJoiner::Build(), WireJoiner::getResultWires() returns a shape with edges // only if those given as inputs crosses each others auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(0.1, 0.1, 0.0)).Edge()}; auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; // A vector of edges used as argument for wjNoResultWires.addShape() std::vector edgesNoResultWires {edge1, edge2, edge3}; // A vector of TopoShape edges used as argument for wjNoOp.addShape(). A Tag is needed for every // TopoShape, otherwise no element map will be created and no op can be found std::vector edgesNoOp {TopoShape(edge1, 4), TopoShape(edge2, 5), TopoShape(edge4, 6)}; // A vector of TopoShape edges used as argument for wjOp.addShape(). A Tag is needed for every // TopoShape, otherwise no element map will be created and no op can be found std::vector edgesOp {TopoShape(edge1, 7), TopoShape(edge2, 8), TopoShape(edge4, 9)}; // A WireJoiner object that will create no closed wires auto wjNoResultWires {WireJoiner()}; // A WireJoiner object where the argument op won't be set and that will create a closed wire auto wjNoOp {WireJoiner()}; // A WireJoiner object where the argument op will be set and that will create a closed wire auto wjOp {WireJoiner()}; // An empty TopoShape that will contain the shapes returned by // wireNoResultWires.getResultWires() auto wireNoResultWires {TopoShape(1)}; // An empty TopoShape that will contain the shapes returned by wjNoOp.getResultWires() auto wireNoOp {TopoShape(2)}; // An empty TopoShape that will contain the shapes returned by wjOp.getResultWires() auto wireOp {TopoShape(3)}; // Act wjNoResultWires.addShape(edgesNoResultWires); // wjNoResultWires.Build() is called by wjNoResultWires.getResultWires() wjNoResultWires.getResultWires(wireNoResultWires); wjNoOp.addShape(edgesNoOp); // wjNoOp.Build() is called by wjNoOp.getResultWires() wjNoOp.getResultWires(wireNoOp, nullptr); wjOp.addShape(edgesOp); // wjOp.Build() is called by wjOp.getResultWires() wjOp.getResultWires(wireOp, "getResultWires"); // Assert // All the edges added with wjNoResultWires.addShape() can't create a closed wire, therefore // wireNoResultWires shouldn't have any edges // It's not possible to get an useful result from wireNoResultWires.isNull() because // WireJoiner::WireJoinerP::compound is always created by // WireJoiner::WireJoinerP::builder.MakeCompound(), which doesn't create a null compound EXPECT_EQ(wireNoResultWires.getSubTopoShapes(TopAbs_EDGE).size(), 0); // In this case, as we haven't set a value for op, WireJoiner::WireJoinerP::getResultWires() // will call TopoShape::makeShapeWithElementMap() which, without a value for op, will use // Part::OpCodes::Maker as value for the various element maps EXPECT_NE(wireNoOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); // In this case WireJoiner::WireJoinerP::getResultWires() will call // TopoShape::makeShapeWithElementMap() giving "getResultWires" as value for the op argument. // That value should be found in the various element maps instead of Part::OpCodes::Maker EXPECT_EQ(wireOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); EXPECT_NE(wireOp.getElementMap()[0].name.find("getResultWires"), -1); } #if OCC_VERSION_HEX >= 0x070600 // WireJoiner::Build() has already been tested indirectly in the other tests. // Here we check only the difference with OCCT versions >= 7.6.0 that add the parameter theRange TEST_F(WireJoinerTest, Build) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; // A vector of edges used as argument for wjtheRange.addShape() std::vector edgestheRange {edge1, edge2, edge3}; // A WireJoiner object that will create no closed wires auto wjtheRange {WireJoiner()}; // A Message_ProgressRange object that will be used as argument for wjtheRange.Build() auto mpr {Message_ProgressRange()}; // Act wjtheRange.addShape(edgestheRange); wjtheRange.Build(mpr); // Assert // theRange isn't used in WireJoiner::Build() and therefore not attached to any indicator. // For more reference see // https://dev.opencascade.org/doc/occt-7.6.0/refman/html/class_message___progress_range.html // and // https://dev.opencascade.org/doc/occt-7.6.0/refman/html/class_message___progress_indicator.html EXPECT_FALSE(mpr.IsActive()); } #endif TEST_F(WireJoinerTest, Modified) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; // A vector of edges used as argument for wjModified.addShape() std::vector edges {edge1, edge2, edge3}; // A WireJoiner object that will have shapes modified by the added edges auto wjModified {WireJoiner()}; // Act wjModified.addShape(edges); wjModified.Build(); // Assert // In this case, every edge added with wjModified.addShape() modifies other shapes 3 times // edge1 modifies the edges with vertexes: // (0.0, 0.0, 0.0) - (1.0, 1.0, 0.0) // (-0.1, -0.1, 0.0) - (0.0, 0.0, 0.0) // (1.0, -0.1, 0.0) - (1.0, 0.0, 0.0) EXPECT_EQ(wjModified.Modified(edge1).Size(), 3); // edge2 modifies the edges with vertexes: // (1.0, 1.0, 0.0) - (1.1, 1.1, 0.0) // (0.0, 0.0, 0.0) - (1.0, 0.0, 0.0) // (1.0, 0.0, 0.0) - (1.1, 0.0, 0.0) EXPECT_EQ(wjModified.Modified(edge2).Size(), 3); // edge3 modifies the edges with vertexes: // (1.0, 0.0, 0.0) - (1.0, 1.0, 0.0) // (1.0, 1.0, 0.0) - (1.0, 1.1, 0.0) // (-0.1, 0.0, 0.0) - (0.0, 0.0, 0.0) EXPECT_EQ(wjModified.Modified(edge3).Size(), 3); } TEST_F(WireJoinerTest, Generated) { // Arrange // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; // A vector of edges used as argument for wjGenerated.addShape() std::vector edges {edge1, edge2, edge3}; // A WireJoiner object that will have shapes generated by the added edges auto wjGenerated {WireJoiner()}; // Act wjGenerated.addShape(edges); wjGenerated.Build(); // Assert // There aren't calls to WireJoiner::WireJoinerP::aHistory->AddGenerated() or similar methods in // WireJoiner::WireJoinerP, therefore nothing is returned by calling // WireJoiner::WireJoinerP::aHistory->Generated(). // There's a call to WireJoiner::WireJoinerP::aHistory->Merge() that uses the history produced // by a ShapeFix_Wire object in WireJoiner::WireJoinerP::makeCleanWire() that however looks // always empty as no methods in ShapeFix_Wire call AddGenerated() or similar methods // (checked in OCCT 7.3.0 source // https://git.dev.opencascade.org/gitweb/?p=occt.git;a=snapshot;h=42da0d5115bff683c6b596e66cdeaff957f81e7d;sf=tgz) EXPECT_EQ(wjGenerated.Generated(edge1).Size(), 0); EXPECT_EQ(wjGenerated.Generated(edge2).Size(), 0); EXPECT_EQ(wjGenerated.Generated(edge3).Size(), 0); } TEST_F(WireJoinerTest, IsDeleted) { // Create various edges that will be used for the WireJoiner objects tests auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()}; auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; auto edge5 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.49, 0.0, 0.0), gp_Pnt(0.51, 0.0, 0.0)).Edge()}; // A vector of edges used as argument for wjIsDeleted.addShape() std::vector edges {edge1, edge2, edge3, edge4, edge5}; // A WireJoiner object that will have shapes deleted by the added edges auto wjIsDeleted {WireJoiner()}; // To get all the deleted shapes in this case we also need to set the tolerance value wjIsDeleted.setTolerance(0.03); // Act wjIsDeleted.addShape(edges); wjIsDeleted.Build(); // Assert // In this case, edge1, edge2 and edge3 don't meet the conditions for deletion EXPECT_FALSE(wjIsDeleted.IsDeleted(edge1)); EXPECT_FALSE(wjIsDeleted.IsDeleted(edge2)); EXPECT_FALSE(wjIsDeleted.IsDeleted(edge3)); // edge4 is a duplicate of edge3 and therefore deleted EXPECT_TRUE(wjIsDeleted.IsDeleted(edge4)); // edge5 is smaller that the smallest shape that can be considered with the given value of // tolerance and therefore deleted EXPECT_TRUE(wjIsDeleted.IsDeleted(edge5)); } // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)