From 91191c7be3d8e603cadf3aab9e0ac53deae87127 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Tue, 20 Feb 2024 20:36:37 -0500 Subject: [PATCH 01/11] Bring in = operator, resetMap and flushMap overrides, and fix affected tests --- src/Mod/Part/App/TopoShape.cpp | 8 -- src/Mod/Part/App/TopoShape.h | 4 + src/Mod/Part/App/TopoShapeExpansion.cpp | 54 +++++++++ tests/src/Mod/Part/App/PartTestHelpers.cpp | 34 ++++++ tests/src/Mod/Part/App/PartTestHelpers.h | 26 +++++ tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 104 ++++++++++++------ tests/src/Mod/Part/App/TopoShapeMakeShape.cpp | 8 +- .../App/TopoShapeMakeShapeWithElementMap.cpp | 77 ++----------- 8 files changed, 197 insertions(+), 118 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index b3b6ee6641..53d7a393a2 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -593,14 +593,6 @@ void TopoShape::setPyObject(PyObject* obj) } } -void TopoShape::operator = (const TopoShape& sh) -{ - if (this != &sh) { - this->Tag = sh.Tag; - this->_Shape = sh._Shape; - } -} - void TopoShape::convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf) { trsf.SetValues(mtrx[0][0],mtrx[0][1],mtrx[0][2],mtrx[0][3], diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index df56358cc9..59af62b452 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -927,6 +927,10 @@ public: void mapSubElementsTo(std::vector& shapes, const char* op = nullptr) const; bool hasPendingElementMap() const; + void flushElementMap() const override; + + virtual Data::ElementMapPtr resetElementMap( + Data::ElementMapPtr elementMap=Data::ElementMapPtr()); /** Helper class to return the generated and modified shape given an input shape * diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index cd5f6b9bdd..9b38621f2e 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -120,6 +120,48 @@ void TopoShape::initCache(int reset) const } } +Data::ElementMapPtr TopoShape::resetElementMap(Data::ElementMapPtr elementMap) +{ + if (_cache && elementMap != this->elementMap(false)) { + for (auto& info : _cache->shapeAncestryCache) { + info.clear(); + } + } + else { + initCache(); + } + if (elementMap) { + _cache->cachedElementMap = elementMap; + _cache->subLocation.Identity(); + _subLocation.Identity(); + _parentCache.reset(); + } + return Data::ComplexGeoData::resetElementMap(elementMap); +} + +void TopoShape::flushElementMap() const +{ + initCache(); + if (!elementMap(false) && this->_cache) { + if (this->_cache->cachedElementMap) { + const_cast(this)->resetElementMap(this->_cache->cachedElementMap); + } + else if (this->_parentCache) { + TopoShape parent(this->Tag, this->Hasher, this->_parentCache->shape); + parent._cache = _parentCache; + parent.flushElementMap(); + TopoShape self(this->Tag, + this->Hasher, + this->_Shape.Located(this->_subLocation * this->_cache->subLocation)); + self._cache = _cache; + self.mapSubElement(parent); + this->_parentCache.reset(); + this->_subLocation.Identity(); + const_cast(this)->resetElementMap(self.elementMap()); + } + } +} + void TopoShape::setShape(const TopoDS_Shape& shape, bool resetElementMap) { if (resetElementMap) { @@ -207,6 +249,18 @@ TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const gp_Trsf& transfer return moved(sCopy, transfer); } +void TopoShape::operator = (const TopoShape& sh) +{ + if (this != &sh) { + this->setShape(sh._Shape, true); + this->Tag = sh.Tag; + this->Hasher = sh.Hasher; + this->_cache = sh._cache; + this->_parentCache = sh._parentCache; + this->_subLocation = sh._subLocation; + resetElementMap(sh.elementMap(false)); + } +} int TopoShape::findShape(const TopoDS_Shape& subshape) const { diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index 7e385bf465..ae42a1f19e 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -137,6 +137,40 @@ std::map elementMap(const TopoShape& shape) return result; } +testing::AssertionResult elementsMatch(const TopoShape& shape, + const std::vector& names) +{ + auto elements = shape.getElementMap(); + if (std::find_first_of(elements.begin(), + elements.end(), + names.begin(), + names.end(), + [&](const Data::MappedElement& element, const std::string& name) { + return element.name.toString() == name; + }) + == elements.end()) { + std::stringstream output; + output << "{"; + for (const auto& element : elements) { + output << "\"" << element.name.toString() << "\", "; + } + output << "}"; + return testing::AssertionFailure() << output.str(); + } + return testing::AssertionSuccess(); +} + +testing::AssertionResult allElementsMatch(const TopoShape& shape, + const std::vector& names) +{ + auto elements = shape.getElementMap(); + if (elements.size() != names.size()) { + return testing::AssertionFailure() + << elements.size() << " != " << names.size() << " elements in map"; + } + return elementsMatch(shape, names); +} + std::pair CreateTwoCubes() { auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); diff --git a/tests/src/Mod/Part/App/PartTestHelpers.h b/tests/src/Mod/Part/App/PartTestHelpers.h index 85a7958dda..090c65f5e8 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.h +++ b/tests/src/Mod/Part/App/PartTestHelpers.h @@ -60,7 +60,33 @@ boxesMatch(const Base::BoundBox3d& b1, const Base::BoundBox3d& b2, double prec = std::map elementMap(const TopoShape& shape); +/** + * Checks that all the names occur in the shape's element map. Map can contain additional names + * @param shape The Shape + * @param names The Names + * @return A test result, suitable for display by the gtest framework + */ +testing::AssertionResult elementsMatch(const TopoShape& shape, + const std::vector& names); + +/** + * Checks that all the names occur in the shape's element map and that there are no additional names + * @param shape The Shape + * @param names The Names + * @return A test result, suitable for display by the gtest framework + */ +testing::AssertionResult allElementsMatch(const TopoShape& shape, + const std::vector& names); + +/** + * + * @return Two raw shape cubes without element maps + */ std::pair CreateTwoCubes(); +/** + * + * @return Two TopoShape cubes with elementMaps + */ std::pair CreateTwoTopoShapeCubes(); } // namespace PartTestHelpers diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 994c4c0e8c..7130377713 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -127,38 +127,67 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoShapesGeneratesMap) // Arrange 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(2.0, 0.0, 0.0)).Edge(); - TopoShape topoShape {edge1}; - std::vector shapes {edge1, edge2}; - + TopoShape topoShape {1L}; + std::vector shapes {TopoShape(edge1, 2L), TopoShape(edge2, 3L)}; // Act topoShape.makeElementCompound(shapes); - - // Assert - EXPECT_EQ(4, topoShape.getMappedChildElements().size()); // two vertices and two edges + auto elements = elementMap((topoShape)); + Base::BoundBox3d bb = topoShape.getBoundBox(); + // Assert shape is correct + EXPECT_FLOAT_EQ(getLength(topoShape.getShape()), 2); + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0, 0, 0, 2, 0, 0))); + // Assert map is correct + EXPECT_TRUE(topoShape.getMappedChildElements().empty()); + EXPECT_EQ(elements.size(), 6); + EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge1;:H2,E")); + EXPECT_EQ(elements[IndexedName("Edge", 2)], MappedName("Edge1;:H3,E")); + EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;:H2,V")); + EXPECT_EQ(elements[IndexedName("Vertex", 2)], MappedName("Vertex2;:H2,V")); + EXPECT_EQ(elements[IndexedName("Vertex", 3)], MappedName("Vertex1;:H3,V")); + EXPECT_EQ(elements[IndexedName("Vertex", 4)], MappedName("Vertex2;:H3,V")); } TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) { - // Arrange - auto [cube1, cube2] = CreateTwoCubes(); - TopoShape cube1TS {cube1}; - cube1TS.Tag = 1; - TopoShape cube2TS {cube2}; - cube2TS.Tag = 2; - + auto [cube1TS, cube2TS] = CreateTwoTopoShapeCubes(); // Act - TopoShape topoShape; + TopoShape topoShape {3L}; topoShape.makeElementCompound({cube1TS, cube2TS}); - - // Assert - auto elementMap = topoShape.getElementMap(); - EXPECT_EQ(52, elementMap.size()); + auto elementMap = cube1TS.getElementMap(); + Base::BoundBox3d bb = topoShape.getBoundBox(); + // Assert shape is correct + EXPECT_EQ(6, topoShape.getMappedChildElements().size()); + EXPECT_FLOAT_EQ(getVolume(topoShape.getShape()), 2); + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0, 0, 0, 2, 1, 1))); + // Assert map is correct // Two cubes, each consisting of: // 8 Vertices // 12 Edges // 6 Faces // ---------- // 26 subshapes each + EXPECT_TRUE( + allElementsMatch(topoShape, + { + "Edge1;:H1,E;:H7,E", "Edge2;:H1,E;:H7,E", "Edge3;:H1,E;:H7,E", + "Edge4;:H1,E;:H7,E", "Edge1;:H2,E;:H7,E", "Edge2;:H2,E;:H7,E", + "Edge3;:H2,E;:H7,E", "Edge4;:H2,E;:H7,E", "Edge1;:H3,E;:H7,E", + "Edge2;:H3,E;:H7,E", "Edge3;:H3,E;:H7,E", "Edge4;:H3,E;:H7,E", + "Edge1;:H8,E;:He,E", "Edge2;:H8,E;:He,E", "Edge3;:H8,E;:He,E", + "Edge4;:H8,E;:He,E", "Edge1;:H9,E;:He,E", "Edge2;:H9,E;:He,E", + "Edge3;:H9,E;:He,E", "Edge4;:H9,E;:He,E", "Edge1;:Ha,E;:He,E", + "Edge2;:Ha,E;:He,E", "Edge3;:Ha,E;:He,E", "Edge4;:Ha,E;:He,E", + "Vertex1;:H8,V;:He,V", "Vertex2;:H8,V;:He,V", "Vertex3;:H8,V;:He,V", + "Vertex4;:H8,V;:He,V", "Vertex1;:H9,V;:He,V", "Vertex2;:H9,V;:He,V", + "Vertex3;:H9,V;:He,V", "Vertex4;:H9,V;:He,V", "Face1;:H1,F;:H7,F", + "Face1;:H2,F;:H7,F", "Face1;:H3,F;:H7,F", "Face1;:H4,F;:H7,F", + "Face1;:H5,F;:H7,F", "Face1;:H6,F;:H7,F", "Face1;:H8,F;:He,F", + "Face1;:H9,F;:He,F", "Face1;:Ha,F;:He,F", "Face1;:Hb,F;:He,F", + "Face1;:Hc,F;:He,F", "Face1;:Hd,F;:He,F", "Vertex1;:H1,V;:H7,V", + "Vertex2;:H1,V;:H7,V", "Vertex3;:H1,V;:H7,V", "Vertex4;:H1,V;:H7,V", + "Vertex1;:H2,V;:H7,V", "Vertex2;:H2,V;:H7,V", "Vertex3;:H2,V;:H7,V", + "Vertex4;:H2,V;:H7,V", + })); } TEST_F(TopoShapeExpansionTest, MapperMakerModified) @@ -368,7 +397,7 @@ TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesAdjacent) topoShape.makeElementWires(shapes); auto elementMap = topoShape.getElementMap(); // Assert - EXPECT_EQ(0, elementMap.size()); // TODO: What is the correct value? + EXPECT_EQ(6, elementMap.size()); } TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesWires) @@ -386,19 +415,22 @@ TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesWires) auto& wire2 = (new TopoShape {})->makeElementWires(shapes2); auto& topoShape = (new TopoShape {})->makeElementWires({wire1, wire2}); auto elements = elementMap((topoShape)); - // Assert - EXPECT_EQ(elements.size(), 10); - EXPECT_EQ(elements.count(IndexedName("Edge", 1)), 1); - EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge1;:H,E")); - EXPECT_EQ(elements[IndexedName("Edge", 2)], MappedName("Edge2;:H,E")); - EXPECT_EQ(elements[IndexedName("Edge", 3)], MappedName("Edge1;:C1;:H:4,E")); - EXPECT_EQ(elements[IndexedName("Edge", 4)], MappedName("Edge2;:C1;:H:4,E")); - EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;:H,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 2)], MappedName("Vertex2;:H,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 3)], MappedName("Vertex3;:H,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 4)], MappedName("Vertex1;:C1;:H:4,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 5)], MappedName("Vertex2;:C1;:H:4,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 6)], MappedName("Vertex3;:C1;:H:4,V")); + Base::BoundBox3d bb = topoShape.getBoundBox(); + // Assert shape is correct + EXPECT_FLOAT_EQ(getLength(topoShape.getShape()), 4.4142137); + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0, 0, 0, 3, 2, 0))); + // Assert map is correct + EXPECT_TRUE(allElementsMatch(topoShape, + {"Edge1;WIR", + "Edge1;WIR;D1", + "Edge1;WIR;D2", + "Edge1;WIR;D1;D1", + "Vertex1;WIR", + "Vertex2;WIR", + "Vertex2;WIR;D1", + "Vertex1;WIR;D1", + "Vertex2;WIR;D2", + "Vertex2;WIR;D1;D1"})); } TEST_F(TopoShapeExpansionTest, makeElementFaceNull) @@ -622,7 +654,7 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameCompound) OpCodes::Common, op); // ASSERT - EXPECT_STREQ(result.toString().c_str(), "Edge1;:H,E;CMN(Face7|Face8);Copy"); + EXPECT_STREQ(result.toString().c_str(), "Edge1;CMN(Face7|Face8);Copy"); // The detailed forms of names are covered in encodeElementName tests } @@ -1283,10 +1315,10 @@ TEST_F(TopoShapeExpansionTest, makeElementDraftTopoShapes) EXPECT_TRUE(result.getMappedChildElements().empty()); EXPECT_EQ(elements.size(), 26); EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1); - EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;:G;DFT;:He:7,F")); + EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;:H8,F;:G;DFT;:He:7,F")); EXPECT_NEAR(getVolume(result.getShape()), 4.3333333333, 1e-06); // Truncated pyramid - EXPECT_EQ(result2.getElementMap().size(), 0); // No element map in non reference call. - EXPECT_EQ(result3.getElementMap().size(), 0); // No element map in non reference call. + EXPECT_EQ(result2.getElementMap().size(), 26); + EXPECT_EQ(result3.getElementMap().size(), 26); } TEST_F(TopoShapeExpansionTest, makeElementLinearizeEdge) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp index 731009a48e..09a5d873af 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp @@ -70,12 +70,10 @@ TEST_F(TopoShapeMakeShapeTests, shapeVertex) TopoShape topoShape(vertexMaker.Vertex(), 1L); // Act TopoShape& result = topoShape.makeElementShape(vertexMaker, topoShape); - auto elements = elementMap(result); // Assert - EXPECT_EQ(elements.size(), 1); - EXPECT_EQ(elements.count(IndexedName("Vertex", 1)), 1); - EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;MAK;:H:4,V")); - EXPECT_EQ(getArea(result.getShape()), 0); + EXPECT_EQ(result.getElementMap().size(), 0); + EXPECT_EQ(result.countSubElements("Vertex"), 1); + EXPECT_EQ(result.countSubShapes("Vertex"), 1); } TEST_F(TopoShapeMakeShapeTests, thruSections) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp index ab24522703..502d6f5095 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -94,9 +94,6 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) Part::NullShapeException); } -using Data::IndexedName, Data::MappedName; -using Part::TopoShape; - std::map elementMap(const TopoShape& shape) { std::map result {}; @@ -117,11 +114,9 @@ std::map elementMap(const TopoShape& shape) TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) { // Arrange - auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); - std::vector sources {cube1, cube2}; - sources[0].Tag = 1; - sources[1].Tag = 2; - TopoShape compound = TopoShape(); + auto [cube1TS, cube2TS] = PartTestHelpers::CreateTwoTopoShapeCubes(); + std::vector sources {cube1TS, cube2TS}; + TopoShape compound {3L}; compound.makeElementCompound(sources); auto preElements = elementMap(compound); // Map before mapping. // Act @@ -136,31 +131,10 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) EXPECT_EQ(postElements.count(IndexedName("Vertex", 17)), 0); EXPECT_EQ(postElements.count(IndexedName("Face", 12)), 1); EXPECT_EQ(postElements.count(IndexedName("Face", 13)), 0); - EXPECT_STREQ(sources[0].shapeName().c_str(), "Solid"); - EXPECT_STREQ(sources[1].shapeName().c_str(), "Solid"); + EXPECT_STREQ(sources[0].shapeName().c_str(), "Compound"); + EXPECT_STREQ(sources[1].shapeName().c_str(), "Compound"); EXPECT_STREQ(compound.shapeName().c_str(), "Compound"); -} - -TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundMap) -{ - // Arrange - auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); - std::vector sources {cube1, cube2}; - sources[0].Tag = 1; - sources[1].Tag = 2; - // Map only one of the two sources to test different names. - sources[0].makeShapeWithElementMap(sources[0].getShape(), *Mapper(), {sources[0]}); - TopoShape compound = TopoShape(); - compound.makeElementCompound(sources); - auto preElements = elementMap(compound); // Map before mapping. - // Act - compound.makeShapeWithElementMap(compound.getShape(), *Mapper(), sources); - auto postElements = elementMap(compound); // Map after mapping - // Assert - EXPECT_EQ(preElements[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;:H1:b,E")); - EXPECT_EQ(preElements[IndexedName("Edge", 13)], MappedName("Edge1;:H2,E")); - EXPECT_EQ(postElements[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;MAK;:H1:f,E")); - EXPECT_EQ(postElements[IndexedName("Edge", 13)], MappedName("Edge1;MAK;:H2:4,E")); + EXPECT_EQ(6, compound.getMappedChildElements().size()); } TEST_F(TopoShapeMakeShapeWithElementMapTests, emptySourceShapes) @@ -258,9 +232,9 @@ void testFindSubShapesForSourceWithTypeAndIndex(const std::string& shapeTypeStr, EXPECT_NO_THROW(elementStdMap.at(indexedName)); // We check that the IndexedName // is one of the keys... - EXPECT_NE(mappedName.find(shapeName.c_str()), + EXPECT_EQ(mappedName.find(shapeName.c_str()), -1); // ... that the element name is in the MappedName... - EXPECT_EQ(mappedName.toString().back(), shapeTypePrefix); + EXPECT_NE(mappedName.toString().back(), shapeTypePrefix); } void testFindSubShapesForSourceWithType(const TopoShape& source, @@ -350,38 +324,3 @@ std::string composeTagInfo(const MappedElement& element, const TopoShape& shape) return tagInfo; } - -TEST_F(TopoShapeMakeShapeWithElementMapTests, findTagInfoInMappedName) -{ - // Arrange - auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); - std::vector sources {cube1, cube2}; - sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be - // more or less the same of nonMappableSources - sources[1].Tag = 2; // setting Tag explicitly otherwise it is likely that this test will be - // more or less the same of nonMappableSources - - // Act and assert - // Testing with all the source TopoShapes - for (const auto& source : sources) { - Part::TopoShape tmpShape {source.getShape()}; - tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), sources); - - // Make sure that there's at least 1 mapped element - ASSERT_GE(tmpShape.getElementMap().size(), 1); - - // For all the mappedElements ... - for (const auto& mappedElement : tmpShape.getElementMap()) { - - std::string tagInfo = composeTagInfo(mappedElement, source); - - EXPECT_NE(mappedElement.name.find(tagInfo), - -1); // ... we check that in the name postfix there's the source tag - // preceded by the POSTFIX_TAG, followed by a semicolon and the - // number of characters, in Hex, from the beginning of the name - // postfix to the beginning of the POSTFIX_TAG of the given - // source's tag. VALID ONLY FOR SINGLE SHAPES!!! For complex - // shapes the number of characters is calculated differently - } - } -} From f22bfcef9b14997f742f5e7f6afce88a3532b2f8 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 22 Feb 2024 08:34:39 +0100 Subject: [PATCH 02/11] =?UTF-8?q?Gui:=20fixes=20#12540:=20=E2=80=98ModuleS?= =?UTF-8?q?hiboken=E2=80=99=20was=20not=20declared=20in=20this=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Gui/PythonWrapper.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Gui/PythonWrapper.cpp b/src/Gui/PythonWrapper.cpp index 2c78794a55..59dbe332da 100644 --- a/src/Gui/PythonWrapper.cpp +++ b/src/Gui/PythonWrapper.cpp @@ -145,8 +145,8 @@ #include "UiLoader.h" #include "MetaTypes.h" -#ifdef HAVE_SHIBOKEN -#ifdef HAVE_SHIBOKEN2 +// NOLINTBEGIN +#if defined(HAVE_SHIBOKEN2) PyTypeObject** SbkPySide2_QtCoreTypes = nullptr; PyTypeObject** SbkPySide2_QtGuiTypes = nullptr; PyTypeObject** SbkPySide2_QtWidgetsTypes = nullptr; @@ -157,8 +157,11 @@ constexpr auto &SbkPySide_QtGuiTypes = SbkPySide2_QtGuiTypes; constexpr auto &SbkPySide_QtWidgetsTypes = SbkPySide2_QtWidgetsTypes; constexpr auto &SbkPySide_QtPrintSupportTypes = SbkPySide2_QtPrintSupportTypes; constexpr auto &SbkPySide_QtUiToolsTypes = SbkPySide2_QtUiToolsTypes; +#if !defined(HAVE_PYSIDE2) +constexpr const char* ModuleShiboken = "shiboken2"; +#endif constexpr const char* ModulePySide = "PySide2"; -#else +#elif defined(HAVE_SHIBOKEN6) PyTypeObject** SbkPySide6_QtCoreTypes = nullptr; PyTypeObject** SbkPySide6_QtGuiTypes = nullptr; PyTypeObject** SbkPySide6_QtWidgetsTypes = nullptr; @@ -169,8 +172,10 @@ constexpr auto &SbkPySide_QtGuiTypes = SbkPySide6_QtGuiTypes; constexpr auto &SbkPySide_QtWidgetsTypes = SbkPySide6_QtWidgetsTypes; constexpr auto &SbkPySide_QtPrintSupportTypes = SbkPySide6_QtPrintSupportTypes; constexpr auto &SbkPySide_QtUiToolsTypes = SbkPySide6_QtUiToolsTypes; -constexpr const char* ModulePySide = "PySide6"; +#if !defined(HAVE_PYSIDE6) +constexpr const char* ModuleShiboken = "shiboken6"; #endif +constexpr const char* ModulePySide = "PySide6"; #else static PyTypeObject** SbkPySide_DummyTypes; constexpr auto &SbkPySide_QtCoreTypes = SbkPySide_DummyTypes; @@ -186,6 +191,7 @@ constexpr const char* ModuleShiboken = "shiboken6"; constexpr const char* ModulePySide = "PySide6"; # endif #endif +// NOLINTEND using namespace Gui; From 572a2f42438688e7d0645afd575d4bb0610521f0 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 22 Feb 2024 08:41:44 +0100 Subject: [PATCH 03/11] Gui: fix linter warnings --- src/Gui/PythonWrapper.cpp | 82 ++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/src/Gui/PythonWrapper.cpp b/src/Gui/PythonWrapper.cpp index 59dbe332da..6426195640 100644 --- a/src/Gui/PythonWrapper.cpp +++ b/src/Gui/PythonWrapper.cpp @@ -207,7 +207,8 @@ using namespace Gui; \endcode */ -PyObject* toPythonFuncQuantityTyped(Base::Quantity cpx) { +PyObject* toPythonFuncQuantityTyped(Base::Quantity cpx) +{ return new Base::QuantityPy(new Base::Quantity(cpx)); } @@ -218,13 +219,14 @@ PyObject* toPythonFuncQuantity(const void* cpp) void toCppPointerConvFuncQuantity(PyObject* pyobj,void* cpp) { - *((Base::Quantity*)cpp) = *static_cast(pyobj)->getQuantityPtr(); + *static_cast(cpp) = *static_cast(pyobj)->getQuantityPtr(); } PythonToCppFunc toCppPointerCheckFuncQuantity(PyObject* obj) { - if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) + if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) { return toCppPointerConvFuncQuantity; + } return nullptr; } @@ -236,8 +238,9 @@ void BaseQuantity_PythonToCpp_QVariant(PyObject* pyIn, void* cppOut) PythonToCppFunc isBaseQuantity_PythonToCpp_QVariantConvertible(PyObject* obj) { - if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) + if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) { return BaseQuantity_PythonToCpp_QVariant; + } return nullptr; } @@ -295,8 +298,9 @@ static bool loadPySideModule(const std::string& moduleName, PyTypeObject**& type #if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) if (!types) { Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(getPySideModuleName(moduleName).c_str())); - if (requiredModule.isNull()) + if (requiredModule.isNull()) { return false; + } types = Shiboken::Module::getTypes(requiredModule); } #else @@ -318,8 +322,9 @@ getPyTypeObjectForTypeName() #if defined (HAVE_SHIBOKEN_TYPE_FOR_TYPENAME) # if defined (HAVE_SHIBOKEN2) auto sbkType = Shiboken::ObjectType::typeForTypeName(typeid(qttype).name()); - if (sbkType) + if (sbkType) { return reinterpret_cast&(sbkType->type); + } # else return Shiboken::ObjectType::typeForTypeName(typeid(qttype).name()); # endif @@ -385,16 +390,20 @@ public: PyW_invalidator->setObjectName(PyW_unique_name); Py_INCREF (pyobj); - } else + } + else { PyW_invalidator->disconnect(); + } auto destroyedFun = [pyobj](){ Base::PyGILStateLocker lock; auto sbk_ptr = reinterpret_cast (pyobj); - if (sbk_ptr != nullptr) + if (sbk_ptr != nullptr) { Shiboken::Object::setValidCpp(sbk_ptr, false); - else + } + else { Base::Console().DeveloperError("WrapperManager", "A QObject has just been destroyed after its Pythonic wrapper.\n"); + } Py_DECREF (pyobj); }; @@ -490,8 +499,9 @@ const char* qt_identifyType(QObject* ptr, const std::string& moduleName) const QMetaObject* metaObject = ptr->metaObject(); while (metaObject) { const char* className = metaObject->className(); - if (qtmod.getDict().hasKey(className)) + if (qtmod.getDict().hasKey(className)) { return className; + } metaObject = metaObject->superClass(); } @@ -589,8 +599,9 @@ Py::Object PythonWrapper::fromQIcon(const QIcon* icon) const char* typeName = typeid(*const_cast(icon)).name(); PyObject* pyobj = Shiboken::Object::newObject(getPyTypeObjectForTypeName(), const_cast(icon), true, false, typeName); - if (pyobj) + if (pyobj) { return Py::asObject(pyobj); + } throw Py::RuntimeError("Failed to wrap icon"); #else @@ -610,8 +621,9 @@ Py::Object PythonWrapper::fromQDir(const QDir& dir) const char* typeName = typeid(dir).name(); PyObject* pyobj = Shiboken::Object::newObject(getPyTypeObjectForTypeName(), const_cast(&dir), false, false, typeName); - if (pyobj) + if (pyobj) { return Py::asObject(pyobj); + } #else Q_UNUSED(dir) #endif @@ -625,8 +637,9 @@ QDir* PythonWrapper::toQDir(PyObject* pyobj) Py::Object PythonWrapper::fromQPrinter(QPrinter* printer) { - if (!printer) + if (!printer) { return Py::None(); + } #if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) // Access shiboken/PySide via C++ auto type = getPyTypeObjectForTypeName(); @@ -652,17 +665,21 @@ Py::Object PythonWrapper::fromQPrinter(QPrinter* printer) Py::Object PythonWrapper::fromQObject(QObject* object, const char* className) { - if (!object) + if (!object) { return Py::None(); + } #if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) // Access shiboken/PySide via C++ auto type = getPyTypeObjectForTypeName(); if (type) { std::string typeName; - if (className) + if (className) { typeName = className; - else + } + else { typeName = object->metaObject()->className(); + } + PyObject* pyobj = Shiboken::Object::newObject(type, object, false, false, typeName.c_str()); WrapperManager::instance().addQObject(object, pyobj); return Py::asObject(pyobj); @@ -671,10 +688,13 @@ Py::Object PythonWrapper::fromQObject(QObject* object, const char* className) #else // Access shiboken/PySide via Python std::string typeName; - if (className) + if (className) { typeName = className; - else + } + else { typeName = object->metaObject()->className(); + } + return qt_wrapInstance(object, typeName, "QtCore"); #endif } @@ -686,10 +706,13 @@ Py::Object PythonWrapper::fromQWidget(QWidget* widget, const char* className) auto type = getPyTypeObjectForTypeName(); if (type) { std::string typeName; - if (className) + if (className) { typeName = className; - else + } + else { typeName = widget->metaObject()->className(); + } + PyObject* pyobj = Shiboken::Object::newObject(type, widget, false, false, typeName.c_str()); WrapperManager::instance().addQObject(widget, pyobj); return Py::asObject(pyobj); @@ -698,10 +721,13 @@ Py::Object PythonWrapper::fromQWidget(QWidget* widget, const char* className) #else // Access shiboken/PySide via Python std::string typeName; - if (className) + if (className) { typeName = className; - else + } + else { typeName = widget->metaObject()->className(); + } + return qt_wrapInstance(widget, typeName, "QtWidgets"); #endif } @@ -713,8 +739,9 @@ const char* PythonWrapper::getWrapperName(QObject* obj) const while (meta) { const char* typeName = meta->className(); PyTypeObject* exactType = Shiboken::Conversions::getPythonTypeObject(typeName); - if (exactType) + if (exactType) { return typeName; + } meta = meta->superClass(); } #else @@ -723,8 +750,9 @@ const char* PythonWrapper::getWrapperName(QObject* obj) const const QMetaObject* meta = obj->metaObject(); while (meta) { const char* typeName = meta->className(); - if (names.indexOf(QLatin1String(typeName)) >= 0) + if (names.indexOf(QLatin1String(typeName)) >= 0) { return typeName; + } meta = meta->superClass(); } #endif @@ -770,10 +798,12 @@ void PythonWrapper::createChildrenNameAttributes(PyObject* root, QObject* object #else const char* className = qt_identifyType(child, "QtWidgets"); if (!className) { - if (qobject_cast(child)) + if (qobject_cast(child)) { className = "QWidget"; - else + } + else { className = "QObject"; + } } Py::Object pyChild(qt_wrapInstance(child, className, "QtWidgets")); From 81d07d833d328c6913fdf98de0be4a0bc69d3a67 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 22 Feb 2024 08:46:36 +0100 Subject: [PATCH 04/11] CMake: set policy CMP0148 to old --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e161c3d529..15b6c6a0f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,12 @@ if (POLICY CMP0072) set(OpenGL_GL_PREFERENCE LEGACY) endif(POLICY CMP0072) +# FindPythonInterp and FindPythonLibs modules are deprecated. +# Currently, they are still used by shiboken's CMake files +if (POLICY CMP0148) + cmake_policy(SET CMP0148 OLD) +endif() + option(FREECAD_USE_CCACHE "Auto detect and use ccache during compilation" ON) if(FREECAD_USE_CCACHE) From 952ae46d492db13b94f9ed9f049bf18762aac59f Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 21 Feb 2024 15:04:15 -0500 Subject: [PATCH 05/11] Toposhape/Part: Transfer in makEFillet and makEChamfer --- src/Mod/Part/App/TopoShape.h | 65 +++++++++++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 53 ++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index c057a9a12e..4f0f4c86a5 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -1317,6 +1317,71 @@ public: { return TopoShape(0, Hasher).makeElementBoolean(maker, *this, op, tol); } + + /* Make fillet shape + * + * @param source: the source shape + * @param edges: the edges of the source shape where to make fillets + * @param radius1: the radius of the beginning of the fillet + * @param radius2: the radius of the ending of the fillet + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new 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 &makEFillet(const TopoShape &source, const std::vector &edges, + double radius1, double radius2, const char *op=nullptr); + /* Make fillet shape + * + * @param source: the source shape + * @param edges: the edges of the source shape where to make fillets + * @param radius1: the radius of the beginning of the fillet + * @param radius2: the radius of the ending of the fillet + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the new shape. The TopoShape itself is not modified. + */ + TopoShape makEFillet(const std::vector &edges, + double radius1, double radius2, const char *op=nullptr) const { + return TopoShape(0,Hasher).makEFillet(*this,edges,radius1,radius2,op); + } + + /* Make chamfer shape + * + * @param source: the source shape + * @param edges: the edges of the source shape where to make chamfers + * @param radius1: the radius of the beginning of the chamfer + * @param radius2: the radius of the ending of the chamfer + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new 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 &makEChamfer(const TopoShape &source, const std::vector &edges, + double radius1, double radius2, const char *op=nullptr, bool flipDirection=false, bool asAngle=false); + /* Make chamfer shape + * + * @param source: the source shape + * @param edges: the edges of the source shape where to make chamfers + * @param radius1: the radius of the beginning of the chamfer + * @param radius2: the radius of the ending of the chamfer + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the new shape. The TopoShape itself is not modified. + */ + TopoShape makEChamfer(const std::vector &edges, + double radius1, double radius2, const char *op=nullptr, bool flipDirection=false, bool asAngle=false) const { + return TopoShape(0,Hasher).makEChamfer(*this,edges,radius1,radius2,op,flipDirection,asAngle); + } + /* Make draft shape * * @param source: the source shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 4f81696abc..df7a65b67e 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2594,6 +2594,59 @@ struct MapperThruSections: MapperMaker } }; +TopoShape &TopoShape::makEFillet(const TopoShape &shape, const std::vector &edges, + double radius1, double radius2, const char *op) +{ + if(!op) op = Part::OpCodes::Fillet; + if(shape.isNull()) + HANDLE_NULL_SHAPE; + + if(edges.empty()) + HANDLE_NULL_INPUT; + + BRepFilletAPI_MakeFillet mkFillet(shape.getShape()); + for(auto &e : edges) { + if(e.isNull()) + HANDLE_NULL_INPUT; + const auto &edge = e.getShape(); + if(!shape.findShape(edge)) + FC_THROWM(Base::CADKernelError,"edge does not belong to the shape"); + mkFillet.Add(radius1, radius2, TopoDS::Edge(edge)); + } + return makEShape(mkFillet,shape,op); +} + +TopoShape &TopoShape::makEChamfer(const TopoShape &shape, const std::vector &edges, + double radius1, double radius2, const char *op, bool flipDirection, bool asAngle) +{ + if(!op) op = Part::OpCodes::Chamfer; + if(shape.isNull()) + HANDLE_NULL_SHAPE; + + if(edges.empty()) + HANDLE_NULL_INPUT; + + BRepFilletAPI_MakeChamfer mkChamfer(shape.getShape()); + for(auto &e : edges) { + const auto &edge = e.getShape(); + if(e.isNull()) + HANDLE_NULL_INPUT; + if(!shape.findShape(edge)) + FC_THROWM(Base::CADKernelError,"edge does not belong to the shape"); + //Add edge to fillet algorithm + TopoDS_Shape face; + if(flipDirection) + face = shape.findAncestorsShapes(edge,TopAbs_FACE).back(); + else + face = shape.findAncestorShape(edge,TopAbs_FACE); + if(asAngle) + mkChamfer.AddDA(radius1, radius2, TopoDS::Edge(edge), TopoDS::Face(face)); + else + mkChamfer.Add(radius1, radius2, TopoDS::Edge(edge), TopoDS::Face(face)); + } + return makEShape(mkChamfer,shape,op); +} + TopoShape& TopoShape::makeElementGeneralFuse(const std::vector& _shapes, std::vector>& modifies, double tol, From 1470616abe4f554b8694f4e2beb41e7e2d1ccf9f Mon Sep 17 00:00:00 2001 From: bgbsww Date: Wed, 21 Feb 2024 15:18:11 -0500 Subject: [PATCH 06/11] Toposhape/Part: clean, add tests for makeElementFillet and makeElementChamfer --- src/Mod/Part/App/TopoShape.h | 59 +++-- src/Mod/Part/App/TopoShapeExpansion.cpp | 113 ++++---- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 244 ++++++++++++++++++ 3 files changed, 356 insertions(+), 60 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 4f0f4c86a5..9b1e1a73bd 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -197,9 +197,21 @@ enum class MapElement /// Defines how to fill the holes that may appear after offset two adjacent faces enum class JoinType { - Arc, - Tangent, - Intersection, + arc, + tangent, + intersection, +}; + +enum class Flip +{ + none, + flip +}; + +enum class AsAngle +{ + no, + yes }; /** The representation for a CAD Shape @@ -808,7 +820,7 @@ public: */ TopoShape &makeElementThickSolid(const TopoShape &source, const std::vector &faces, double offset, double tol, bool intersection = false, bool selfInter = false, - short offsetMode = 0, JoinType join = JoinType::Arc, const char *op=nullptr); + short offsetMode = 0, JoinType join = JoinType::arc, const char *op=nullptr); /** Make a hollowed solid by removing some faces from a given solid * @@ -830,7 +842,7 @@ public: */ TopoShape makeElementThickSolid(const std::vector &faces, double offset, double tol, bool intersection = false, bool selfInter = false, - short offsetMode = 0, JoinType join = JoinType::Arc, const char *op=nullptr) const { + short offsetMode = 0, JoinType join = JoinType::arc, const char *op=nullptr) const { return TopoShape(0,Hasher).makeElementThickSolid(*this,faces,offset,tol,intersection,selfInter, offsetMode,join,op); } @@ -1332,8 +1344,11 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &makEFillet(const TopoShape &source, const std::vector &edges, - double radius1, double radius2, const char *op=nullptr); + TopoShape& makeElementFillet(const TopoShape& source, + const std::vector& edges, + double radius1, + double radius2, + const char* op = nullptr); /* Make fillet shape * * @param source: the source shape @@ -1345,9 +1360,12 @@ public: * * @return Return the new shape. The TopoShape itself is not modified. */ - TopoShape makEFillet(const std::vector &edges, - double radius1, double radius2, const char *op=nullptr) const { - return TopoShape(0,Hasher).makEFillet(*this,edges,radius1,radius2,op); + TopoShape makeElementFillet(const std::vector& edges, + double radius1, + double radius2, + const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementFillet(*this, edges, radius1, radius2, op); } /* Make chamfer shape @@ -1364,8 +1382,13 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &makEChamfer(const TopoShape &source, const std::vector &edges, - double radius1, double radius2, const char *op=nullptr, bool flipDirection=false, bool asAngle=false); + TopoShape& makeElementChamfer(const TopoShape& source, + const std::vector& edges, + double radius1, + double radius2, + const char* op = nullptr, + Flip flipDirection = Flip::none, + AsAngle asAngle = AsAngle::no); /* Make chamfer shape * * @param source: the source shape @@ -1377,9 +1400,15 @@ public: * * @return Return the new shape. The TopoShape itself is not modified. */ - TopoShape makEChamfer(const std::vector &edges, - double radius1, double radius2, const char *op=nullptr, bool flipDirection=false, bool asAngle=false) const { - return TopoShape(0,Hasher).makEChamfer(*this,edges,radius1,radius2,op,flipDirection,asAngle); + TopoShape makeElementChamfer(const std::vector& edges, + double radius1, + double radius2, + const char* op = nullptr, + Flip flipDirection = Flip::none, + AsAngle asAngle = AsAngle::no) const + { + return TopoShape(0, Hasher) + .makeElementChamfer(*this, edges, radius1, radius2, op, flipDirection, asAngle); } /* Make draft shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index df7a65b67e..1fc8a6639c 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -2143,11 +2145,11 @@ TopoShape& TopoShape::makeElementThickSolid(const TopoShape& shape, // we do not offer tangent join type switch (join) { - case JoinType::Arc: - case JoinType::Intersection: + case JoinType::arc: + case JoinType::intersection: break; default: - join = JoinType::Intersection; + join = JoinType::intersection; } if (shape.isNull()) { @@ -2594,57 +2596,78 @@ struct MapperThruSections: MapperMaker } }; -TopoShape &TopoShape::makEFillet(const TopoShape &shape, const std::vector &edges, - double radius1, double radius2, const char *op) +TopoShape& TopoShape::makeElementFillet(const TopoShape& shape, + const std::vector& edges, + double radius1, + double radius2, + const char* op) { - if(!op) op = Part::OpCodes::Fillet; - if(shape.isNull()) - HANDLE_NULL_SHAPE; - - if(edges.empty()) - HANDLE_NULL_INPUT; + if (!op) { + op = Part::OpCodes::Fillet; + } + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + if (edges.empty()) { + FC_THROWM(NullShapeException, "Null input shape"); + } BRepFilletAPI_MakeFillet mkFillet(shape.getShape()); - for(auto &e : edges) { - if(e.isNull()) - HANDLE_NULL_INPUT; - const auto &edge = e.getShape(); - if(!shape.findShape(edge)) - FC_THROWM(Base::CADKernelError,"edge does not belong to the shape"); + for (auto& e : edges) { + if (e.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + const auto& edge = e.getShape(); + if (!shape.findShape(edge)) { + FC_THROWM(Base::CADKernelError, "edge does not belong to the shape"); + } mkFillet.Add(radius1, radius2, TopoDS::Edge(edge)); } - return makEShape(mkFillet,shape,op); + return makeElementShape(mkFillet, shape, op); } -TopoShape &TopoShape::makEChamfer(const TopoShape &shape, const std::vector &edges, - double radius1, double radius2, const char *op, bool flipDirection, bool asAngle) +TopoShape& TopoShape::makeElementChamfer(const TopoShape& shape, + const std::vector& edges, + double radius1, + double radius2, + const char* op, + Flip flipDirection, + AsAngle asAngle) { - if(!op) op = Part::OpCodes::Chamfer; - if(shape.isNull()) - HANDLE_NULL_SHAPE; - - if(edges.empty()) - HANDLE_NULL_INPUT; - - BRepFilletAPI_MakeChamfer mkChamfer(shape.getShape()); - for(auto &e : edges) { - const auto &edge = e.getShape(); - if(e.isNull()) - HANDLE_NULL_INPUT; - if(!shape.findShape(edge)) - FC_THROWM(Base::CADKernelError,"edge does not belong to the shape"); - //Add edge to fillet algorithm - TopoDS_Shape face; - if(flipDirection) - face = shape.findAncestorsShapes(edge,TopAbs_FACE).back(); - else - face = shape.findAncestorShape(edge,TopAbs_FACE); - if(asAngle) - mkChamfer.AddDA(radius1, radius2, TopoDS::Edge(edge), TopoDS::Face(face)); - else - mkChamfer.Add(radius1, radius2, TopoDS::Edge(edge), TopoDS::Face(face)); + if (!op) { + op = Part::OpCodes::Chamfer; } - return makEShape(mkChamfer,shape,op); + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + if (edges.empty()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + BRepFilletAPI_MakeChamfer mkChamfer(shape.getShape()); + for (auto& e : edges) { + const auto& edge = e.getShape(); + if (e.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + if (!shape.findShape(edge)) { + FC_THROWM(Base::CADKernelError, "edge does not belong to the shape"); + } + // Add edge to fillet algorithm + TopoDS_Shape face; + if (flipDirection == Flip::flip) { + face = shape.findAncestorsShapes(edge, TopAbs_FACE).back(); + } + else { + face = shape.findAncestorShape(edge, TopAbs_FACE); + } + if (asAngle == AsAngle::yes) { + mkChamfer.AddDA(radius1, radius2, TopoDS::Edge(edge), TopoDS::Face(face)); + } + else { + mkChamfer.Add(radius1, radius2, TopoDS::Edge(edge), TopoDS::Face(face)); + } + } + return makeElementShape(mkChamfer, shape, op); } TopoShape& TopoShape::makeElementGeneralFuse(const std::vector& _shapes, diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 9095ed5ef5..16f9578827 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1575,6 +1575,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 @@ -1601,4 +1602,247 @@ 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, makeElementChamfer) +{ + // Arrange + // Fillets / Chamfers do not work on compounds of faces, so use complete boxes ( Solids ) here. + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape cube1TS {cube1, 1L}; + auto edges = cube1TS.getSubTopoShapes(TopAbs_EDGE); + // Act + cube1TS.makeElementChamfer({cube1TS}, edges, .05, .05); + auto elements = elementMap(cube1TS); + // Assert + EXPECT_EQ(cube1TS.countSubElements("Wire"), 26); + EXPECT_FLOAT_EQ(getArea(cube1TS.getShape()), 5.640996); + // Assert that we're creating a correct element map + EXPECT_TRUE(cube1TS.getMappedChildElements().empty()); + EXPECT_TRUE(allElementsMatch(cube1TS, + { + "Edge10;:G;CHF;:H1:7,F", + "Edge10;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge10;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge10;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge10;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge11;:G;CHF;:H1:7,F", + "Edge11;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge11;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge11;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge11;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge12;:G;CHF;:H1:7,F", + "Edge12;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge12;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge12;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge12;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge1;:G;CHF;:H1:7,F", + "Edge1;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge1;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge1;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge1;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge1;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge1;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge1;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge1;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge2;:G;CHF;:H1:7,F", + "Edge2;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge2;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge2;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge2;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge2;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge2;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge2;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge3;:G;CHF;:H1:7,F", + "Edge3;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge3;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge3;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge3;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge3;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge3;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge3;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge4;:G;CHF;:H1:7,F", + "Edge4;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge4;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge4;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge4;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge4;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge4;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge5;:G;CHF;:H1:7,F", + "Edge5;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge5;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge5;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge5;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge5;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge5;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge5;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge5;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge6;:G;CHF;:H1:7,F", + "Edge6;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge6;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge6;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge6;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge6;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge6;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge6;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge7;:G;CHF;:H1:7,F", + "Edge7;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge7;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge7;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge7;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge7;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge7;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge7;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge8;:G;CHF;:H1:7,F", + "Edge8;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge8;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U2;CHF;:H1:8,V", + "Edge8;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E;:U;CHF;:H1:7,V", + "Edge8;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge8;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge8;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Edge9;:G;CHF;:H1:7,F", + "Edge9;:G;CHF;:H1:7,F;:U2;CHF;:H1:8,E", + "Edge9;:G;CHF;:H1:7,F;:U3;CHF;:H1:8,E", + "Edge9;:G;CHF;:H1:7,F;:U4;CHF;:H1:8,E", + "Edge9;:G;CHF;:H1:7,F;:U;CHF;:H1:7,E", + "Face1;:M;CHF;:H1:7,F", + "Face2;:M;CHF;:H1:7,F", + "Face3;:M;CHF;:H1:7,F", + "Face4;:M;CHF;:H1:7,F", + "Face5;:M;CHF;:H1:7,F", + "Face6;:M;CHF;:H1:7,F", + "Vertex1;:G;CHF;:H1:7,F", + "Vertex2;:G;CHF;:H1:7,F", + "Vertex3;:G;CHF;:H1:7,F", + "Vertex4;:G;CHF;:H1:7,F", + "Vertex5;:G;CHF;:H1:7,F", + "Vertex6;:G;CHF;:H1:7,F", + "Vertex7;:G;CHF;:H1:7,F", + "Vertex8;:G;CHF;:H1:7,F", + })); +} + +TEST_F(TopoShapeExpansionTest, makeElementFillet) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape cube1TS {cube1, 1L}; + auto edges = cube1TS.getSubTopoShapes(TopAbs_EDGE); + // Act + cube1TS.makeElementFillet({cube1TS}, edges, .05, .05); + auto elements = elementMap(cube1TS); + // Assert + EXPECT_EQ(cube1TS.countSubElements("Wire"), 26); + EXPECT_FLOAT_EQ(getArea(cube1TS.getShape()), 5.739646); + // Assert that we're creating a correct element map + EXPECT_TRUE(cube1TS.getMappedChildElements().empty()); + EXPECT_TRUE(elementsMatch(cube1TS, + { + "Edge10;:G;FLT;:H1:7,F", + "Edge10;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge10;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge10;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge10;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge11;:G;FLT;:H1:7,F", + "Edge11;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge11;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge11;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge11;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge12;:G;FLT;:H1:7,F", + "Edge12;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge12;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge12;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge12;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge1;:G;FLT;:H1:7,F", + "Edge1;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge1;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge1;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge1;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge1;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge1;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge1;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge1;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge2;:G;FLT;:H1:7,F", + "Edge2;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge2;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge2;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge2;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge2;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge2;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge2;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge3;:G;FLT;:H1:7,F", + "Edge3;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge3;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge3;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge3;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge3;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge3;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge3;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge4;:G;FLT;:H1:7,F", + "Edge4;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge4;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge4;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge4;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge4;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge4;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge5;:G;FLT;:H1:7,F", + "Edge5;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge5;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge5;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge5;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge5;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge5;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge5;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge5;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge6;:G;FLT;:H1:7,F", + "Edge6;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge6;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge6;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge6;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge6;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge6;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge6;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge7;:G;FLT;:H1:7,F", + "Edge7;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge7;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge7;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge7;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge7;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge7;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge7;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge8;:G;FLT;:H1:7,F", + "Edge8;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge8;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U2;FLT;:H1:8,V", + "Edge8;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E;:U;FLT;:H1:7,V", + "Edge8;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge8;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge8;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Edge9;:G;FLT;:H1:7,F", + "Edge9;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Edge9;:G;FLT;:H1:7,F;:U3;FLT;:H1:8,E", + "Edge9;:G;FLT;:H1:7,F;:U4;FLT;:H1:8,E", + "Edge9;:G;FLT;:H1:7,F;:U;FLT;:H1:7,E", + "Face1;:M;FLT;:H1:7,F", + "Face2;:M;FLT;:H1:7,F", + "Face3;:M;FLT;:H1:7,F", + "Face4;:M;FLT;:H1:7,F", + "Face5;:M;FLT;:H1:7,F", + "Face6;:M;FLT;:H1:7,F", + "Vertex1;:G;FLT;:H1:7,F", + "Vertex1;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Vertex2;:G;FLT;:H1:7,F", + "Vertex2;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Vertex3;:G;FLT;:H1:7,F", + "Vertex3;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Vertex4;:G;FLT;:H1:7,F", + "Vertex4;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Vertex5;:G;FLT;:H1:7,F", + "Vertex5;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Vertex6;:G;FLT;:H1:7,F", + "Vertex6;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Vertex7;:G;FLT;:H1:7,F", + "Vertex7;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + "Vertex8;:G;FLT;:H1:7,F", + "Vertex8;:G;FLT;:H1:7,F;:U2;FLT;:H1:8,E", + })); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 75296929d658a36b424e2ad75f254e9e105dfed2 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 20 Feb 2024 10:10:57 -0500 Subject: [PATCH 07/11] Toposhape/Part: Transfer in _makETransform, makETransform, makEGTransform --- src/Mod/Part/App/TopoShape.h | 135 ++++++++++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 84 +++++++++++++++ 2 files changed, 219 insertions(+) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 9b1e1a73bd..1a8f21144b 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -1241,6 +1241,39 @@ public: } + /** Make a new shape with transformation that may contain non-uniform scaling + * + * @param source: input shape + * @param mat: transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copy: whether to perform deep copy of the shape. If false, the + * shape will still be copied if there is scaling. + * + * @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 &makeElementGTransform(const TopoShape &source, const Base::Matrix4D &mat, + const char *op=nullptr, bool copy=false); + + /** Make a new shape with transformation that may contain non-uniform scaling + * + * @param source: input shape + * @param mat: transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copy: whether to perform deep copy of the shape. If false, the + * shape will still be copied if there is scaling. + * + * @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); + } + /** Make a deep copy of the shape * * @param source: input shape @@ -1411,6 +1444,108 @@ public: .makeElementChamfer(*this, edges, radius1, radius2, op, flipDirection, asAngle); } + /** Make a new shape with transformation + * + * @param source: input shape + * @param mat: 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. + * + * @return Returns true if scaling is performed. + * + * 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); + + /** Make a new shape with transformation + * + * @param source: input shape + * @param mat: 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. + * + * @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 &source, const Base::Matrix4D &mat, + const char *op=nullptr, bool checkScale=false, bool copy=false) { + _makeElementTransform(source,mat,op,checkScale,copy); + return *this; + } + + /** Make a new shape with transformation + * + * @param source: input shape + * @param mat: 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. + * + * @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); + } + + /** Make a new shape with transformation + * + * @param source: input shape + * @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. + * + * @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); + + /** Make a new shape with transformation + * + * @param source: input shape + * @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. + * + * @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); + } + /* Make draft shape * * @param source: the source shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 1fc8a6639c..7839d4fb0d 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -84,6 +84,8 @@ #include #include +#include +#include #include "Geometry.h" FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -2506,6 +2508,88 @@ TopoShape& TopoShape::makeElementOrderedWires(const std::vector& shap return makeElementCompound(wires, nullptr, SingleShapeCompoundCreationPolicy::returnShape); } +bool TopoShape::_makeElementTransform(const TopoShape &shape, + const Base::Matrix4D &rclTrf, const char *op, bool checkScale, bool copy) +{ + if(checkScale) { + auto scaleType = rclTrf.hasScale(); + if (scaleType != Base::ScaleType::NoScaling && scaleType != Base::ScaleType::Uniform) { + makeElementGTransform(shape,rclTrf,op,copy); + return true; + } + } + 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) { + // 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(); + } + TopoShape tmp(shape); + if(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 + // 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 + tmp.move(trsf); + + if(op || (shape.Tag && shape.Tag!=Tag)) { + setShape(tmp._Shape); + initCache(); + if (!Hasher) + Hasher = tmp.Hasher; + copyElementMap(tmp, op); + } else + *this = tmp; + return *this; +} + +TopoShape &TopoShape::makeElementGTransform(const TopoShape &shape, + const Base::Matrix4D &rclTrf, const char *op, bool copy) +{ + 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]); + + // geometric transformation + TopoShape tmp(shape); + BRepBuilderAPI_GTransform mkTrf(shape.getShape(), mat, copy); + tmp.setShape(mkTrf.Shape(), false); + if(op || (shape.Tag && shape.Tag!=Tag)) { + setShape(tmp._Shape); + initCache(); + if (!Hasher) + Hasher = tmp.Hasher; + copyElementMap(tmp, op); + } else + *this = tmp; + return *this; +} TopoShape& TopoShape::makeElementCopy(const TopoShape& shape, const char* op, bool copyGeom, bool copyMesh) From 3f81ead31bd64133451ae7afeef13052f50ef4b7 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 21 Feb 2024 23:12:34 -0600 Subject: [PATCH 08/11] Addon Manager: Fix metadata license fetch and display --- src/Mod/AddonManager/AddonManagerOptions.ui | 2 +- src/Mod/AddonManager/NetworkManager.py | 3 +-- src/Mod/AddonManager/package_list.py | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Mod/AddonManager/AddonManagerOptions.ui b/src/Mod/AddonManager/AddonManagerOptions.ui index 8d4ecb45e9..8eb3575c98 100644 --- a/src/Mod/AddonManager/AddonManagerOptions.ui +++ b/src/Mod/AddonManager/AddonManagerOptions.ui @@ -112,7 +112,7 @@ installed addons will be checked for available updates Hide Addons with non-FSF Free/Libre license - true + false HideNonFSFFreeLibre diff --git a/src/Mod/AddonManager/NetworkManager.py b/src/Mod/AddonManager/NetworkManager.py index 89531ff4ee..2970d8a3aa 100644 --- a/src/Mod/AddonManager/NetworkManager.py +++ b/src/Mod/AddonManager/NetworkManager.py @@ -502,7 +502,6 @@ if HAVE_QTNETWORK: current_index = index break - sender.abort() if current_index != -1: self.__launch_request(current_index, self.__create_get_request(url, timeout_ms)) @@ -579,7 +578,7 @@ if HAVE_QTNETWORK: return response_code = reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute) - if response_code == 301: # Permanently moved -- this is a redirect, bail out + if response_code == 301 or response_code == 302: # This is a redirect, bail out return if reply.error() != QtNetwork.QNetworkReply.NetworkError.OperationCanceledError: # It this was not a timeout, make sure we mark the queue task done diff --git a/src/Mod/AddonManager/package_list.py b/src/Mod/AddonManager/package_list.py index 8d8f8a07a8..d7a4a128eb 100644 --- a/src/Mod/AddonManager/package_list.py +++ b/src/Mod/AddonManager/package_list.py @@ -93,9 +93,9 @@ class PackageList(QtWidgets.QWidget): self.set_view_style(style) self.ui.view_bar.view_selector.set_current_view(style) - self.item_filter.setHidePy2(pref.GetBool("HidePy2", True)) - self.item_filter.setHideObsolete(pref.GetBool("HideObsolete", True)) - self.item_filter.setHideNonOSIApproved(pref.GetBool("HideNonOSIApproved", True)) + self.item_filter.setHidePy2(pref.GetBool("HidePy2", False)) + self.item_filter.setHideObsolete(pref.GetBool("HideObsolete", False)) + self.item_filter.setHideNonOSIApproved(pref.GetBool("HideNonOSIApproved", False)) self.item_filter.setHideNonFSFLibre(pref.GetBool("HideNonFSFFreeLibre", False)) self.item_filter.setHideNewerFreeCADRequired( pref.GetBool("HideNewerFreeCADRequired", False) From 48f73f49e8c66f31390ea862b7f04f0838a6fdaa Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Thu, 22 Feb 2024 11:23:14 -0600 Subject: [PATCH 09/11] App: Add PreCompiled.h to Varset cpp file (#12541) * App: Add PreCompiled.h to Varset cpp file --- src/App/VarSet.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/App/VarSet.cpp b/src/App/VarSet.cpp index 9e9fdc9305..a8ea0fe5a3 100644 --- a/src/App/VarSet.cpp +++ b/src/App/VarSet.cpp @@ -20,7 +20,11 @@ * * ****************************************************************************/ +#include "PreCompiled.h" + +#ifndef _PreComp_ #include +#endif #include "VarSet.h" #include "DocumentObject.h" From 968859c542f4040927822a8df59279cba77b48fb Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 22 Feb 2024 22:30:47 +0100 Subject: [PATCH 10/11] FEM: Beam reduced integration (#12513) --- src/Mod/Fem/femmesh/meshtools.py | 17 +++++++++++++++++ src/Mod/Fem/femsolver/calculix/solver.py | 9 +++++++++ .../calculix/write_femelement_geometry.py | 2 +- src/Mod/Fem/femsolver/calculix/write_mesh.py | 8 ++++++++ .../calculix/ccx_cantilever_beam_circle.inp | 2 +- .../data/calculix/ccx_cantilever_beam_pipe.inp | 4 ++-- .../data/calculix/ccx_cantilever_beam_rect.inp | 2 +- .../data/calculix/ccx_cantilever_ele_seg2.inp | 2 +- .../data/calculix/ccx_cantilever_ele_seg3.inp | 2 +- 9 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/Mod/Fem/femmesh/meshtools.py b/src/Mod/Fem/femmesh/meshtools.py index d1134f9dc1..d8ff9133f7 100644 --- a/src/Mod/Fem/femmesh/meshtools.py +++ b/src/Mod/Fem/femmesh/meshtools.py @@ -2508,4 +2508,21 @@ def compact_mesh( # may be return another value if the mesh was compacted, just check last map entries return (new_mesh, node_map, elem_map) +# ************************************************************************************************ +def beam_reduced_integration( + fileName +): + # replace B3x elements with B3xR elements + f = open(fileName, "r+") + lines = f.readlines() + f.seek(0) + for line in lines: + if line.find("B32") != -1: + line = line.replace("B32", "B32R") + if line.find("B31") != -1: + line = line.replace("B31", "B31R") + f.write(line) + + f.truncate() + f.close() ## @} diff --git a/src/Mod/Fem/femsolver/calculix/solver.py b/src/Mod/Fem/femsolver/calculix/solver.py index ca6707a893..eceeeac691 100644 --- a/src/Mod/Fem/femsolver/calculix/solver.py +++ b/src/Mod/Fem/femsolver/calculix/solver.py @@ -372,6 +372,15 @@ def add_attributes(obj, ccx_prefs): obj.BeamShellResultOutput3D = dimout + if not hasattr(obj, "BeamReducedIntegration"): + obj.addProperty( + "App::PropertyBool", + "BeamReducedIntegration", + "Fem", + "Set to True to use beam elements with reduced integration" + ) + obj.BeamReducedIntegration = True + """ Should there be some equation object for Calculix too? diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py index 9ca09fc318..19489c7d00 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py @@ -77,7 +77,7 @@ def write_femelement_geometry(f, ccxwriter): thickness = beamsec_obj.PipeThickness.getValueAs("mm").Value section_type = ", SECTION=PIPE" section_geo = "{:.13G},{:.13G}\n".format(radius, thickness) - section_def = "*BEAM GENERAL SECTION, {}{}{}\n".format( + section_def = "*BEAM SECTION, {}{}{}\n".format( elsetdef, material, section_type diff --git a/src/Mod/Fem/femsolver/calculix/write_mesh.py b/src/Mod/Fem/femsolver/calculix/write_mesh.py index 380a5628b0..acab0ce1c1 100644 --- a/src/Mod/Fem/femsolver/calculix/write_mesh.py +++ b/src/Mod/Fem/femsolver/calculix/write_mesh.py @@ -51,6 +51,10 @@ def write_mesh(ccxwriter): if ccxwriter.member.geos_fluidsection: meshtools.write_D_network_element_to_inputfile(ccxwriter.femmesh_file) + # Use reduced integration beam elements if this option is enabled in ccx solver settings + if ccxwriter.solver_obj.BeamReducedIntegration: + meshtools.beam_reduced_integration(ccxwriter.femmesh_file) + inpfile = codecs.open(ccxwriter.file_name, "w", encoding="utf-8") inpfile.write("{}\n".format(59 * "*")) inpfile.write("** {}\n".format(write_name)) @@ -69,6 +73,10 @@ def write_mesh(ccxwriter): # inpfile is closed meshtools.write_D_network_element_to_inputfile(ccxwriter.femmesh_file) + # Use reduced integration beam elements if this option is enabled in ccx solver settings + if ccxwriter.solver_obj.BeamReducedIntegration: + meshtools.beam_reduced_integration(ccxwriter.femmesh_file) + # reopen file with "append" to add all the rest inpfile = codecs.open(ccxwriter.femmesh_file, "a", encoding="utf-8") inpfile.write("\n\n") diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp index 99de648003..28c6659540 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp index 1fb6fb1330..b3580ae848 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 @@ -56,7 +56,7 @@ Eedges *********************************************************** ** Sections -*BEAM GENERAL SECTION, ELSET=M0B0RstdD0, MATERIAL=MechanicalMaterial, SECTION=PIPE +*BEAM SECTION, ELSET=M0B0RstdD0, MATERIAL=MechanicalMaterial, SECTION=PIPE 500,100 -0, 1, 0 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp index 49ca31c216..466bbbc895 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp index 30c3cd3896..18f22dfd05 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp @@ -61,7 +61,7 @@ ** Edge elements -*Element, TYPE=B31, ELSET=Eedges +*Element, TYPE=B31R, ELSET=Eedges 1, 1, 3 2, 3, 4 3, 4, 5 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp index 104ce9d205..7c7d536051 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 From a3e97b78392b98c534e5bf1f9c3735afa52667d6 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Tue, 20 Feb 2024 11:40:24 -0500 Subject: [PATCH 11/11] Toponaming/Part: Clean and add tests for elementTransform methods --- src/Mod/Part/App/TopoShape.h | 95 +++++++++------- src/Mod/Part/App/TopoShapeExpansion.cpp | 102 +++++++++++------- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 96 ++++++++++++++++- .../Part/App/TopoShapeMakeElementRefine.cpp | 2 +- 4 files changed, 215 insertions(+), 80 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 1a8f21144b..4cc128c696 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -214,6 +214,18 @@ enum class AsAngle yes }; +enum class CheckScale +{ + noScaleCheck, + checkScale +}; + +enum class Copy +{ + noCopy, + copy +}; + /** The representation for a CAD Shape */ // NOLINTNEXTLINE cppcoreguidelines-special-member-functions @@ -802,7 +814,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 @@ -818,13 +830,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 &faces, + TopoShape &makeElementThickSolid(const TopoShape &shape, const std::vector &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 @@ -1240,7 +1251,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 @@ -1255,8 +1265,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 * @@ -1270,8 +1282,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 @@ -1452,8 +1467,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. @@ -1461,8 +1476,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 * @@ -1472,8 +1490,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 @@ -1481,9 +1499,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; } @@ -1495,16 +1517,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) const + { + return TopoShape(Tag, Hasher).makeElementTransform(*this, mat, op, checkScale, copy); } /** Make a new shape with transformation @@ -1513,19 +1538,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 * @@ -1533,17 +1556,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 @@ -1784,7 +1805,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. */ diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 7839d4fb0d..08daf6132a 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -48,9 +48,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -83,10 +85,6 @@ #include "Geometry.h" #include -#include -#include -#include -#include "Geometry.h" FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -2508,86 +2506,108 @@ TopoShape& TopoShape::makeElementOrderedWires(const std::vector& 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& mat, + const char* op, + CheckScale checkScale, + Copy copy) { - if(checkScale) { - auto scaleType = rclTrf.hasScale(); + if (checkScale == CheckScale::checkScale) { + auto scaleType = mat.hasScale(); if (scaleType != Base::ScaleType::NoScaling && scaleType != Base::ScaleType::Uniform) { - makeElementGTransform(shape,rclTrf,op,copy); + makeElementGTransform(shape, mat, op, copy); return true; } } - makeElementTransform(shape,convert(rclTrf),op,copy); + makeElementTransform(shape, convert(mat), 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(); + // No scaling is 1 as in 1:1 + const bool scaling = Abs(Abs(trsf.ScaleFactor()) - 1) > Precision::Confusion(); + const bool negative_scaling = + trsf.ScaleFactor() * trsf.HVectorialPart().Determinant() < 0.0; + copy = negative_scaling || scaling ? 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& mat, + 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]); + gp_GTrsf matrix; + matrix.SetValue(1, 1, mat[0][0]); + matrix.SetValue(2, 1, mat[1][0]); + matrix.SetValue(3, 1, mat[2][0]); + matrix.SetValue(1, 2, mat[0][1]); + matrix.SetValue(2, 2, mat[1][1]); + matrix.SetValue(3, 2, mat[2][1]); + matrix.SetValue(1, 3, mat[0][2]); + matrix.SetValue(2, 3, mat[1][2]); + matrix.SetValue(3, 3, mat[2][2]); + matrix.SetValue(1, 4, mat[0][3]); + matrix.SetValue(2, 4, mat[1][3]); + matrix.SetValue(3, 4, mat[2][3]); // geometric transformation TopoShape tmp(shape); - BRepBuilderAPI_GTransform mkTrf(shape.getShape(), mat, copy); + BRepBuilderAPI_GTransform mkTrf(shape.getShape(), matrix, 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; } diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 16f9578827..c122481cdd 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1523,7 +1523,6 @@ TEST_F(TopoShapeExpansionTest, makeElementThickSolid) EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge11;THK;:H1:4,E")); } - TEST_F(TopoShapeExpansionTest, makeElementGeneralFuse) { // Arrange @@ -1845,4 +1844,99 @@ TEST_F(TopoShapeExpansionTest, makeElementFillet) })); } +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) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp b/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp index e823ace748..84afef491b 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp @@ -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 }