From 3df8ae2fabca222bccec23c454fe1f34958fc720 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Mon, 12 Feb 2024 15:29:15 -0600 Subject: [PATCH 01/19] Revert "Resolve #11825 - no automatic quantity conversion: `App::anyToQuantity`" This reverts commit 6a54591bac6c8843f12c6971623437083d017ab3. --- src/App/Expression.cpp | 28 ----------------------- src/App/Expression.h | 1 - tests/src/App/Expression.cpp | 43 ------------------------------------ 3 files changed, 72 deletions(-) diff --git a/src/App/Expression.cpp b/src/App/Expression.cpp index f62ff8c8143f..75ac7e2e3763 100644 --- a/src/App/Expression.cpp +++ b/src/App/Expression.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include @@ -518,29 +517,6 @@ Py::Object pyFromQuantity(const Quantity &quantity) { } } -static const std::regex REGEX_QUANTITY( - R"(\s*)" - R"(([-+]?[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?))" // value - R"(\s*)" - R"(([^\s]+)?)" // unit - R"(\s*)" -); - -// https://github.com/FreeCAD/FreeCAD/issues/11825 -Quantity parseQuantityFromText(std::string text) { - std::smatch match; - if (std::regex_match(text, match, REGEX_QUANTITY)) { - std::string value_text = match[1]; - std::string unit_text = match[3]; - double value = std::stod(value_text); - Unit unit = Unit(QString::fromStdString(unit_text)); - return Quantity(value, unit); - } else { - std::string error_message = "Failed to parse to Quantity: text='" + text + "'"; - PARSER_THROW(error_message); - } -} - Quantity anyToQuantity(const App::any &value, const char *msg) { if (is_type(value,typeid(Quantity))) { return cast(value); @@ -554,10 +530,6 @@ Quantity anyToQuantity(const App::any &value, const char *msg) { return Quantity(cast(value)); } else if (is_type(value,typeid(double))) { return Quantity(cast(value)); - } else if (is_type(value,typeid(const char*))) { - return parseQuantityFromText(std::string(cast(value))); - } else if (is_type(value,typeid(std::string))) { - return parseQuantityFromText(cast(value)); } if(!msg) msg = "Failed to convert to Quantity"; diff --git a/src/App/Expression.h b/src/App/Expression.h index 55dc530aef27..78f1edb1c58c 100644 --- a/src/App/Expression.h +++ b/src/App/Expression.h @@ -49,7 +49,6 @@ using ExpressionPtr = std::unique_ptr; AppExport bool isAnyEqual(const App::any &v1, const App::any &v2); AppExport Base::Quantity anyToQuantity(const App::any &value, const char *errmsg = nullptr); -AppExport Base::Quantity parseQuantityFromText(std::string text); // Map of depending objects to a map of depending property name to the full referencing object identifier using ExpressionDeps = std::map > >; diff --git a/tests/src/App/Expression.cpp b/tests/src/App/Expression.cpp index 8ac0737b693c..8a7c1a50fb12 100644 --- a/tests/src/App/Expression.cpp +++ b/tests/src/App/Expression.cpp @@ -49,47 +49,4 @@ TEST(Expression, test_e_rad) EXPECT_EQ(op->toString(), "e rad"); op.release(); } - -TEST(Expression, parseQuantityFromText) -{ - EXPECT_ANY_THROW(App::parseQuantityFromText("")) << "should not parse empty"; - EXPECT_ANY_THROW(App::parseQuantityFromText("mm")) << "should not parse missing value"; - EXPECT_NO_THROW(App::parseQuantityFromText("2")) << "ok to parse missing unit"; - EXPECT_NO_THROW(App::parseQuantityFromText("2mm")); - EXPECT_NO_THROW(App::parseQuantityFromText("2 mm")); - EXPECT_NO_THROW(App::parseQuantityFromText("\t \n .5e-3kg/m^3 \t")); - EXPECT_NO_THROW(App::parseQuantityFromText("\n \t -6.7E3 \t A/m^2 \t")); - EXPECT_EQ(App::parseQuantityFromText("2mm"), Base::Quantity(2.0, QString::fromStdString("mm"))); // exact ULP form - EXPECT_EQ(App::parseQuantityFromText("2 mm"), Base::Quantity(2.0, QString::fromStdString("mm"))); // exact ULP form - auto quant_one = App::parseQuantityFromText("\t \n.5e-3kg/m^3 \t"); - EXPECT_DOUBLE_EQ(quant_one.getValue(), 0.5e-3); // approximately equal, to within 4 ULPs - EXPECT_EQ(quant_one.getUnit(), Base::Unit(QString::fromStdString("kg/m^3"))); - auto quant_two = App::parseQuantityFromText("\n \t -6.7E3 \t A/m^2 \t"); - EXPECT_DOUBLE_EQ(quant_two.getValue(), -6.7e+3); // approximately equal, to within 4 ULPs - EXPECT_EQ(quant_two.getUnit(), Base::Unit(QString::fromStdString("A/m^2"))); -} - -TEST(Expression, anyToQuantity) -{ - EXPECT_EQ(App::anyToQuantity(Base::Quantity()), Base::Quantity()); - EXPECT_EQ(App::anyToQuantity(true), Base::Quantity(1.0)); - EXPECT_EQ(App::anyToQuantity(false), Base::Quantity(0.0)); - EXPECT_EQ(App::anyToQuantity(123), Base::Quantity(123.0)); - EXPECT_EQ(App::anyToQuantity(123L), Base::Quantity(123.0)); - EXPECT_EQ(App::anyToQuantity(123.0F), Base::Quantity(123.0)); - EXPECT_EQ(App::anyToQuantity(123.0), Base::Quantity(123.0)); - EXPECT_EQ(App::anyToQuantity("123"), Base::Quantity(123.0)); - EXPECT_EQ(App::anyToQuantity(std::string("123")), Base::Quantity(123.0)); - EXPECT_EQ(App::anyToQuantity("123 mm"), Base::Quantity(123.0, QString::fromStdString("mm"))); - EXPECT_EQ(App::anyToQuantity(std::string("123 mm")), Base::Quantity(123.0, QString::fromStdString("mm"))); - EXPECT_ANY_THROW(App::anyToQuantity("")); - EXPECT_ANY_THROW(App::anyToQuantity("mm")); -} - -TEST(Expression, isAnyEqual) -{ - EXPECT_TRUE(App::isAnyEqual("123 mm", "123 mm")); - EXPECT_TRUE(App::isAnyEqual("123 mm", Base::Quantity(123.0, QString::fromStdString("mm")))); - EXPECT_TRUE(App::isAnyEqual(Base::Quantity(123.0, QString::fromStdString("mm")), "123 mm")); -} // clang-format on From c5ba5194703a955d774f5b785c1cff6e9ff5a380 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 13 Feb 2024 13:41:56 -0500 Subject: [PATCH 02/19] Toponaming/Part: move in makeElementDraft --- src/Mod/Part/App/TopoShape.h | 37 +++++++++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 39 +++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 0aaa5d9fe717..e3673bbdb54c 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -965,6 +965,43 @@ class PartExport TopoShape: public Data::ComplexGeoData { return TopoShape(0, Hasher).makeElementBoolean(maker, *this, op, tol); } + /* Make draft shape + * + * @param source: the source shape + * @param faces: the faces of the source shape to make draft faces + * @param pullDirection: the pulling direction for making the draft + * @param angle: the angle of the draft + * @param neutralPlane: the neutral plane used as a reference to decide pulling direction + * @param retry: whether to keep going by skipping faces that failed to create draft + * @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 &makeElementDraft(const TopoShape &source, const std::vector &faces, + const gp_Dir &pullDirection, double angle, const gp_Pln &neutralPlane, + bool retry=true, const char *op=nullptr); + /* Make draft shape + * + * @param source: the source shape + * @param faces: the faces of the source shape to make draft faces + * @param pullDirection: the pulling direction for making the draft + * @param angle: the angle of the draft + * @param neutralPlane: the neutral plane used as a reference to decide pulling direction + * @param retry: whether to keep going by skipping faces that failed to create draft + * @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 makeElementDraft(const std::vector &faces, + const gp_Dir &pullDirection, double angle, const gp_Pln &neutralPlane, + bool retry=true, const char *op=nullptr) const { + return TopoShape(0,Hasher).makeElementDraft(*this,faces,pullDirection,angle,neutralPlane,retry,op); + } /* Make a shell using this shape * @param silent: whether to throw exception on failure diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 3194b0b76f03..fca36f0c6cbb 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1811,6 +1811,45 @@ TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op); } +TopoShape &TopoShape::makeElementDraft(const TopoShape &shape, const std::vector &_faces, + const gp_Dir &pullDirection, double angle, const gp_Pln &neutralPlane, + bool retry, const char *op) +{ + if(!op) op = Part::OpCodes::Draft; + + if(shape.isNull()) + HANDLE_NULL_SHAPE; + + std::vector faces(_faces); + bool done = true; + BRepOffsetAPI_DraftAngle mkDraft; + do { + if(faces.empty()) + FC_THROWM(Base::CADKernelError,"no faces can be used"); + + mkDraft.Init(shape.getShape()); + done = true; + for(auto it=faces.begin();it!=faces.end();++it) { + // TODO: What is the flag for? + mkDraft.Add(TopoDS::Face(it->getShape()), pullDirection, angle, neutralPlane); + if (!mkDraft.AddDone()) { + // Note: the function ProblematicShape returns the face on which the error occurred + // Note: mkDraft.Remove() stumbles on a bug in Draft_Modification::Remove() and is + // therefore unusable. See http://forum.freecadweb.org/viewtopic.php?f=10&t=3209&start=10#p25341 + // The only solution is to discard mkDraft and start over without the current face + // mkDraft.Remove(face); + FC_ERR("Failed to add some face for drafting, skip"); + done = false; + faces.erase(it); + break; + } + } + }while(retry && !done); + + mkDraft.Build(); + return makeElementShape(mkDraft,shape,op); +} + TopoShape& TopoShape::makeElementFace(const TopoShape& shape, const char* op, const char* maker, From 6e9cd051f2cc7f17e634795ca46d682ed6672ed0 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Wed, 14 Feb 2024 12:20:10 -0500 Subject: [PATCH 03/19] Toponaming/Part: Cleanup and test makeElementDraft --- src/Mod/Part/App/TopoShapeExpansion.cpp | 5 +- tests/src/Mod/Part/App/PartTestHelpers.cpp | 23 ++++++++ tests/src/Mod/Part/App/PartTestHelpers.h | 1 + tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 55 ++++++++++++++++++- 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index fca36f0c6cbb..adf55cf732da 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include #include @@ -68,7 +70,6 @@ #include "FaceMaker.h" #include -#include FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -1818,7 +1819,7 @@ TopoShape &TopoShape::makeElementDraft(const TopoShape &shape, const std::vector if(!op) op = Part::OpCodes::Draft; if(shape.isNull()) - HANDLE_NULL_SHAPE; + FC_THROWM(NullShapeException, "Null shape"); std::vector faces(_faces); bool done = true; diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index 36dc63f0678a..feb73e8d1088 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include +#include #include "PartTestHelpers.h" @@ -155,6 +156,28 @@ std::pair CreateTwoCubes() return {box1, box2}; } +std::pair CreateTwoTopoShapeCubes() +{ + auto [box1, box2] = CreateTwoCubes(); + std::vector vec; + long tag = 1L; + for (TopExp_Explorer exp(box1, TopAbs_FACE); exp.More(); exp.Next()) { + vec.emplace_back(TopoShape(exp.Current(), tag++)); + } + TopoShape box1ts; + box1ts.makeElementCompound(vec); + box1ts.Tag = tag++; + vec.clear(); + for (TopExp_Explorer exp(box2, TopAbs_FACE); exp.More(); exp.Next()) { + vec.emplace_back(TopoShape(exp.Current(), tag++)); + } + TopoShape box2ts; + box2ts.Tag = tag++; + box2ts.makeElementCompound(vec); + + return {box1ts, box2ts}; +} + } // namespace PartTestHelpers // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) diff --git a/tests/src/Mod/Part/App/PartTestHelpers.h b/tests/src/Mod/Part/App/PartTestHelpers.h index 8b829e4ba6d1..a10e1f9e36a9 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.h +++ b/tests/src/Mod/Part/App/PartTestHelpers.h @@ -59,4 +59,5 @@ std::map elementMap(const TopoShape& shape); std::pair CreateTwoCubes(); +std::pair CreateTwoTopoShapeCubes(); } // namespace PartTestHelpers diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index b047f62eb006..7b6458e603ff 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include @@ -867,4 +867,57 @@ TEST_F(TopoShapeExpansionTest, makeElementBooleanFuse) EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1.75); } +TEST_F(TopoShapeExpansionTest, makeElementDraft) +{ // Draft as in Draft Angle or sloped sides for removing shapes from a mold. + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape cube1TS {cube1, 1L}; + std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); + std::vector faces {subShapes[0], subShapes[1], subShapes[2], subShapes[3]}; + const gp_Dir pullDirection {0, 0, 1}; + double angle {M_PI * 10 + / 8}; // Angle should be between Pi and Pi * 1.5 ( 180 and 270 degrees ) + const gp_Pln plane {}; + // Act + TopoShape& result = cube1TS.makeElementDraft(cube1TS, faces, pullDirection, angle, plane); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(elements.size(), 26); // Cubes have 6 Faces, 12 Edges, 8 Vertexes + EXPECT_NEAR(getVolume(result.getShape()), 4.3333333333, 1e-06); // Truncated pyramid +} + +TEST_F(TopoShapeExpansionTest, makeElementDraftTopoShapes) +{ + // Arrange + auto [cube1TS, cube2TS] = CreateTwoTopoShapeCubes(); + const gp_Dir pullDirection {0, 0, 1}; + double angle {M_PI * 10 + / 8}; // Angle should be between Pi and Pi * 1.5 ( 180 and 270 degrees ) + const gp_Pln plane {}; + // Act + TopoShape result3 = cube1TS.makeElementDraft(cube1TS.getSubTopoShapes(TopAbs_FACE), + pullDirection, + angle, + plane); // Non Reference call type + TopoShape result2 = cube1TS.makeElementDraft(cube1TS, + cube1TS.getSubTopoShapes(TopAbs_FACE), + pullDirection, + angle, + plane); // Bad use of Reference call + TopoShape& result = cube1TS.makeElementDraft(cube2TS, + cube2TS.getSubTopoShapes(TopAbs_FACE), + pullDirection, + angle, + plane); // Correct usage + auto elements = elementMap((result)); + // Assert + 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_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. +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 66e29cbd79f618b352ee9067949048332b523a51 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 14 Feb 2024 18:26:30 -0600 Subject: [PATCH 04/19] Assembly: Add ${COIN3D_INCLUDE_DIRS} to CMakeLists.txt (#12407) * Assembly: Add ${COIN3D_INCLUDE_DIRS} to CMakeLists.txt --- src/Mod/Assembly/Gui/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Assembly/Gui/CMakeLists.txt b/src/Mod/Assembly/Gui/CMakeLists.txt index 6dcc4acb2532..a7dc9c9f99a7 100644 --- a/src/Mod/Assembly/Gui/CMakeLists.txt +++ b/src/Mod/Assembly/Gui/CMakeLists.txt @@ -1,6 +1,7 @@ include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} + ${COIN3D_INCLUDE_DIRS} ${OCC_INCLUDE_DIR} ) From b075f7d5d69e9c7630d8194429499f36ada70a95 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 14 Feb 2024 18:56:38 -0600 Subject: [PATCH 05/19] Part/Toponaming: Apply clang-format to TopoShapeExpansion --- src/Mod/Part/App/TopoShapeExpansion.cpp | 91 +++++++++++++++---------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index adf55cf732da..5ba877d180ae 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -59,7 +59,7 @@ #endif #if OCC_VERSION_HEX >= 0x070500 -# include +#include #endif #include "modelRefine.h" @@ -601,12 +601,11 @@ struct NameKey long tag = 0; int shapetype = 0; - NameKey() - = default; - explicit NameKey(Data::MappedName n) + NameKey() = default; + explicit NameKey(Data::MappedName n) : name(std::move(n)) {} - NameKey(int type, Data::MappedName n) + NameKey(int type, Data::MappedName n) : name(std::move(n)) { // Order the shape type from vertex < edge < face < other. We'll rely @@ -796,9 +795,9 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, // First, collect names from other shapes that generates or modifies the // new shape - for (auto& pinfo : infos) { // Walk Vertexes, then Edges, then Faces + for (auto& pinfo : infos) { // Walk Vertexes, then Edges, then Faces auto& info = *pinfo; - for (const auto & incomingShape : shapes) { + for (const auto& incomingShape : shapes) { if (!canMapElement(incomingShape)) { continue; } @@ -811,7 +810,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, const auto& otherElement = otherMap.find(incomingShape._Shape, i); // Find all new objects that are a modification of the old object Data::ElementIDRefs sids; - NameKey key(info.type, + NameKey key( + info.type, incomingShape.getMappedName(Data::IndexedName::fromConst(info.shapetype, i), true, &sids)); @@ -851,7 +851,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, continue; } - Data::IndexedName element = Data::IndexedName::fromConst(newInfo.shapetype, newShapeIndex); + Data::IndexedName element = + Data::IndexedName::fromConst(newInfo.shapetype, newShapeIndex); if (getMappedName(element)) { continue; } @@ -1163,11 +1164,13 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, TopTools_IndexedMapOfShape submap; TopExp::MapShapes(info.find(elementCounter), next.type, submap); - for (int submapIndex = 1, infoCounter = 1; submapIndex <= submap.Extent(); ++submapIndex) { + for (int submapIndex = 1, infoCounter = 1; submapIndex <= submap.Extent(); + ++submapIndex) { ss.str(""); int elementIndex = next.find(submap(submapIndex)); assert(elementIndex); - Data::IndexedName indexedName = Data::IndexedName::fromConst(next.shapetype, elementIndex); + Data::IndexedName indexedName = + Data::IndexedName::fromConst(next.shapetype, elementIndex); if (getMappedName(indexedName)) { continue; } @@ -1201,7 +1204,7 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, bool hasUnnamed = false; for (size_t ifo = 1; ifo < infos.size(); ++ifo) { auto& info = *infos.at(ifo); - auto& prev = *infos.at(ifo-1); + auto& prev = *infos.at(ifo - 1); for (int i = 1; i <= info.count(); ++i) { Data::IndexedName element = Data::IndexedName::fromConst(info.shapetype, i); if (getMappedName(element)) { @@ -1220,7 +1223,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, for (; xp.More(); xp.Next()) { int previousElementIndex = prev.find(xp.Current()); assert(previousElementIndex); - Data::IndexedName prevElement = Data::IndexedName::fromConst(prev.shapetype, previousElementIndex); + Data::IndexedName prevElement = + Data::IndexedName::fromConst(prev.shapetype, previousElementIndex); if (!delayed && (newNames.count(prevElement) != 0U)) { names.clear(); break; @@ -1723,7 +1727,7 @@ struct MapperThruSections: MapperMaker const std::vector& generated(const TopoDS_Shape& s) const override { MapperMaker::generated(s); - if ( ! _res.empty()) { + if (!_res.empty()) { return _res; } try { @@ -1812,32 +1816,41 @@ TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op); } -TopoShape &TopoShape::makeElementDraft(const TopoShape &shape, const std::vector &_faces, - const gp_Dir &pullDirection, double angle, const gp_Pln &neutralPlane, - bool retry, const char *op) +TopoShape& TopoShape::makeElementDraft(const TopoShape& shape, + const std::vector& _faces, + const gp_Dir& pullDirection, + double angle, + const gp_Pln& neutralPlane, + bool retry, + const char* op) { - if(!op) op = Part::OpCodes::Draft; + if (!op) { + op = Part::OpCodes::Draft; + } - if(shape.isNull()) + if (shape.isNull()) { FC_THROWM(NullShapeException, "Null shape"); + } std::vector faces(_faces); bool done = true; BRepOffsetAPI_DraftAngle mkDraft; do { - if(faces.empty()) - FC_THROWM(Base::CADKernelError,"no faces can be used"); + if (faces.empty()) { + FC_THROWM(Base::CADKernelError, "no faces can be used"); + } mkDraft.Init(shape.getShape()); done = true; - for(auto it=faces.begin();it!=faces.end();++it) { + for (auto it = faces.begin(); it != faces.end(); ++it) { // TODO: What is the flag for? mkDraft.Add(TopoDS::Face(it->getShape()), pullDirection, angle, neutralPlane); if (!mkDraft.AddDone()) { // Note: the function ProblematicShape returns the face on which the error occurred // Note: mkDraft.Remove() stumbles on a bug in Draft_Modification::Remove() and is - // therefore unusable. See http://forum.freecadweb.org/viewtopic.php?f=10&t=3209&start=10#p25341 - // The only solution is to discard mkDraft and start over without the current face + // therefore unusable. See + // http://forum.freecadweb.org/viewtopic.php?f=10&t=3209&start=10#p25341 The + // only solution is to discard mkDraft and start over without the current face // mkDraft.Remove(face); FC_ERR("Failed to add some face for drafting, skip"); done = false; @@ -1845,10 +1858,10 @@ TopoShape &TopoShape::makeElementDraft(const TopoShape &shape, const std::vector break; } } - }while(retry && !done); + } while (retry && !done); mkDraft.Build(); - return makeElementShape(mkDraft,shape,op); + return makeElementShape(mkDraft, shape, op); } TopoShape& TopoShape::makeElementFace(const TopoShape& shape, @@ -1927,18 +1940,20 @@ TopoShape& TopoShape::makeElementFace(const std::vector& shapes, return *this; } -class MyRefineMaker : public BRepBuilderAPI_RefineModel +class MyRefineMaker: public BRepBuilderAPI_RefineModel { public: - explicit MyRefineMaker(const TopoDS_Shape &s) - :BRepBuilderAPI_RefineModel(s) + explicit MyRefineMaker(const TopoDS_Shape& s) + : BRepBuilderAPI_RefineModel(s) {} - void populate(ShapeMapper &mapper) + void populate(ShapeMapper& mapper) { - for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape it(this->myModified); it.More(); it.Next()) - { - if (it.Key().IsNull()) continue; + for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape it(this->myModified); it.More(); + it.Next()) { + if (it.Key().IsNull()) { + continue; + } mapper.populate(MappingStatus::Generated, it.Key(), it.Value()); } } @@ -2429,8 +2444,10 @@ bool TopoShape::fixSolidOrientation() return false; } -TopoShape& -TopoShape::makeElementBoolean(const char* maker, const TopoShape& shape, const char* op, double tolerance) +TopoShape& TopoShape::makeElementBoolean(const char* maker, + const TopoShape& shape, + const char* op, + double tolerance) { return makeElementBoolean(maker, std::vector(1, shape), op, tolerance); } @@ -2613,8 +2630,8 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker, #if OCC_VERSION_HEX >= 0x070500 // -1/22/2024 Removing the parameter. // if (PartParams::getParallelRunThreshold() > 0) { - mk->SetRunParallel(Standard_True); - OSD_Parallel::SetUseOcctThreads(Standard_True); + mk->SetRunParallel(Standard_True); + OSD_Parallel::SetUseOcctThreads(Standard_True); // } #else // 01/22/2024 This will be an extremely rare case, since we don't From db377c1854eabdc9f6a066e0f62be9835b14f201 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 15 Feb 2024 07:54:22 -0500 Subject: [PATCH 06/19] Toponaming/Part: transfer in searchSubShape --- src/Mod/Part/App/TopoShape.h | 11 +- src/Mod/Part/App/TopoShapeExpansion.cpp | 243 ++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 5 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 2a0a730621a6..ebb6f581bb89 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -707,6 +707,7 @@ class PartExport TopoShape: public Data::ComplexGeoData * * unlike findShape(), the input shape does not have to be an actual * sub-shape of this shape. The sub-shape is searched by shape geometry + * Note that subshape must be a Vertex, Edge, or Face. * * @param subshape: a sub shape to search * @param names: optional output of found sub shape indexed based name @@ -714,11 +715,11 @@ class PartExport TopoShape: public Data::ComplexGeoData * @param tol: tolerance to check coincident vertices * @param atol: tolerance to check for same angles */ - // TODO: Implement this method and its tests later in Toponaming Phase 3. - // std::vector searchSubShape(const TopoShape &subshape, - // std::vector *names=nullptr, - // bool checkGeometry=true, - // double tol=1e-7, double atol=1e-12) const; + // TODO: Refactor this method and its tests later in Toponaming Phase 3. + std::vector searchSubShape(const TopoShape &subshape, + std::vector *names=nullptr, + bool checkGeometry=true, + double tol=1e-7, double atol=1e-12) const; //@} void copyElementMap(const TopoShape & topoShape, const char *op=nullptr); diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 3acd96d0deb3..de0b1b045441 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -66,6 +66,7 @@ #include "TopoShapeCache.h" #include "TopoShapeMapper.h" #include "FaceMaker.h" +#include "Geometry.h" #include #include @@ -220,6 +221,248 @@ TopoDS_Shape TopoShape::findShape(TopAbs_ShapeEnum type, int idx) const return _cache->findShape(_Shape, type, idx); } +std::vector TopoShape::searchSubShape(const TopoShape& subshape, + std::vector* names, + bool checkGeometry, + double tol, + double atol) const +{ + std::vector res; + if (subshape.isNull() || this->isNull()) { + return res; + } + double tol2 = tol * tol; + int i = 0; + TopAbs_ShapeEnum shapeType = subshape.shapeType(); + switch (shapeType) { + case TopAbs_VERTEX: + // Vertex search will do comparison with tolerance to account for + // rounding error inccured through transformation. + for (auto& s : getSubTopoShapes(TopAbs_VERTEX)) { + ++i; + if (BRep_Tool::Pnt(TopoDS::Vertex(s.getShape())) + .SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(subshape.getShape()))) + <= tol2) { + if (names) { + names->push_back(std::string("Vertex") + std::to_string(i)); + } + res.push_back(s); + } + } + break; + case TopAbs_EDGE: + case TopAbs_FACE: { + std::unique_ptr g; + bool isLine = false; + bool isPlane = false; + + std::vector vertices; + TopoShape wire; + if (shapeType == TopAbs_FACE) { + wire = subshape.splitWires(); + vertices = wire.getSubShapes(TopAbs_VERTEX); + } + else { + vertices = subshape.getSubShapes(TopAbs_VERTEX); + } + + if (vertices.empty() || checkGeometry) { + g = Geometry::fromShape(subshape.getShape()); + if (!g) { + return res; + } + if (shapeType == TopAbs_EDGE) { + isLine = (g->isDerivedFrom(GeomLine::getClassTypeId()) + || g->isDerivedFrom(GeomLineSegment::getClassTypeId())); + } + else { + isPlane = g->isDerivedFrom(GeomPlane::getClassTypeId()); + } + } + + auto compareGeometry = [&](const TopoShape& s, bool strict) { + std::unique_ptr g2(Geometry::fromShape(s.getShape())); + if (!g2) { + return false; + } + if (isLine && !strict) { + // For lines, don't compare geometry, just check the + // vertices below instead, because the exact same edge + // may have different geometrical representation. + if (!g2->isDerivedFrom(GeomLine::getClassTypeId()) + && !g2->isDerivedFrom(GeomLineSegment::getClassTypeId())) { + return false; + } + } + else if (isPlane && !strict) { + // For planes, don't compare geometry either, so that + // we don't need to worry about orientation and so on. + // Just check the edges. + if (!g2->isDerivedFrom(GeomPlane::getClassTypeId())) { + return false; + } + } + else if (!g2 || !g2->isSame(*g, tol, atol)) { + return false; + } + return true; + }; + + if (vertices.empty()) { + // Probably an infinite shape, so we have to search by geometry + int idx = 0; + for (auto& s : getSubTopoShapes(shapeType)) { + ++idx; + if (!s.countSubShapes(TopAbs_VERTEX) && compareGeometry(s, true)) { + if (names) { + names->push_back(shapeName(shapeType) + std::to_string(idx)); + } + res.push_back(s); + } + } + break; + } + + // The basic idea of shape search is about the same for both edge and face. + // * Search the first vertex, which is done with tolerance. + // * Find the ancestor shape of the found vertex + // * Compare each vertex of the ancestor shape and the input shape + // * Perform geometry comparison of the ancestor and input shape. + // * For face, perform addition geometry comparison of each edges. + std::unordered_set shapeSet; + for (auto& v : searchSubShape(vertices[0], nullptr, checkGeometry, tol, atol)) { + for (auto idx : findAncestors(v.getShape(), shapeType)) { + auto s = getSubTopoShape(shapeType, idx); + if (!shapeSet.insert(s).second) { + continue; + } + TopoShape otherWire; + std::vector otherVertices; + if (shapeType == TopAbs_FACE) { + otherWire = s.splitWires(); + if (wire.countSubShapes(TopAbs_EDGE) + != otherWire.countSubShapes(TopAbs_EDGE)) { + continue; + } + otherVertices = otherWire.getSubShapes(TopAbs_VERTEX); + } + else { + otherVertices = s.getSubShapes(TopAbs_VERTEX); + } + if (otherVertices.size() != vertices.size()) { + continue; + } + if (checkGeometry && !compareGeometry(s, false)) { + continue; + } + unsigned i = 0; + bool matched = true; + for (auto& v : vertices) { + bool found = false; + for (unsigned j = 0; j < otherVertices.size(); ++j) { + auto& v1 = otherVertices[i]; + if (++i == otherVertices.size()) { + i = 0; + } + if (BRep_Tool::Pnt(TopoDS::Vertex(v)) + .SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(v1))) + <= tol2) { + found = true; + break; + } + } + if (!found) { + matched = false; + break; + } + } + if (!matched) { + continue; + } + + if (shapeType == TopAbs_FACE && checkGeometry) { + // Is it really necessary to check geometries of each edge of a face? + // Right now we only do outer wire check + auto otherEdges = otherWire.getSubShapes(TopAbs_EDGE); + std::vector> geos; + geos.resize(otherEdges.size()); + bool matched = true; + unsigned i = 0; + auto edges = wire.getSubShapes(TopAbs_EDGE); + for (auto& e : edges) { + std::unique_ptr g(Geometry::fromShape(e)); + if (!g) { + matched = false; + break; + } + bool isLine = false; + gp_Pnt pt1, pt2; + if (g->isDerivedFrom(GeomLine::getClassTypeId()) + || g->isDerivedFrom(GeomLineSegment::getClassTypeId())) { + pt1 = BRep_Tool::Pnt(TopExp::FirstVertex(TopoDS::Edge(e))); + pt2 = BRep_Tool::Pnt(TopExp::LastVertex(TopoDS::Edge(e))); + isLine = true; + } + // We will tolerate on edge reordering + bool found = false; + for (unsigned j = 0; j < otherEdges.size(); j++) { + auto& e1 = otherEdges[i]; + auto& g1 = geos[i]; + if (++i >= otherEdges.size()) { + i = 0; + } + if (!g1) { + g1 = Geometry::fromShape(e1); + if (!g1) { + break; + } + } + if (isLine) { + if (g1->isDerivedFrom(GeomLine::getClassTypeId()) + || g1->isDerivedFrom(GeomLineSegment::getClassTypeId())) { + auto p1 = + BRep_Tool::Pnt(TopExp::FirstVertex(TopoDS::Edge(e1))); + auto p2 = + BRep_Tool::Pnt(TopExp::LastVertex(TopoDS::Edge(e1))); + if ((p1.SquareDistance(pt1) <= tol2 + && p2.SquareDistance(pt2) <= tol2) + || (p1.SquareDistance(pt2) <= tol2 + && p2.SquareDistance(pt1) <= tol2)) { + found = true; + break; + } + } + continue; + } + + if (g1->isSame(*g, tol, atol)) { + found = true; + break; + } + } + if (!found) { + matched = false; + break; + } + } + if (!matched) { + continue; + } + } + if (names) { + names->push_back(shapeName(shapeType) + std::to_string(idx)); + } + res.push_back(s); + } + } + break; + } + default: + break; + } + return res; +} + int TopoShape::findAncestor(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const { initCache(); From c5d462a3d94eb3585ae677217435a2e61ee3c8a7 Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Tue, 13 Feb 2024 22:25:48 +0530 Subject: [PATCH 07/19] [planegcs][test] Fix issue in arc-B-spline tangent test Correct the size of the knot vector used (described here with knots and multiplicities). --- tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp b/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp index 670553d180cc..105cb20df543 100644 --- a/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp +++ b/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp @@ -85,13 +85,15 @@ TEST_F(ConstraintsTest, tangentBSplineAndArc) // NOLINT } std::vector weights(bSplineControlPoints.size(), 1.0); std::vector weightsAsPtr; - std::vector knots(bSplineControlPoints.size()); + std::vector knots(bSplineControlPoints.size() - 2); // Hardcoded for cubic std::vector knotsAsPtr; - std::vector mult(bSplineControlPoints.size(), 1); - mult.front() = 4; // Hardcoded for cubic - mult.back() = 4; // Hardcoded for cubic + std::vector mult(bSplineControlPoints.size() - 2, 1); // Hardcoded for cubic + mult.front() = 4; // Hardcoded for cubic + mult.back() = 4; // Hardcoded for cubic for (size_t i = 0; i < bSplineControlPoints.size(); ++i) { weightsAsPtr.push_back(&weights[i]); + } + for (size_t i = 0; i < knots.size(); ++i) { knots[i] = i; knotsAsPtr.push_back(&knots[i]); } From 6c3f250e4ca94bfaa19adbb9f9bc383a5ac83e97 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 15 Feb 2024 07:55:15 -0500 Subject: [PATCH 08/19] Toponaming/Part: Add tests for searchSubShape --- src/Mod/Part/App/TopoShape.h | 15 ++- src/Mod/Part/App/TopoShapeExpansion.cpp | 12 +- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 123 ++++++++++++++++++ 3 files changed, 140 insertions(+), 10 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index ebb6f581bb89..285dd06126e8 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -118,6 +118,12 @@ enum class RefineFail throwException }; +/// Behavior of findSubShapesWithSharedVertex. +enum class CheckGeometry +{ + ignoreGeometry, + checkGeometry +}; /** The representation for a CAD Shape */ class PartExport TopoShape: public Data::ComplexGeoData @@ -703,8 +709,10 @@ class PartExport TopoShape: public Data::ComplexGeoData std::vector findAncestors(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const; std::vector findAncestorsShapes(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const; - /** Search sub shape + /** Find sub shapes with shared Vertexes. * + * Renamed: searchSubShape -> findSubShapesWithSharedVertex + * * unlike findShape(), the input shape does not have to be an actual * sub-shape of this shape. The sub-shape is searched by shape geometry * Note that subshape must be a Vertex, Edge, or Face. @@ -715,10 +723,9 @@ class PartExport TopoShape: public Data::ComplexGeoData * @param tol: tolerance to check coincident vertices * @param atol: tolerance to check for same angles */ - // TODO: Refactor this method and its tests later in Toponaming Phase 3. - std::vector searchSubShape(const TopoShape &subshape, + std::vector findSubShapesWithSharedVertex(const TopoShape &subshape, std::vector *names=nullptr, - bool checkGeometry=true, + CheckGeometry checkGeometry=CheckGeometry::checkGeometry, double tol=1e-7, double atol=1e-12) const; //@} diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index de0b1b045441..ef4f8e1edc11 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -221,9 +221,9 @@ TopoDS_Shape TopoShape::findShape(TopAbs_ShapeEnum type, int idx) const return _cache->findShape(_Shape, type, idx); } -std::vector TopoShape::searchSubShape(const TopoShape& subshape, +std::vector TopoShape::findSubShapesWithSharedVertex(const TopoShape& subshape, std::vector* names, - bool checkGeometry, + CheckGeometry checkGeometry, double tol, double atol) const { @@ -266,7 +266,7 @@ std::vector TopoShape::searchSubShape(const TopoShape& subshape, vertices = subshape.getSubShapes(TopAbs_VERTEX); } - if (vertices.empty() || checkGeometry) { + if (vertices.empty() || checkGeometry == CheckGeometry::checkGeometry) { g = Geometry::fromShape(subshape.getShape()); if (!g) { return res; @@ -330,7 +330,7 @@ std::vector TopoShape::searchSubShape(const TopoShape& subshape, // * Perform geometry comparison of the ancestor and input shape. // * For face, perform addition geometry comparison of each edges. std::unordered_set shapeSet; - for (auto& v : searchSubShape(vertices[0], nullptr, checkGeometry, tol, atol)) { + for (auto& v : findSubShapesWithSharedVertex(vertices[0], nullptr, checkGeometry, tol, atol)) { for (auto idx : findAncestors(v.getShape(), shapeType)) { auto s = getSubTopoShape(shapeType, idx); if (!shapeSet.insert(s).second) { @@ -352,7 +352,7 @@ std::vector TopoShape::searchSubShape(const TopoShape& subshape, if (otherVertices.size() != vertices.size()) { continue; } - if (checkGeometry && !compareGeometry(s, false)) { + if (checkGeometry == CheckGeometry::checkGeometry && !compareGeometry(s, false)) { continue; } unsigned i = 0; @@ -380,7 +380,7 @@ std::vector TopoShape::searchSubShape(const TopoShape& subshape, continue; } - if (shapeType == TopAbs_FACE && checkGeometry) { + if (shapeType == TopAbs_FACE && checkGeometry == CheckGeometry::checkGeometry) { // Is it really necessary to check geometries of each edge of a face? // Right now we only do outer wire check auto otherEdges = otherWire.getSubShapes(TopAbs_EDGE); diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index e72288d45e85..894be2d2a8a0 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -746,6 +746,129 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestors) EXPECT_TRUE(ancestorShapeList.back().IsEqual(topoShape6.getShape())); } +TEST_F(TopoShapeExpansionTest, findSubShapesWithSharedVertexEverything) +{ + // Arrange + auto [box1, box2] = CreateTwoCubes(); + TopoShape box1TS {box1}; + std::vector names; + std::vector names1; + std::vector names2; + double tol {2}; // Silly big tolerance to get everything + double atol {2}; + + TopExp_Explorer exp(box1, TopAbs_FACE); + auto face = exp.Current(); + exp.Init(box1, TopAbs_EDGE); + auto edge = exp.Current(); + exp.Init(box1, TopAbs_VERTEX); + auto vertex = exp.Current(); + // Act + auto shapes = + box1TS.findSubShapesWithSharedVertex(face, &names, CheckGeometry::checkGeometry, tol, atol); + auto shapes1 = box1TS.findSubShapesWithSharedVertex(edge, + &names1, + CheckGeometry::checkGeometry, + tol, + atol); + auto shapes2 = box1TS.findSubShapesWithSharedVertex(vertex, + &names2, + CheckGeometry::checkGeometry, + tol, + atol); + // Assert + EXPECT_EQ(shapes.size(), 6); + EXPECT_EQ(names.size(), 6); + EXPECT_STREQ(names[0].c_str(), "Face1"); + EXPECT_STREQ(names[1].c_str(), "Face3"); + EXPECT_STREQ(names[2].c_str(), "Face6"); + EXPECT_STREQ(names[3].c_str(), "Face5"); + EXPECT_STREQ(names[4].c_str(), "Face4"); + EXPECT_STREQ(names[5].c_str(), "Face2"); + EXPECT_EQ(shapes1.size(), 12); + EXPECT_EQ(names1.size(), 12); + EXPECT_EQ(shapes2.size(), 8); + EXPECT_EQ(names2.size(), 8); +} + +TEST_F(TopoShapeExpansionTest, findSubShapesWithSharedVertexMid) +{ + // Arrange + auto [box1, box2] = CreateTwoCubes(); + TopoShape box1TS {box1}; + std::vector names; + std::vector names1; + std::vector names2; + double tol {1e-0}; + double atol {1e-04}; + + TopExp_Explorer exp(box1, TopAbs_FACE); + auto face = exp.Current(); + exp.Init(box1, TopAbs_EDGE); + auto edge = exp.Current(); + exp.Init(box1, TopAbs_VERTEX); + auto vertex = exp.Current(); + // Act + auto shapes = + box1TS.findSubShapesWithSharedVertex(face, &names, CheckGeometry::checkGeometry, tol, atol); + auto shapes1 = box1TS.findSubShapesWithSharedVertex(edge, + &names1, + CheckGeometry::checkGeometry, + tol, + atol); + auto shapes2 = box1TS.findSubShapesWithSharedVertex(vertex, + &names2, + CheckGeometry::checkGeometry, + tol, + atol); + // Assert + EXPECT_EQ(shapes.size(), 6); + EXPECT_EQ(names.size(), 6); + EXPECT_EQ(shapes1.size(), 7); + EXPECT_EQ(names1.size(), 7); + EXPECT_EQ(shapes2.size(), 4); + EXPECT_EQ(names2.size(), 4); +} + +TEST_F(TopoShapeExpansionTest, findSubShapesWithSharedVertexClose) +{ + // Arrange + auto [box1, box2] = CreateTwoCubes(); + TopoShape box1TS {box1}; + std::vector names; + std::vector names1; + std::vector names2; + double tol {1e-02}; + double atol {1e-04}; + + TopExp_Explorer exp(box1, TopAbs_FACE); + auto face = exp.Current(); + exp.Init(box1, TopAbs_EDGE); + auto edge = exp.Current(); + exp.Init(box1, TopAbs_VERTEX); + auto vertex = exp.Current(); + // Act + auto shapes = + box1TS.findSubShapesWithSharedVertex(face, &names, CheckGeometry::checkGeometry, tol, atol); + auto shapes1 = box1TS.findSubShapesWithSharedVertex(edge, + &names1, + CheckGeometry::checkGeometry, + tol, + atol); + auto shapes2 = box1TS.findSubShapesWithSharedVertex(vertex, + &names2, + CheckGeometry::checkGeometry, + tol, + atol); + // Assert + EXPECT_EQ(shapes.size(), 1); + EXPECT_EQ(names.size(), 1); + EXPECT_EQ(shapes1.size(), 1); + EXPECT_EQ(names1.size(), 1); + EXPECT_EQ(shapes2.size(), 1); + EXPECT_EQ(names2.size(), 1); +} + TEST_F(TopoShapeExpansionTest, makeElementShellInvalid) { // Arrange From f2d4a31171e30a447747fc0753cf59ecb0876e3d Mon Sep 17 00:00:00 2001 From: Max Wilfinger Date: Tue, 13 Feb 2024 16:41:44 +0100 Subject: [PATCH 09/19] add action workflow to generate monthly issue metrics --- .github/workflows/issue-metrics.yml | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/issue-metrics.yml diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml new file mode 100644 index 000000000000..76c947c4f289 --- /dev/null +++ b/.github/workflows/issue-metrics.yml @@ -0,0 +1,43 @@ +name: Monthly issue metrics +on: + workflow_dispatch: + schedule: + - cron: '0 0 15 * *' + +permissions: + issues: write + pull-requests: read + +jobs: + build: + name: issue metrics + runs-on: ubuntu-latest + + steps: + + - name: Get dates for last month + shell: bash + run: | + # Calculate the first day of the previous month + first_day=$(date -d "last month" +%Y-%m-01) + + # Calculate the last day of the previous month + last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d) + + #Set an environment variable with the date range + echo "$first_day..$last_day" + echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV" + + - name: Run issue-metrics tool + uses: github/issue-metrics@v2 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }} -reason:"not planned"' + + - name: Create issue + uses: peter-evans/create-issue-from-file@v4 + with: + title: Monthly issue metrics report + token: ${{ secrets.GITHUB_TOKEN }} + content-filepath: ./issue_metrics.md + assignees: maxwxyz \ No newline at end of file From 964e15805ac4686d7a93218078c50932b398c50e Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 15 Feb 2024 08:05:23 -0500 Subject: [PATCH 10/19] Toponaming/Part: transfer in linearize --- src/Mod/Part/App/TopoShape.h | 10 ++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 78 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index b6bbd06133f6..25b679011125 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -317,6 +317,10 @@ class PartExport TopoShape: public Data::ComplexGeoData bool isInfinite() const; /// Checks whether the shape is a planar face bool isPlanar(double tol = 1.0e-7) const; + /// Check if this shape is a single linear edge, works on BSplineCurve and BezierCurve + bool isLinearEdge(Base::Vector3d *dir = nullptr, Base::Vector3d *base = nullptr) const; + /// Check if this shape is a single planar face, works on BSplineSurface and BezierSurface + bool isPlanarFace(double tol=1e-7) const; //@} /** @name Boolean operation*/ @@ -680,6 +684,12 @@ class PartExport TopoShape: public Data::ComplexGeoData } //@} + /** Try to simplify geometry of any linear/planar subshape to line/plane + * + * @return Return true if the shape is modified + */ + bool linearize(bool face, bool edge); + static TopAbs_ShapeEnum shapeType(const char* type, bool silent = false); static TopAbs_ShapeEnum shapeType(char type, bool silent = false); TopAbs_ShapeEnum shapeType(bool silent = false) const; diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index c7c7081cbf92..4da7445a7751 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,8 @@ #include "Geometry.h" #include +#include +#include "Geometry.h" FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -2490,6 +2493,81 @@ TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient return TopoShape {}; } +bool TopoShape::isLinearEdge(Base::Vector3d* dir, Base::Vector3d* base) const +{ + if (isNull() || getShape().ShapeType() != TopAbs_EDGE) { + return false; + } + + if (!GeomCurve::isLinear(BRepAdaptor_Curve(TopoDS::Edge(getShape())).Curve().Curve(), + dir, + base)) { + return false; + } + + // BRep_Tool::Curve() will transform the returned geometry, so no need to + // check the shape's placement. + return true; +} + +bool TopoShape::isPlanarFace(double tol) const +{ + if (isNull() || getShape().ShapeType() != TopAbs_FACE) { + return false; + } + + return GeomSurface::isPlanar(BRepAdaptor_Surface(TopoDS::Face(getShape())).Surface().Surface(), + nullptr, + tol); +} + +bool TopoShape::linearize(bool face, bool edge) +{ + bool touched = false; + BRep_Builder builder; + // Note: changing edge geometry seems to mess up with face (or shell, or solid) + // Probably need to do some fix afterwards. + if (edge) { + for (auto& edge : getSubTopoShapes(TopAbs_EDGE)) { + TopoDS_Edge e = TopoDS::Edge(edge.getShape()); + BRepAdaptor_Curve curve(e); + if (curve.GetType() == GeomAbs_Line || !edge.isLinearEdge()) { + continue; + } + std::unique_ptr geo( + Geometry::fromShape(e.Located(TopLoc_Location()).Oriented(TopAbs_FORWARD))); + std::unique_ptr gline(static_cast(geo.get())->toLine()); + if (gline) { + touched = true; + builder.UpdateEdge(e, + Handle(Geom_Curve)::DownCast(gline->handle()), + e.Location(), + BRep_Tool::Tolerance(e)); + } + } + } + if (face) { + for (auto& face : getSubTopoShapes(TopAbs_FACE)) { + TopoDS_Face f = TopoDS::Face(face.getShape()); + BRepAdaptor_Surface surf(f); + if (surf.GetType() == GeomAbs_Plane || !face.isPlanarFace()) { + continue; + } + std::unique_ptr geo( + Geometry::fromShape(f.Located(TopLoc_Location()).Oriented(TopAbs_FORWARD))); + std::unique_ptr gplane(static_cast(geo.get())->toPlane()); + if (gplane) { + touched = true; + builder.UpdateFace(f, + Handle(Geom_Surface)::DownCast(gplane->handle()), + f.Location(), + BRep_Tool::Tolerance(f)); + } + } + } + return touched; +} + struct MapperFill: Part::TopoShape::Mapper { BRepFill_Generator& maker; From a489b095f0c55c5fd2a86589a289a90fc3294b3f Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 15 Feb 2024 17:47:27 +0100 Subject: [PATCH 11/19] Assembly: Add ${PYTHON_INCLUDE_DIRS} to CMakeLists.txt Assembly Gui is including which in turn includes , so path to Python includes is needed. --- src/Mod/Assembly/Gui/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Assembly/Gui/CMakeLists.txt b/src/Mod/Assembly/Gui/CMakeLists.txt index a7dc9c9f99a7..b20c30bad826 100644 --- a/src/Mod/Assembly/Gui/CMakeLists.txt +++ b/src/Mod/Assembly/Gui/CMakeLists.txt @@ -3,6 +3,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${COIN3D_INCLUDE_DIRS} ${OCC_INCLUDE_DIR} + ${PYTHON_INCLUDE_DIRS} ) set(AssemblyGui_LIBS From c49c52716c757697c2096cd7c36f8d2f7ec988c7 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Tue, 13 Feb 2024 19:55:46 -0300 Subject: [PATCH 12/19] Fem: Move Points and Normals properties to base class --- src/Mod/Fem/App/FemConstraint.cpp | 23 +++++++++++++++ src/Mod/Fem/App/FemConstraint.h | 4 +++ src/Mod/Fem/App/FemConstraintContact.cpp | 26 ----------------- src/Mod/Fem/App/FemConstraintContact.h | 4 --- src/Mod/Fem/App/FemConstraintDisplacement.cpp | 28 ------------------ src/Mod/Fem/App/FemConstraintDisplacement.h | 4 --- src/Mod/Fem/App/FemConstraintFixed.cpp | 29 +------------------ src/Mod/Fem/App/FemConstraintFixed.h | 4 --- .../Fem/App/FemConstraintFluidBoundary.cpp | 21 -------------- src/Mod/Fem/App/FemConstraintFluidBoundary.h | 2 -- src/Mod/Fem/App/FemConstraintForce.cpp | 20 +------------ src/Mod/Fem/App/FemConstraintForce.h | 2 -- src/Mod/Fem/App/FemConstraintHeatflux.cpp | 27 ----------------- src/Mod/Fem/App/FemConstraintHeatflux.h | 3 -- .../App/FemConstraintInitialTemperature.cpp | 27 ----------------- .../Fem/App/FemConstraintInitialTemperature.h | 4 --- .../Fem/App/FemConstraintPlaneRotation.cpp | 28 +----------------- src/Mod/Fem/App/FemConstraintPlaneRotation.h | 5 ---- src/Mod/Fem/App/FemConstraintPressure.cpp | 25 +--------------- src/Mod/Fem/App/FemConstraintPressure.h | 2 -- src/Mod/Fem/App/FemConstraintSpring.cpp | 24 --------------- src/Mod/Fem/App/FemConstraintSpring.h | 2 -- src/Mod/Fem/App/FemConstraintTemperature.cpp | 27 ----------------- src/Mod/Fem/App/FemConstraintTemperature.h | 4 --- src/Mod/Fem/App/FemConstraintTransform.cpp | 16 ---------- src/Mod/Fem/App/FemConstraintTransform.h | 2 -- 26 files changed, 31 insertions(+), 332 deletions(-) diff --git a/src/Mod/Fem/App/FemConstraint.cpp b/src/Mod/Fem/App/FemConstraint.cpp index fddf496146bd..606e373b92bd 100644 --- a/src/Mod/Fem/App/FemConstraint.cpp +++ b/src/Mod/Fem/App/FemConstraint.cpp @@ -91,6 +91,19 @@ Constraint::Constraint() App::PropertyType(App::Prop_Output), "Scale used for drawing constraints"); // OvG: Add scale parameter inherited // by all derived constraints + ADD_PROPERTY_TYPE(Points, + (Base::Vector3d()), + "Constraint", + App::PropertyType(App::Prop_ReadOnly | App::Prop_Output | App::Prop_Hidden), + "Points where symbols are drawn"); + ADD_PROPERTY_TYPE(Normals, + (Base::Vector3d()), + "Constraint", + App::PropertyType(App::Prop_ReadOnly | App::Prop_Output | App::Prop_Hidden), + "Normals where symbols are drawn"); + + Points.setValues(std::vector()); + Normals.setValues(std::vector()); References.setScope(App::LinkScope::Global); } @@ -165,6 +178,16 @@ void Constraint::onChanged(const App::Property* prop) } } } + + std::vector points; + std::vector normals; + int scale = 1; + if (getPoints(points, normals, &scale)) { + Points.setValues(points); + Normals.setValues(normals); + Scale.setValue(scale); + Points.touch(); + } } App::DocumentObject::onChanged(prop); diff --git a/src/Mod/Fem/App/FemConstraint.h b/src/Mod/Fem/App/FemConstraint.h index e5f9c0782ffb..dba4804af3ed 100644 --- a/src/Mod/Fem/App/FemConstraint.h +++ b/src/Mod/Fem/App/FemConstraint.h @@ -102,6 +102,10 @@ class FemExport Constraint: public App::DocumentObject */ App::PropertyInteger Scale; + // Read-only (calculated values). These trigger changes in the ViewProvider + App::PropertyVectorList Points; + App::PropertyVectorList Normals; + /** * @brief Updates @ref NormalDirection. * diff --git a/src/Mod/Fem/App/FemConstraintContact.cpp b/src/Mod/Fem/App/FemConstraintContact.cpp index f4d4fa526874..51c74389b6c3 100644 --- a/src/Mod/Fem/App/FemConstraintContact.cpp +++ b/src/Mod/Fem/App/FemConstraintContact.cpp @@ -58,20 +58,6 @@ ConstraintContact::ConstraintContact() "ConstraintContact", App::PropertyType(App::Prop_None), "Stick slope"); - - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintContact", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintContact", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - /* */ - Points.setValues(std::vector()); - Normals.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintContact::execute() @@ -87,18 +73,6 @@ const char* ConstraintContact::getViewProviderName() const void ConstraintContact::onChanged(const App::Property* prop) { Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } } void ConstraintContact::handleChangedPropertyType(Base::XMLReader& reader, diff --git a/src/Mod/Fem/App/FemConstraintContact.h b/src/Mod/Fem/App/FemConstraintContact.h index 68b17eef8b75..c2cc575b3003 100644 --- a/src/Mod/Fem/App/FemConstraintContact.h +++ b/src/Mod/Fem/App/FemConstraintContact.h @@ -38,10 +38,6 @@ class FemExport ConstraintContact: public Fem::Constraint /// Constructor ConstraintContact(); - // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; - App::PropertyVectorList Normals; - /*Note*/ // Constraint parameters /****** diff --git a/src/Mod/Fem/App/FemConstraintDisplacement.cpp b/src/Mod/Fem/App/FemConstraintDisplacement.cpp index 4ef6497c70a3..7f8c3d7f8500 100644 --- a/src/Mod/Fem/App/FemConstraintDisplacement.cpp +++ b/src/Mod/Fem/App/FemConstraintDisplacement.cpp @@ -78,20 +78,6 @@ ConstraintDisplacement::ConstraintDisplacement() ADD_PROPERTY(rotzFix, (false)); ADD_PROPERTY(rotzFree, (true)); ADD_PROPERTY(zRotation, (0.0)); - - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintFixed", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintFixed", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - - Points.setValues(std::vector()); - Normals.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintDisplacement::execute() @@ -148,19 +134,5 @@ void ConstraintDisplacement::handleChangedPropertyType(Base::XMLReader& reader, void ConstraintDisplacement::onChanged(const App::Property* prop) { - // Note: If we call this at the end, then the arrows are not oriented correctly initially - // because the NormalDirection has not been calculated yet Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } } diff --git a/src/Mod/Fem/App/FemConstraintDisplacement.h b/src/Mod/Fem/App/FemConstraintDisplacement.h index 69fa72248f72..ef480b1f59ea 100644 --- a/src/Mod/Fem/App/FemConstraintDisplacement.h +++ b/src/Mod/Fem/App/FemConstraintDisplacement.h @@ -40,10 +40,6 @@ class FemExport ConstraintDisplacement: public Fem::Constraint /// Constructor ConstraintDisplacement(); - // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; - App::PropertyVectorList Normals; - // Displacement parameters App::PropertyDistance xDisplacement; App::PropertyDistance yDisplacement; diff --git a/src/Mod/Fem/App/FemConstraintFixed.cpp b/src/Mod/Fem/App/FemConstraintFixed.cpp index 0df6f879f5f2..59db8bc816b8 100644 --- a/src/Mod/Fem/App/FemConstraintFixed.cpp +++ b/src/Mod/Fem/App/FemConstraintFixed.cpp @@ -31,20 +31,7 @@ using namespace Fem; PROPERTY_SOURCE(Fem::ConstraintFixed, Fem::Constraint) ConstraintFixed::ConstraintFixed() -{ - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintFixed", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintFixed", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); -} +{} App::DocumentObjectExecReturn* ConstraintFixed::execute() { @@ -53,19 +40,5 @@ App::DocumentObjectExecReturn* ConstraintFixed::execute() void ConstraintFixed::onChanged(const App::Property* prop) { - // Note: If we call this at the end, then the symbols are not oriented correctly initially - // because the NormalDirection has not been calculated yet Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } } diff --git a/src/Mod/Fem/App/FemConstraintFixed.h b/src/Mod/Fem/App/FemConstraintFixed.h index a7d2d1462b4d..26a203971b6b 100644 --- a/src/Mod/Fem/App/FemConstraintFixed.h +++ b/src/Mod/Fem/App/FemConstraintFixed.h @@ -38,10 +38,6 @@ class FemExport ConstraintFixed: public Fem::Constraint /// Constructor ConstraintFixed(); - // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; - App::PropertyVectorList Normals; - /// recalculate the object App::DocumentObjectExecReturn* execute() override; diff --git a/src/Mod/Fem/App/FemConstraintFluidBoundary.cpp b/src/Mod/Fem/App/FemConstraintFluidBoundary.cpp index 56a98c888d68..76759626558c 100644 --- a/src/Mod/Fem/App/FemConstraintFluidBoundary.cpp +++ b/src/Mod/Fem/App/FemConstraintFluidBoundary.cpp @@ -96,17 +96,9 @@ ConstraintFluidBoundary::ConstraintFluidBoundary() "Heat flux value for thermal boundary condition"); ADD_PROPERTY_TYPE(HTCoeffValue,(0.0),"HeatTransfer",(App::PropertyType)(App::Prop_None), "Heat transfer coefficient for convective boundary condition"); - /// geometry rendering related properties - ADD_PROPERTY_TYPE(Points,(Base::Vector3d()),"FluidBoundary",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), - "Points where arrows are drawn"); - Points.setValues(std::vector()); ADD_PROPERTY_TYPE(DirectionVector,(Base::Vector3d(0,0,1)),"FluidBoundary",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), "Direction of arrows"); naturalDirectionVector = Base::Vector3d(0,0,0); // by default use the null vector to indicate an invalid value - // property from: FemConstraintFixed object - ADD_PROPERTY_TYPE(Normals,(Base::Vector3d()),"FluidBoundary",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), - "Normals where symbols are drawn"); - Normals.setValues(std::vector()); // clang-format on } @@ -147,19 +139,6 @@ void ConstraintFluidBoundary::onChanged(const App::Property* prop) Subtype.setValue(1); // need to trigger ViewProvider::updateData() for redraw in 3D view after this method } - - // naturalDirectionVector is a private member of this class - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } else if (prop == &Direction) { Base::Vector3d direction = getDirection(Direction); // if Direct has no link provided return Base::Vector3d(0,0,0); diff --git a/src/Mod/Fem/App/FemConstraintFluidBoundary.h b/src/Mod/Fem/App/FemConstraintFluidBoundary.h index 9a4303180062..b2904bbba753 100644 --- a/src/Mod/Fem/App/FemConstraintFluidBoundary.h +++ b/src/Mod/Fem/App/FemConstraintFluidBoundary.h @@ -54,8 +54,6 @@ class FemExport ConstraintFluidBoundary: public Fem::Constraint App::PropertyBool Reversed; // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; - App::PropertyVectorList Normals; // needed to draw diff BoundaryType App::PropertyVector DirectionVector; /// recalculate the object diff --git a/src/Mod/Fem/App/FemConstraintForce.cpp b/src/Mod/Fem/App/FemConstraintForce.cpp index 79b9d8c746b2..65da11d919e2 100644 --- a/src/Mod/Fem/App/FemConstraintForce.cpp +++ b/src/Mod/Fem/App/FemConstraintForce.cpp @@ -46,11 +46,6 @@ ConstraintForce::ConstraintForce() Direction.setScope(App::LinkScope::Global); ADD_PROPERTY(Reversed, (0)); - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintForce", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where arrows are drawn"); ADD_PROPERTY_TYPE(DirectionVector, (Base::Vector3d(0, 0, 1)), "ConstraintForce", @@ -59,7 +54,6 @@ ConstraintForce::ConstraintForce() // by default use the null vector to indicate an invalid value naturalDirectionVector = Base::Vector3d(0, 0, 0); - Points.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintForce::execute() @@ -91,19 +85,7 @@ void ConstraintForce::onChanged(const App::Property* prop) // because the NormalDirection has not been calculated yet Constraint::onChanged(prop); - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - // We don't use the normals because all arrows should have - // the same direction - Points.setValues(points); - Scale.setValue(scale); - Points.touch(); - } - } - else if (prop == &Direction) { + if (prop == &Direction) { Base::Vector3d direction = getDirection(Direction); if (direction.Length() < Precision::Confusion()) { return; diff --git a/src/Mod/Fem/App/FemConstraintForce.h b/src/Mod/Fem/App/FemConstraintForce.h index f669f63a6a5f..1c8bed7f0d3c 100644 --- a/src/Mod/Fem/App/FemConstraintForce.h +++ b/src/Mod/Fem/App/FemConstraintForce.h @@ -41,8 +41,6 @@ class FemExport ConstraintForce: public Fem::Constraint App::PropertyForce Force; App::PropertyLinkSub Direction; App::PropertyBool Reversed; - // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; App::PropertyVector DirectionVector; /// recalculate the object diff --git a/src/Mod/Fem/App/FemConstraintHeatflux.cpp b/src/Mod/Fem/App/FemConstraintHeatflux.cpp index fdff59ced963..5b77b97e28a1 100644 --- a/src/Mod/Fem/App/FemConstraintHeatflux.cpp +++ b/src/Mod/Fem/App/FemConstraintHeatflux.cpp @@ -45,19 +45,6 @@ ConstraintHeatflux::ConstraintHeatflux() (App::PropertyType)(App::Prop_None), "Type of constraint, surface convection or surface heat flux"); ConstraintType.setEnums(ConstraintTypes); - - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintHeatflux", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintHeatflux", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintHeatflux::execute() @@ -72,19 +59,5 @@ const char* ConstraintHeatflux::getViewProviderName() const void ConstraintHeatflux::onChanged(const App::Property* prop) { - // Note: If we call this at the end, then the arrows are not oriented correctly initially - // because the NormalDirection has not been calculated yet Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } } diff --git a/src/Mod/Fem/App/FemConstraintHeatflux.h b/src/Mod/Fem/App/FemConstraintHeatflux.h index 69a37d41e516..2da365326496 100644 --- a/src/Mod/Fem/App/FemConstraintHeatflux.h +++ b/src/Mod/Fem/App/FemConstraintHeatflux.h @@ -45,9 +45,6 @@ class FemExport ConstraintHeatflux: public Fem::Constraint App::PropertyFloat DFlux; App::PropertyEnumeration ConstraintType; - App::PropertyVectorList Points; - App::PropertyVectorList Normals; - /// recalculate the object App::DocumentObjectExecReturn* execute() override; diff --git a/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp b/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp index cb2af6aa78a6..0a6420a0a8f5 100644 --- a/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp +++ b/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp @@ -36,19 +36,6 @@ ConstraintInitialTemperature::ConstraintInitialTemperature() { ADD_PROPERTY(initialTemperature, (300.0)); - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintInitialTemperature", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintInitialTemperature", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); - References.setStatus(App::Property::ReadOnly, true); References.setStatus(App::Property::Hidden, true); } @@ -81,19 +68,5 @@ void ConstraintInitialTemperature::handleChangedPropertyType(Base::XMLReader& re void ConstraintInitialTemperature::onChanged(const App::Property* prop) { - // Note: If we call this at the end, then the arrows are not oriented correctly initially - // because the NormalDirection has not been calculated yet Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } } diff --git a/src/Mod/Fem/App/FemConstraintInitialTemperature.h b/src/Mod/Fem/App/FemConstraintInitialTemperature.h index fc7331b00259..6698a055c0cc 100644 --- a/src/Mod/Fem/App/FemConstraintInitialTemperature.h +++ b/src/Mod/Fem/App/FemConstraintInitialTemperature.h @@ -40,10 +40,6 @@ class FemExport ConstraintInitialTemperature: public Fem::Constraint /// Constructor ConstraintInitialTemperature(); - // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; - App::PropertyVectorList Normals; - // Temperature parameters App::PropertyTemperature initialTemperature; diff --git a/src/Mod/Fem/App/FemConstraintPlaneRotation.cpp b/src/Mod/Fem/App/FemConstraintPlaneRotation.cpp index fd329393a47c..53a884a67e87 100644 --- a/src/Mod/Fem/App/FemConstraintPlaneRotation.cpp +++ b/src/Mod/Fem/App/FemConstraintPlaneRotation.cpp @@ -31,21 +31,7 @@ using namespace Fem; PROPERTY_SOURCE(Fem::ConstraintPlaneRotation, Fem::Constraint) ConstraintPlaneRotation::ConstraintPlaneRotation() -{ - - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintPlaneRotation", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintPlaneRotation", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); -} +{} App::DocumentObjectExecReturn* ConstraintPlaneRotation::execute() { @@ -60,16 +46,4 @@ const char* ConstraintPlaneRotation::getViewProviderName() const void ConstraintPlaneRotation::onChanged(const App::Property* prop) { Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } } diff --git a/src/Mod/Fem/App/FemConstraintPlaneRotation.h b/src/Mod/Fem/App/FemConstraintPlaneRotation.h index a251ded4e80c..5ef2e0471ed2 100644 --- a/src/Mod/Fem/App/FemConstraintPlaneRotation.h +++ b/src/Mod/Fem/App/FemConstraintPlaneRotation.h @@ -38,11 +38,6 @@ class FemExport ConstraintPlaneRotation: public Fem::Constraint /// Constructor ConstraintPlaneRotation(); - // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; - App::PropertyVectorList Normals; - - /// recalculate the object App::DocumentObjectExecReturn* execute() override; diff --git a/src/Mod/Fem/App/FemConstraintPressure.cpp b/src/Mod/Fem/App/FemConstraintPressure.cpp index 911d55505681..427617139bd8 100644 --- a/src/Mod/Fem/App/FemConstraintPressure.cpp +++ b/src/Mod/Fem/App/FemConstraintPressure.cpp @@ -34,18 +34,6 @@ ConstraintPressure::ConstraintPressure() { ADD_PROPERTY(Pressure, (0.0)); ADD_PROPERTY(Reversed, (0)); - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintPressure", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where arrows are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintPressure", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintPressure::execute() @@ -80,18 +68,7 @@ void ConstraintPressure::onChanged(const App::Property* prop) { Constraint::onChanged(prop); - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = Scale.getValue(); - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); - Points.touch(); - } - } - else if (prop == &Reversed) { + if (prop == &Reversed) { Points.touch(); } } diff --git a/src/Mod/Fem/App/FemConstraintPressure.h b/src/Mod/Fem/App/FemConstraintPressure.h index c3f4de62b492..5568bcdc3fe5 100644 --- a/src/Mod/Fem/App/FemConstraintPressure.h +++ b/src/Mod/Fem/App/FemConstraintPressure.h @@ -39,8 +39,6 @@ class FemExport ConstraintPressure: public Fem::Constraint App::PropertyPressure Pressure; App::PropertyBool Reversed; - App::PropertyVectorList Points; - App::PropertyVectorList Normals; /// recalculate the object App::DocumentObjectExecReturn* execute() override; diff --git a/src/Mod/Fem/App/FemConstraintSpring.cpp b/src/Mod/Fem/App/FemConstraintSpring.cpp index 6aa2dbdb6d6b..1ce3db58a553 100644 --- a/src/Mod/Fem/App/FemConstraintSpring.cpp +++ b/src/Mod/Fem/App/FemConstraintSpring.cpp @@ -37,20 +37,8 @@ ConstraintSpring::ConstraintSpring() ADD_PROPERTY(NormalStiffness, (0.0)); ADD_PROPERTY(TangentialStiffness, (0.0)); ADD_PROPERTY(ElmerStiffness, (1)); - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintSpring", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where arrows are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintSpring", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); ElmerStiffness.setEnums(Stiffnesses); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintSpring::execute() @@ -66,16 +54,4 @@ const char* ConstraintSpring::getViewProviderName() const void ConstraintSpring::onChanged(const App::Property* prop) { Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = Scale.getValue(); - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); - Points.touch(); - } - } } diff --git a/src/Mod/Fem/App/FemConstraintSpring.h b/src/Mod/Fem/App/FemConstraintSpring.h index 52caebf72317..427dffab0ad3 100644 --- a/src/Mod/Fem/App/FemConstraintSpring.h +++ b/src/Mod/Fem/App/FemConstraintSpring.h @@ -40,8 +40,6 @@ class FemExport ConstraintSpring: public Fem::Constraint App::PropertyStiffness NormalStiffness; App::PropertyStiffness TangentialStiffness; App::PropertyEnumeration ElmerStiffness; - App::PropertyVectorList Points; - App::PropertyVectorList Normals; /// recalculate the object App::DocumentObjectExecReturn* execute() override; diff --git a/src/Mod/Fem/App/FemConstraintTemperature.cpp b/src/Mod/Fem/App/FemConstraintTemperature.cpp index 6c906333c8a6..c13801425e69 100644 --- a/src/Mod/Fem/App/FemConstraintTemperature.cpp +++ b/src/Mod/Fem/App/FemConstraintTemperature.cpp @@ -44,19 +44,6 @@ ConstraintTemperature::ConstraintTemperature() (App::PropertyType)(App::Prop_None), "Type of constraint, temperature or concentrated heat flux"); ConstraintType.setEnums(ConstraintTypes); - - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintTemperature", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintTemperature", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintTemperature::execute() @@ -93,19 +80,5 @@ void ConstraintTemperature::handleChangedPropertyType(Base::XMLReader& reader, void ConstraintTemperature::onChanged(const App::Property* prop) { - // Note: If we call this at the end, then the arrows are not oriented correctly initially - // because the NormalDirection has not been calculated yet Constraint::onChanged(prop); - - if (prop == &References) { - std::vector points; - std::vector normals; - int scale = 1; // OvG: Enforce use of scale - if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() - } - } } diff --git a/src/Mod/Fem/App/FemConstraintTemperature.h b/src/Mod/Fem/App/FemConstraintTemperature.h index b857c23a7f10..cf5612e2ef04 100644 --- a/src/Mod/Fem/App/FemConstraintTemperature.h +++ b/src/Mod/Fem/App/FemConstraintTemperature.h @@ -40,10 +40,6 @@ class FemExport ConstraintTemperature: public Fem::Constraint /// Constructor ConstraintTemperature(); - // Read-only (calculated values). These trigger changes in the ViewProvider - App::PropertyVectorList Points; - App::PropertyVectorList Normals; - // Temperature parameters App::PropertyTemperature Temperature; App::PropertyPower CFlux; diff --git a/src/Mod/Fem/App/FemConstraintTransform.cpp b/src/Mod/Fem/App/FemConstraintTransform.cpp index 53947104b20b..dc2856caa69c 100644 --- a/src/Mod/Fem/App/FemConstraintTransform.cpp +++ b/src/Mod/Fem/App/FemConstraintTransform.cpp @@ -66,18 +66,6 @@ ConstraintTransform::ConstraintTransform() "ConstraintTransform", App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), "Axis of cylindrical surface"); - ADD_PROPERTY_TYPE(Points, - (Base::Vector3d()), - "ConstraintTransform", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Points where symbols are drawn"); - ADD_PROPERTY_TYPE(Normals, - (Base::Vector3d()), - "ConstraintTransform", - App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), - "Normals where symbols are drawn"); - Points.setValues(std::vector()); - Normals.setValues(std::vector()); } App::DocumentObjectExecReturn* ConstraintTransform::execute() @@ -124,10 +112,6 @@ void ConstraintTransform::onChanged(const App::Property* prop) std::vector normals; int scale = 1; // OvG: Enforce use of scale if (getPoints(points, normals, &scale)) { - Points.setValues(points); - Normals.setValues(normals); - Scale.setValue(scale); // OvG: Scale - Points.touch(); // This triggers ViewProvider::updateData() std::string transform_type = TransformType.getValueAsString(); if (transform_type == "Cylindrical") { // Find data of cylinder diff --git a/src/Mod/Fem/App/FemConstraintTransform.h b/src/Mod/Fem/App/FemConstraintTransform.h index 27eb133da6fb..3c77fc948c50 100644 --- a/src/Mod/Fem/App/FemConstraintTransform.h +++ b/src/Mod/Fem/App/FemConstraintTransform.h @@ -40,8 +40,6 @@ class FemExport ConstraintTransform: public Fem::Constraint // Read-only (calculated values). These trigger changes in the ViewProvider App::PropertyLinkSubList RefDispl; App::PropertyLinkList NameDispl; - App::PropertyVectorList Points; - App::PropertyVectorList Normals; App::PropertyVector BasePoint; App::PropertyVector Axis; App::PropertyAngle X_rot; From ea840bc3a34c812f6e983c0e9eae84b75ab199e8 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Tue, 13 Feb 2024 20:17:24 -0300 Subject: [PATCH 13/19] Fem: Expose Constraint view provider symbol node to Python --- src/Mod/Fem/Gui/CMakeLists.txt | 3 + src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp | 10 +++ src/Mod/Fem/Gui/ViewProviderFemConstraint.h | 7 ++ .../Fem/Gui/ViewProviderFemConstraintPy.xml | 23 ++++++ .../Gui/ViewProviderFemConstraintPyImp.cpp | 71 +++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 src/Mod/Fem/Gui/ViewProviderFemConstraintPy.xml create mode 100644 src/Mod/Fem/Gui/ViewProviderFemConstraintPyImp.cpp diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt index 5635bef1fc86..2334876e0136 100755 --- a/src/Mod/Fem/Gui/CMakeLists.txt +++ b/src/Mod/Fem/Gui/CMakeLists.txt @@ -35,10 +35,13 @@ set(FemGui_LIBS PartGui ) +generate_from_xml(ViewProviderFemConstraintPy) generate_from_xml(ViewProviderFemMeshPy) generate_from_xml(ViewProviderFemPostPipelinePy) SET(Python_SRCS + ViewProviderFemConstraintPy.xml + ViewProviderFemConstraintPyImp.cpp ViewProviderFemMeshPy.xml ViewProviderFemMeshPyImp.cpp ViewProviderFemPostPipelinePy.xml diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp index 82fee78652fc..360bebe90812 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp @@ -47,6 +47,7 @@ #include "TaskFemConstraint.h" #include "ViewProviderFemConstraint.h" +#include "ViewProviderFemConstraintPy.h" using namespace FemGui; @@ -215,6 +216,15 @@ void ViewProviderFemConstraint::unsetEdit(int ModNum) } } } + +PyObject* ViewProviderFemConstraint::getPyObject() +{ + if (!pyViewObject) { + pyViewObject = new ViewProviderFemConstraintPy(this); + } + pyViewObject->IncRef(); + return pyViewObject; +} /* // Create a local coordinate system with the z-axis given in dir void getLocalCoordinateSystem(const SbVec3f& z, SbVec3f& y, SbVec3f& x) diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraint.h b/src/Mod/Fem/Gui/ViewProviderFemConstraint.h index e3843fb0fe04..f652fa1da6c8 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemConstraint.h +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraint.h @@ -72,10 +72,17 @@ class FemGuiExport ViewProviderFemConstraint: public Gui::ViewProviderGeometryOb std::vector claimChildren() const override; void setupContextMenu(QMenu*, QObject*, const char*) override; + PyObject* getPyObject() override; + /// Highlight the references that have been selected virtual void highlightReferences(const bool /* on */) {} + SoSeparator* getSymbolSeparator() const + { + return pShapeSep; + } + static std::string gethideMeshShowPartStr(); static std::string gethideMeshShowPartStr(const std::string showConstr); diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintPy.xml b/src/Mod/Fem/Gui/ViewProviderFemConstraintPy.xml new file mode 100644 index 000000000000..0c3a40bbf37d --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintPy.xml @@ -0,0 +1,23 @@ + + + + + + This is the ViewProviderFemConstraint class + + + + A pivy SoSeparator with the nodes of the constraint symbols + + + + + diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintPyImp.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintPyImp.cpp new file mode 100644 index 000000000000..8117daf4c815 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintPyImp.cpp @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2024 Mario Passaglia * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif + +#include + +#include "ViewProviderFemConstraintPy.h" +#include "ViewProviderFemConstraintPy.cpp" + + +using namespace FemGui; + +// returns a string which represent the object e.g. when printed in python +std::string ViewProviderFemConstraintPy::representation() const +{ + std::stringstream str; + str << ""; + + return str.str(); +} + + +Py::Object ViewProviderFemConstraintPy::getSymbolNode() const +{ + try { + SoSeparator* sep = getViewProviderFemConstraintPtr()->getSymbolSeparator(); + PyObject* Ptr = + Base::Interpreter().createSWIGPointerObj("pivy.coin", "_p_SoSeparator", sep, 1); + + sep->ref(); + + return Py::Object(Ptr, true); + } + catch (const Base::Exception& e) { + throw Py::RuntimeError(e.what()); + } +} + +PyObject* ViewProviderFemConstraintPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int ViewProviderFemConstraintPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} From d0c23eed30a2dc5cc0fa2e661bc35a322cbe9d93 Mon Sep 17 00:00:00 2001 From: Max Wilfinger Date: Thu, 15 Feb 2024 22:12:44 +0100 Subject: [PATCH 14/19] added Assembly option to problem template --- .github/ISSUE_TEMPLATE/PROBLEM_REPORT.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/PROBLEM_REPORT.yml b/.github/ISSUE_TEMPLATE/PROBLEM_REPORT.yml index 74c717574609..7abf44903915 100644 --- a/.github/ISSUE_TEMPLATE/PROBLEM_REPORT.yml +++ b/.github/ISSUE_TEMPLATE/PROBLEM_REPORT.yml @@ -34,6 +34,7 @@ body: options: - Addon Manager - Arch + - Assembly - Core - Draft - Expressions From 167b3846de44af462049915a8e8111d10c50b9e5 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Thu, 15 Feb 2024 22:20:53 +0100 Subject: [PATCH 15/19] Draft: gui_line_add_delete.py is obsolete --- .../draftguitools/gui_line_add_delete.py | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 src/Mod/Draft/draftguitools/gui_line_add_delete.py diff --git a/src/Mod/Draft/draftguitools/gui_line_add_delete.py b/src/Mod/Draft/draftguitools/gui_line_add_delete.py deleted file mode 100644 index 53aec152e2de..000000000000 --- a/src/Mod/Draft/draftguitools/gui_line_add_delete.py +++ /dev/null @@ -1,110 +0,0 @@ -# *************************************************************************** -# * (c) 2009, 2010 Yorik van Havre * -# * (c) 2009, 2010 Ken Cline * -# * (c) 2020 Eliud Cabrera Castillo * -# * * -# * This file is part of the FreeCAD CAx development system. * -# * * -# * This program is free software; you can redistribute it and/or modify * -# * it under the terms of the GNU Lesser General Public License (LGPL) * -# * as published by the Free Software Foundation; either version 2 of * -# * the License, or (at your option) any later version. * -# * for detail see the LICENCE text file. * -# * * -# * FreeCAD is distributed in the hope that it will be useful, * -# * but WITHOUT ANY WARRANTY; without even the implied warranty of * -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -# * GNU Library General Public License for more details. * -# * * -# * You should have received a copy of the GNU Library General Public * -# * License along with FreeCAD; if not, write to the Free Software * -# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -# * USA * -# * * -# *************************************************************************** -"""Provides GUI tools to do certain add and remove line operations. - -These GuiCommands aren't really used anymore, as the same actions -are implemented directly in the Draft_Edit command. -""" -## @package gui_line_add_delete -# \ingroup draftguitools -# \brief Provides GUI tools to do certain add and remove line operations. - -## \addtogroup draftguitools -# @{ -from PySide.QtCore import QT_TRANSLATE_NOOP - -import FreeCADGui as Gui -import Draft_rc -import DraftTools -import draftutils.utils as utils - -# The module is used to prevent complaints from code checkers (flake8) -True if Draft_rc.__name__ else False - - -class AddPoint(DraftTools.Modifier): - """GuiCommand to add a point to a line being drawn.""" - - def __init__(self): - self.running = False - - def GetResources(self): - """Set icon, menu and tooltip.""" - - return {'Pixmap': 'Draft_AddPoint', - 'MenuText': QT_TRANSLATE_NOOP("Draft_AddPoint", "Add point"), - 'ToolTip': QT_TRANSLATE_NOOP("Draft_AddPoint", "Adds a point to an existing Wire or B-spline.")} - - def IsActive(self): - """Return True when there is selection and the command is active.""" - if Gui.Selection.getSelection(): - return True - else: - return False - - def Activated(self): - """Execute when the command is called.""" - selection = Gui.Selection.getSelection() - if selection: - if (utils.get_type(selection[0]) in ['Wire', 'BSpline']): - Gui.runCommand("Draft_Edit") - Gui.draftToolBar.vertUi(True) - - -Gui.addCommand('Draft_AddPoint', AddPoint()) - - -class DelPoint(DraftTools.Modifier): - """GuiCommand to delete a point to a line being drawn.""" - - def __init__(self): - self.running = False - - def GetResources(self): - """Set icon, menu and tooltip.""" - - return {'Pixmap': 'Draft_DelPoint', - 'MenuText': QT_TRANSLATE_NOOP("Draft_DelPoint", "Remove point"), - 'ToolTip': QT_TRANSLATE_NOOP("Draft_DelPoint", "Removes a point from an existing Wire or B-spline.")} - - def IsActive(self): - """Return True when there is selection and the command is active.""" - if Gui.Selection.getSelection(): - return True - else: - return False - - def Activated(self): - """Execute when the command is called.""" - selection = Gui.Selection.getSelection() - if selection: - if (utils.get_type(selection[0]) in ['Wire', 'BSpline']): - Gui.runCommand("Draft_Edit") - Gui.draftToolBar.vertUi(False) - - -Gui.addCommand('Draft_DelPoint', DelPoint()) - -## @} From faa9ad4c16e779a92a2fd07b7acfc0a784fe5fd4 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Thu, 15 Feb 2024 22:28:37 +0100 Subject: [PATCH 16/19] Draft: remove reapply_diffuse_color workaround step 2 Stuff that was missed in #11946 --- src/Mod/Draft/draftmake/make_array.py | 1 - src/Mod/Draft/draftmake/make_clone.py | 1 - src/Mod/Draft/draftmake/make_patharray.py | 3 --- src/Mod/Draft/draftmake/make_pointarray.py | 1 - 4 files changed, 6 deletions(-) diff --git a/src/Mod/Draft/draftmake/make_array.py b/src/Mod/Draft/draftmake/make_array.py index b80e87f62767..518361692322 100644 --- a/src/Mod/Draft/draftmake/make_array.py +++ b/src/Mod/Draft/draftmake/make_array.py @@ -39,7 +39,6 @@ from draftobjects.array import Array if App.GuiUp: - from draftutils.todo import ToDo from draftviewproviders.view_array import ViewProviderDraftArray from draftviewproviders.view_draftlink import ViewProviderDraftLink diff --git a/src/Mod/Draft/draftmake/make_clone.py b/src/Mod/Draft/draftmake/make_clone.py index 746242bf010a..0af48100dbfc 100644 --- a/src/Mod/Draft/draftmake/make_clone.py +++ b/src/Mod/Draft/draftmake/make_clone.py @@ -34,7 +34,6 @@ from draftutils import gui_utils if App.GuiUp: - from draftutils.todo import ToDo from draftviewproviders.view_clone import ViewProviderClone diff --git a/src/Mod/Draft/draftmake/make_patharray.py b/src/Mod/Draft/draftmake/make_patharray.py index 2d86d09933f7..eb86cf3bf514 100644 --- a/src/Mod/Draft/draftmake/make_patharray.py +++ b/src/Mod/Draft/draftmake/make_patharray.py @@ -46,7 +46,6 @@ from draftobjects.pathtwistedarray import PathTwistedArray if App.GuiUp: - from draftutils.todo import ToDo from draftviewproviders.view_array import ViewProviderDraftArray from draftviewproviders.view_draftlink import ViewProviderDraftLink @@ -300,8 +299,6 @@ def make_path_array(base_object, path_object, count=4, ViewProviderDraftArray(new_obj.ViewObject) gui_utils.formatObject(new_obj, new_obj.Base) new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject) - # Workaround to trigger update of DiffuseColor: - ToDo.delay(reapply_diffuse_color, new_obj.ViewObject) new_obj.Base.ViewObject.hide() gui_utils.select(new_obj) diff --git a/src/Mod/Draft/draftmake/make_pointarray.py b/src/Mod/Draft/draftmake/make_pointarray.py index a519305ff931..9deb017cd9a6 100644 --- a/src/Mod/Draft/draftmake/make_pointarray.py +++ b/src/Mod/Draft/draftmake/make_pointarray.py @@ -43,7 +43,6 @@ from draftobjects.pointarray import PointArray if App.GuiUp: - from draftutils.todo import ToDo from draftviewproviders.view_array import ViewProviderDraftArray from draftviewproviders.view_draftlink import ViewProviderDraftLink From 61bcd553ee6256577fe026fb307657091b973ca4 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 15 Feb 2024 10:41:45 -0500 Subject: [PATCH 17/19] Toponaming/Part: Clean up linearize, add tests, and some delinting --- src/Mod/Part/App/TopoShape.h | 36 ++++++++++---- src/Mod/Part/App/TopoShapeExpansion.cpp | 7 +-- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 47 +++++++++++++++++++ 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 25b679011125..09e1e7272374 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -59,10 +59,11 @@ namespace Part struct ShapeHasher; class TopoShape; class TopoShapeCache; -typedef std::unordered_map TopoShapeMap; +using TopoShapeMap = std::unordered_map; /* A special sub-class to indicate null shapes */ +// NOLINTNEXTLINE cppcoreguidelines-special-member-functions class PartExport NullShapeException: public Base::ValueError { public: @@ -76,6 +77,7 @@ class PartExport NullShapeException: public Base::ValueError /* A special sub-class to indicate boolean failures */ +// NOLINTNEXTLINE cppcoreguidelines-special-member-functions class PartExport BooleanException: public Base::CADKernelError { public: @@ -89,6 +91,7 @@ class PartExport BooleanException: public Base::CADKernelError class PartExport ShapeSegment: public Data::Segment { + // NOLINTNEXTLINE cppcoreguidelines-avoid-non-const-global-variables TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: @@ -124,17 +127,32 @@ enum class CheckGeometry ignoreGeometry, checkGeometry }; + +enum class LinearizeFace +{ + noFaces, + linearizeFaces +}; + +enum class LinearizeEdge +{ + noEdges, + linearizeEdges +}; + /** The representation for a CAD Shape */ +// NOLINTNEXTLINE cppcoreguidelines-special-member-functions class PartExport TopoShape: public Data::ComplexGeoData { + // NOLINTNEXTLINE cppcoreguidelines-avoid-non-const-global-variables TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - TopoShape(long Tag=0, + TopoShape(long Tag=0, // NOLINT google-explicit-constructor App::StringHasherRef hasher=App::StringHasherRef(), const TopoDS_Shape &shape=TopoDS_Shape()); // Cannot be made explicit - TopoShape(const TopoDS_Shape&, + TopoShape(const TopoDS_Shape&, // NOLINT google-explicit-constructor long Tag=0, App::StringHasherRef hasher=App::StringHasherRef()); // Cannot be made explicit TopoShape(const TopoShape&); @@ -203,7 +221,7 @@ class PartExport TopoShape: public Data::ComplexGeoData uint16_t flags = 0) const override; void setFaces(const std::vector& Points, const std::vector& faces, - double tolerance = 1.0e-06); + double tolerance = 1.0e-06); // NOLINT void getDomains(std::vector&) const; //@} @@ -219,13 +237,13 @@ class PartExport TopoShape: public Data::ComplexGeoData std::vector getElementTypes() const override; unsigned long countSubElements(const char* Type) const override; /// get the subelement by type and number - Data::Segment* getSubElement(const char* Type, unsigned long) const override; + Data::Segment* getSubElement(const char* Type, unsigned long index) const override; /** Get lines from segment */ - void getLinesFromSubElement(const Data::Segment*, + void getLinesFromSubElement(const Data::Segment* segment, std::vector& Points, std::vector& lines) const override; /** Get faces from segment */ - void getFacesFromSubElement(const Data::Segment*, + void getFacesFromSubElement(const Data::Segment* segment, std::vector& Points, std::vector& PointNormals, std::vector& faces) const override; @@ -273,7 +291,7 @@ class PartExport TopoShape: public Data::ComplexGeoData /// get the Topo"sub"Shape with the given name PyObject* getPySubShape(const char* Type, bool silent = false) const; PyObject* getPyObject() override; - void setPyObject(PyObject*) override; + void setPyObject(PyObject* obj) override; /** @name Save/restore */ //@{ @@ -688,7 +706,7 @@ class PartExport TopoShape: public Data::ComplexGeoData * * @return Return true if the shape is modified */ - bool linearize(bool face, bool edge); + bool linearize(LinearizeFace face, LinearizeEdge edge); static TopAbs_ShapeEnum shapeType(const char* type, bool silent = false); static TopAbs_ShapeEnum shapeType(char type, bool silent = false); diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 4da7445a7751..f77357c39f07 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2521,13 +2521,14 @@ bool TopoShape::isPlanarFace(double tol) const tol); } -bool TopoShape::linearize(bool face, bool edge) +// TODO: Refactor this into two methods. Totally separate concerns here. +bool TopoShape::linearize(LinearizeFace do_face, LinearizeEdge do_edge) { bool touched = false; BRep_Builder builder; // Note: changing edge geometry seems to mess up with face (or shell, or solid) // Probably need to do some fix afterwards. - if (edge) { + if (do_edge == LinearizeEdge::linearizeEdges) { for (auto& edge : getSubTopoShapes(TopAbs_EDGE)) { TopoDS_Edge e = TopoDS::Edge(edge.getShape()); BRepAdaptor_Curve curve(e); @@ -2546,7 +2547,7 @@ bool TopoShape::linearize(bool face, bool edge) } } } - if (face) { + if (do_face == LinearizeFace::linearizeFaces) { for (auto& face : getSubTopoShapes(TopAbs_FACE)) { TopoDS_Face f = TopoDS::Face(face.getShape()); BRepAdaptor_Surface surf(f); diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 84279ed41a4f..25f81a5b6a81 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -7,12 +7,17 @@ #include "PartTestHelpers.h" +#include +#include #include #include #include #include #include #include +#include +#include +#include #include #include #include @@ -1111,4 +1116,46 @@ TEST_F(TopoShapeExpansionTest, makeElementDraftTopoShapes) EXPECT_EQ(result3.getElementMap().size(), 0); // No element map in non reference call. } +TEST_F(TopoShapeExpansionTest, makeElementLinearizeEdge) +{ + // Arrange + TColgp_Array1OfPnt points {1, 2}; + points.SetValue(1, gp_Pnt(0.0, 0.0, 0.0)); + points.SetValue(2, gp_Pnt(1.0, 0.0, 0.0)); + auto line1 = new Geom_BezierCurve(points); + auto edge1 = BRepBuilderAPI_MakeEdge(line1).Edge(); + TopoShape topoShape1 {edge1, 1L}; + // Act + auto edges = topoShape1.getSubTopoShapes(TopAbs_EDGE); + BRepAdaptor_Curve curve(TopoDS::Edge(edges.front().getShape())); + topoShape1.linearize(LinearizeFace::noFaces, LinearizeEdge::linearizeEdges); + auto edges2 = topoShape1.getSubTopoShapes(TopAbs_EDGE); + BRepAdaptor_Curve curve2(TopoDS::Edge(edges2.front().getShape())); + // Assert + EXPECT_EQ(curve.GetType(), GeomAbs_BezierCurve); + EXPECT_EQ(curve2.GetType(), GeomAbs_Line); +} + +TEST_F(TopoShapeExpansionTest, makeElementLinearizeFace) +{ + TColgp_Array2OfPnt points2 {1, 2, 1, 2}; + points2.SetValue(1, 1, gp_Pnt(0.0, 0.0, 0.0)); + points2.SetValue(2, 1, gp_Pnt(1.0, 0.0, 0.0)); + points2.SetValue(1, 2, gp_Pnt(0.0, 1.0, 0.0)); + points2.SetValue(2, 2, gp_Pnt(1.0, 1.0, 0.0)); + auto face1 = new Geom_BezierSurface(points2); + auto surf1 = BRepBuilderAPI_MakeFace(face1, 0.1).Face(); + TopoShape topoShape2 {surf1, 2L}; + // Act + auto faces = topoShape2.getSubTopoShapes(TopAbs_FACE); + BRepAdaptor_Surface surface(TopoDS::Face(faces.front().getShape())); + topoShape2.linearize(LinearizeFace::linearizeFaces, LinearizeEdge::noEdges); + auto faces2 = topoShape2.getSubTopoShapes(TopAbs_FACE); + BRepAdaptor_Surface surface2(TopoDS::Face(faces.front().getShape())); + // Assert + EXPECT_EQ(surface.GetType(), GeomAbs_BezierSurface); + EXPECT_EQ(surface2.GetType(), GeomAbs_Plane); +} + + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From c0c16f684c6152146e10a0a14d5aa948f7113b60 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Fri, 9 Feb 2024 21:19:07 -0500 Subject: [PATCH 18/19] TopoNaming/Part: transfer in MakERuledSurface --- src/Mod/Part/App/PartFeatures.cpp | 21 ++++ src/Mod/Part/App/TopoShape.h | 22 ++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 139 ++++++++++++++++++++++++ 3 files changed, 182 insertions(+) diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index facce022f3d3..cdba29b29249 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -114,6 +114,7 @@ App::DocumentObjectExecReturn* RuledSurface::getShape(const App::PropertyLinkSub App::DocumentObjectExecReturn *RuledSurface::execute() { try { +#ifdef FC_USE_TNP_FIX App::DocumentObjectExecReturn* ret; // get the first input shape @@ -231,6 +232,26 @@ App::DocumentObjectExecReturn *RuledSurface::execute() this->Shape.setValue(ruledShape); return App::DocumentObject::StdReturn; +#else + std::vector shapes; + std::array links = {&Curve1,&Curve2}; + for(auto link : links) { + const auto &subs = link->getSubValues(); + if(subs.empty()) + shapes.push_back(getTopoShape(link->getValue())); + else if(subs.size()!=1) + return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked."); + else + shapes.push_back(getTopoShape(link->getValue(), + subs.front().c_str(),true)); + if(shapes.back().isNull()) + return new App::DocumentObjectExecReturn("Invalid link."); + } + TopoShape res(0, getDocument()->getStringHasher()); + res.makERuledSurface(shapes, Orientation.getValue()); + this->Shape.setValue(res); + return Part::Feature::execute(); +#endif } catch (Standard_Failure& e) { diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 09e1e7272374..52d137466073 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -786,6 +786,28 @@ class PartExport TopoShape: public Data::ComplexGeoData } }; + /** Make a ruled surface + * + * @param sources: the source shapes, each of which must contain either a + * single edge or a single wire. + * @param orientation: + * @param isSolid: whether to make a solid + * @param isRuled: If true, then the faces generated between the edges of + * two consecutive section wires are ruled surfaces. If + * false, then they are smoothed out by approximation + * @param isClosed: If true, then the first section is duplicated to close + * the loft as the last section + * @param maxDegree: define the maximal U degree of the result surface + * @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 &makERuledSurface(const std::vector &source, int orientation=0, const char *op=nullptr); + /** Core function to generate mapped element names from shape history * * @param shape: the new shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index f77357c39f07..76ebe54daf4d 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1653,6 +1653,145 @@ TopoShape TopoShape::getSubTopoShape(TopAbs_ShapeEnum type, int idx, bool silent return shapeMap.getTopoShape(*this, idx); } +TopoShape &TopoShape::makERuledSurface(const std::vector &shapes, + int orientation, const char *op) +{ + if(!op) + op = Part::OpCodes::RuledSurface; + + if(shapes.size()!=2) + FC_THROWM(Base::CADKernelError,"Wrong number of input shape"); + + std::vector curves(2); + int i=0; + for(auto &s : shapes) { + if(s.isNull()) + HANDLE_NULL_INPUT; + auto type = s.shapeType(); + if(type == TopAbs_WIRE || type == TopAbs_EDGE) { + curves[i++] = s; + continue; + } + auto count = s.countSubShapes(TopAbs_WIRE); + if(count>1) + FC_THROWM(Base::CADKernelError,"Input shape has more than one wire"); + if(count==1) { + curves[i++] = s.getSubTopoShape(TopAbs_WIRE,1); + continue; + } + count = s.countSubShapes(TopAbs_EDGE); + if(count==0) + FC_THROWM(Base::CADKernelError,"Input shape has no edge"); + if(count == 1) { + curves[i++] = s.getSubTopoShape(TopAbs_EDGE,1); + continue; + } + curves[i] = s.makEWires(); + if(curves[i].isNull()) + HANDLE_NULL_INPUT; + if(curves[i].shapeType()!=TopAbs_WIRE) + FC_THROWM(Base::CADKernelError,"Input shape forms more than one wire"); + ++i; + } + + if(curves[0].shapeType()!=curves[1].shapeType()) { + for(auto &curve : curves) { + if(curve.shapeType() == TopAbs_EDGE) + curve = curve.makEWires(); + } + } + + auto &S1 = curves[0]; + auto &S2 = curves[1]; + bool isWire = S1.shapeType()==TopAbs_WIRE; + + // https://forum.freecadweb.org/viewtopic.php?f=8&t=24052 + // + // if both shapes are sub-elements of one common shape then the fill + // algorithm leads to problems if the shape has set a placement. The + // workaround is to copy the sub-shape + S1 = S1.makECopy(); + S2 = S2.makECopy(); + + if (orientation == 0) { + // Automatic + Handle(Adaptor3d_HCurve) a1; + Handle(Adaptor3d_HCurve) a2; + if (!isWire) { + BRepAdaptor_Curve adapt1(TopoDS::Edge(S1.getShape())); + BRepAdaptor_Curve adapt2(TopoDS::Edge(S2.getShape())); + a1 = new BRepAdaptor_HCurve(adapt1); + a2 = new BRepAdaptor_HCurve(adapt2); + } + else { + BRepAdaptor_CompCurve adapt1(TopoDS::Wire(S1.getShape())); + BRepAdaptor_CompCurve adapt2(TopoDS::Wire(S2.getShape())); + a1 = new BRepAdaptor_HCompCurve(adapt1); + a2 = new BRepAdaptor_HCompCurve(adapt2); + } + + if (!a1.IsNull() && !a2.IsNull()) { + // get end points of 1st curve + gp_Pnt p1 = a1->Value(a1->FirstParameter()); + gp_Pnt p2 = a1->Value(a1->LastParameter()); + if (S1.getShape().Orientation() == TopAbs_REVERSED) { + std::swap(p1, p2); + } + + // get end points of 2nd curve + gp_Pnt p3 = a2->Value(a2->FirstParameter()); + gp_Pnt p4 = a2->Value(a2->LastParameter()); + if (S2.getShape().Orientation() == TopAbs_REVERSED) { + std::swap(p3, p4); + } + + // Form two triangles (P1,P2,P3) and (P4,P3,P2) and check their normals. + // If the dot product is negative then it's assumed that the resulting face + // is twisted, hence the 2nd edge is reversed. + gp_Vec v1(p1, p2); + gp_Vec v2(p1, p3); + gp_Vec n1 = v1.Crossed(v2); + + gp_Vec v3(p4, p3); + gp_Vec v4(p4, p2); + gp_Vec n2 = v3.Crossed(v4); + + if (n1.Dot(n2) < 0) { + S2.setShape(S2.getShape().Reversed(), false); + } + } + } + else if (orientation == 2) { + // Reverse + S2.setShape(S2.getShape().Reversed(), false); + } + + TopoDS_Shape ruledShape; + if (!isWire) { + ruledShape = BRepFill::Face(TopoDS::Edge(S1.getShape()), TopoDS::Edge(S2.getShape())); + } + else { + ruledShape = BRepFill::Shell(TopoDS::Wire(S1.getShape()), TopoDS::Wire(S2.getShape())); + } + + // Both BRepFill::Face() and Shell() modifies the original input edges + // without any API to provide relationship to the output edges. So we have + // to use searchSubShape() to build the relationship by ourselves. + + TopoShape res(ruledShape.Located(TopLoc_Location())); + std::vector edges; + for (const auto &c : curves) { + for (const auto &e : c.getSubTopoShapes(TopAbs_EDGE)) { + auto found = res.searchSubShape(e); + if (found.size() > 0) { + found.front().resetElementMap(e.elementMap()); + edges.push_back(found.front()); + } + } + } + // Use empty mapper and let makEShape name the created surface with lower elements. + return makESHAPE(res.getShape(),Mapper(),edges,op); +} TopoShape& TopoShape::makeElementCompound(const std::vector& shapes, const char* op, From ac618d8ec517494b866ba15fbc142ec146adcfc2 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sat, 10 Feb 2024 07:59:45 -0500 Subject: [PATCH 19/19] TopoNaming/Part: cleanups and tests --- src/Mod/Part/App/PartFeatures.cpp | 39 ++++--- src/Mod/Part/App/TopoShape.h | 11 +- src/Mod/Part/App/TopoShapeExpansion.cpp | 108 +++++++++++------- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 27 +++++ 4 files changed, 114 insertions(+), 71 deletions(-) diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index cdba29b29249..6085ec5ae97b 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -115,6 +115,26 @@ App::DocumentObjectExecReturn *RuledSurface::execute() { try { #ifdef FC_USE_TNP_FIX + std::vector shapes; + std::array links = {&Curve1,&Curve2}; + for(auto link : links) { + const auto &subs = link->getSubValues(); + if(subs.empty()) + shapes.push_back(getTopoShape(link->getValue())); + else if(subs.size()!=1) + return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked."); + else + shapes.push_back(getTopoShape(link->getValue(), + subs.front().c_str(),true)); + if(shapes.back().isNull()) + return new App::DocumentObjectExecReturn("Invalid link."); + } + TopoShape res(0);//, getDocument()->getStringHasher()); + res.makeElementRuledSurface(shapes, Orientation.getValue()); + this->Shape.setValue(res); + return Part::Feature::execute(); + +#else App::DocumentObjectExecReturn* ret; // get the first input shape @@ -232,25 +252,6 @@ App::DocumentObjectExecReturn *RuledSurface::execute() this->Shape.setValue(ruledShape); return App::DocumentObject::StdReturn; -#else - std::vector shapes; - std::array links = {&Curve1,&Curve2}; - for(auto link : links) { - const auto &subs = link->getSubValues(); - if(subs.empty()) - shapes.push_back(getTopoShape(link->getValue())); - else if(subs.size()!=1) - return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked."); - else - shapes.push_back(getTopoShape(link->getValue(), - subs.front().c_str(),true)); - if(shapes.back().isNull()) - return new App::DocumentObjectExecReturn("Invalid link."); - } - TopoShape res(0, getDocument()->getStringHasher()); - res.makERuledSurface(shapes, Orientation.getValue()); - this->Shape.setValue(res); - return Part::Feature::execute(); #endif } catch (Standard_Failure& e) { diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 52d137466073..13803bf0b6a5 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -790,14 +790,7 @@ class PartExport TopoShape: public Data::ComplexGeoData * * @param sources: the source shapes, each of which must contain either a * single edge or a single wire. - * @param orientation: - * @param isSolid: whether to make a solid - * @param isRuled: If true, then the faces generated between the edges of - * two consecutive section wires are ruled surfaces. If - * false, then they are smoothed out by approximation - * @param isClosed: If true, then the first section is duplicated to close - * the loft as the last section - * @param maxDegree: define the maximal U degree of the result surface + * @param orientation: A Qt::Orientation, where Qt::Horizontal is 1 and Qt::Vertical is 2. * @param op: optional string to be encoded into topo naming for indicating * the operation * @@ -806,7 +799,7 @@ class PartExport TopoShape: public Data::ComplexGeoData * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &makERuledSurface(const std::vector &source, int orientation=0, const char *op=nullptr); + TopoShape &makeElementRuledSurface(const std::vector &source, int orientation=0, const char *op=nullptr); /** Core function to generate mapped element names from shape history * diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 76ebe54daf4d..69d666ae3f33 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -27,9 +27,16 @@ #ifndef _PreComp_ #include +#include +#include +# if OCC_VERSION_HEX < 0x070600 +# include +# include +# endif #include #include +#include #include #include #include @@ -77,6 +84,12 @@ FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT +#if OCC_VERSION_HEX >= 0x070600 +using Adaptor3d_HCurve = Adaptor3d_Curve; +using BRepAdaptor_HCurve = BRepAdaptor_Curve; +using BRepAdaptor_HCompCurve = BRepAdaptor_CompCurve; +#endif + namespace Part { @@ -1653,79 +1666,88 @@ TopoShape TopoShape::getSubTopoShape(TopAbs_ShapeEnum type, int idx, bool silent return shapeMap.getTopoShape(*this, idx); } -TopoShape &TopoShape::makERuledSurface(const std::vector &shapes, - int orientation, const char *op) +TopoShape& TopoShape::makeElementRuledSurface(const std::vector& shapes, + int orientation, + const char* op) { - if(!op) + if (!op) { op = Part::OpCodes::RuledSurface; + } - if(shapes.size()!=2) - FC_THROWM(Base::CADKernelError,"Wrong number of input shape"); + if (shapes.size() != 2) { + FC_THROWM(Base::CADKernelError, "Wrong number of input shapes"); + } std::vector curves(2); - int i=0; - for(auto &s : shapes) { - if(s.isNull()) - HANDLE_NULL_INPUT; + int i = 0; + for (auto& s : shapes) { + if (s.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } auto type = s.shapeType(); - if(type == TopAbs_WIRE || type == TopAbs_EDGE) { + if (type == TopAbs_WIRE || type == TopAbs_EDGE) { curves[i++] = s; continue; } - auto count = s.countSubShapes(TopAbs_WIRE); - if(count>1) - FC_THROWM(Base::CADKernelError,"Input shape has more than one wire"); - if(count==1) { - curves[i++] = s.getSubTopoShape(TopAbs_WIRE,1); + auto countOfWires = s.countSubShapes(TopAbs_WIRE); + if (countOfWires > 1) { + FC_THROWM(Base::CADKernelError, "Input shape has more than one wire"); + } + if (countOfWires == 1) { + curves[i++] = s.getSubTopoShape(TopAbs_WIRE, 1); continue; } - count = s.countSubShapes(TopAbs_EDGE); - if(count==0) - FC_THROWM(Base::CADKernelError,"Input shape has no edge"); - if(count == 1) { - curves[i++] = s.getSubTopoShape(TopAbs_EDGE,1); + auto countOfEdges = s.countSubShapes(TopAbs_EDGE); + if (countOfEdges == 0) { + FC_THROWM(Base::CADKernelError, "Input shape has no edge"); + } + if (countOfEdges == 1) { + curves[i++] = s.getSubTopoShape(TopAbs_EDGE, 1); continue; } - curves[i] = s.makEWires(); - if(curves[i].isNull()) - HANDLE_NULL_INPUT; - if(curves[i].shapeType()!=TopAbs_WIRE) - FC_THROWM(Base::CADKernelError,"Input shape forms more than one wire"); + curves[i] = s.makeElementWires(); + if (curves[i].isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + if (curves[i].shapeType() != TopAbs_WIRE) { + FC_THROWM(Base::CADKernelError, "Input shape forms more than one wire"); + } ++i; } - if(curves[0].shapeType()!=curves[1].shapeType()) { - for(auto &curve : curves) { - if(curve.shapeType() == TopAbs_EDGE) - curve = curve.makEWires(); + if (curves[0].shapeType() != curves[1].shapeType()) { + for (auto& curve : curves) { + if (curve.shapeType() == TopAbs_EDGE) { + curve = curve.makeElementWires(); + } } } - auto &S1 = curves[0]; - auto &S2 = curves[1]; - bool isWire = S1.shapeType()==TopAbs_WIRE; + auto& S1 = curves[0]; + auto& S2 = curves[1]; + bool isWire = S1.shapeType() == TopAbs_WIRE; // https://forum.freecadweb.org/viewtopic.php?f=8&t=24052 // // if both shapes are sub-elements of one common shape then the fill // algorithm leads to problems if the shape has set a placement. The // workaround is to copy the sub-shape - S1 = S1.makECopy(); - S2 = S2.makECopy(); + S1 = S1.makeElementCopy(); + S2 = S2.makeElementCopy(); if (orientation == 0) { // Automatic Handle(Adaptor3d_HCurve) a1; Handle(Adaptor3d_HCurve) a2; if (!isWire) { - BRepAdaptor_Curve adapt1(TopoDS::Edge(S1.getShape())); - BRepAdaptor_Curve adapt2(TopoDS::Edge(S2.getShape())); + BRepAdaptor_HCurve adapt1(TopoDS::Edge(S1.getShape())); + BRepAdaptor_HCurve adapt2(TopoDS::Edge(S2.getShape())); a1 = new BRepAdaptor_HCurve(adapt1); a2 = new BRepAdaptor_HCurve(adapt2); } else { - BRepAdaptor_CompCurve adapt1(TopoDS::Wire(S1.getShape())); - BRepAdaptor_CompCurve adapt2(TopoDS::Wire(S2.getShape())); + BRepAdaptor_HCompCurve adapt1(TopoDS::Wire(S1.getShape())); + BRepAdaptor_HCompCurve adapt2(TopoDS::Wire(S2.getShape())); a1 = new BRepAdaptor_HCompCurve(adapt1); a2 = new BRepAdaptor_HCompCurve(adapt2); } @@ -1780,17 +1802,17 @@ TopoShape &TopoShape::makERuledSurface(const std::vector &shapes, TopoShape res(ruledShape.Located(TopLoc_Location())); std::vector edges; - for (const auto &c : curves) { - for (const auto &e : c.getSubTopoShapes(TopAbs_EDGE)) { - auto found = res.searchSubShape(e); + for (const auto& c : curves) { + for (const auto& e : c.getSubTopoShapes(TopAbs_EDGE)) { + auto found = res.findSubShapesWithSharedVertex(e); if (found.size() > 0) { found.front().resetElementMap(e.elementMap()); edges.push_back(found.front()); } } } - // Use empty mapper and let makEShape name the created surface with lower elements. - return makESHAPE(res.getShape(),Mapper(),edges,op); + // Use empty mapper and let makeShapeWithElementMap name the created surface with lower elements. + return makeShapeWithElementMap(res.getShape(), Mapper(), edges, op); } TopoShape& TopoShape::makeElementCompound(const std::vector& shapes, diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 25f81a5b6a81..aa86b7dab742 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1157,5 +1157,32 @@ TEST_F(TopoShapeExpansionTest, makeElementLinearizeFace) EXPECT_EQ(surface2.GetType(), GeomAbs_Plane); } +TEST_F(TopoShapeExpansionTest, makeElementRuledSurfaceEdges) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape cube1TS {cube1, 1L}; + std::vector subEdges = cube1TS.getSubTopoShapes(TopAbs_EDGE); + std::vector shapes2 = {subEdges[0], subEdges[1]}; + // Act + TopoShape result2 = cube1TS.makeElementRuledSurface(shapes2, 0); // TODO: direction as enum? + // Assert + EXPECT_EQ(result2.countSubElements("Wire"), 1); + EXPECT_FLOAT_EQ(getArea(result2.getShape()), 0.32953611); +} + +TEST_F(TopoShapeExpansionTest, makeElementRuledSurfaceWires) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape cube1TS {cube1, 1L}; + std::vector subWires = cube1TS.getSubTopoShapes(TopAbs_WIRE); + std::vector shapes = {subWires[0], subWires[1]}; + // Act + TopoShape result = cube1TS.makeElementRuledSurface(shapes, 0); // TODO: direction as enum? + // Assert + EXPECT_EQ(result.countSubElements("Wire"), 4); + EXPECT_FLOAT_EQ(getArea(result.getShape()), 2.023056); +} // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)