diff --git a/src/App/VarSet.cpp b/src/App/VarSet.cpp index 9e9fdc9305..a8ea0fe5a3 100644 --- a/src/App/VarSet.cpp +++ b/src/App/VarSet.cpp @@ -20,7 +20,11 @@ * * ****************************************************************************/ +#include "PreCompiled.h" + +#ifndef _PreComp_ #include +#endif #include "VarSet.h" #include "DocumentObject.h" diff --git a/src/Mod/AddonManager/AddonManagerOptions.ui b/src/Mod/AddonManager/AddonManagerOptions.ui index 8d4ecb45e9..8eb3575c98 100644 --- a/src/Mod/AddonManager/AddonManagerOptions.ui +++ b/src/Mod/AddonManager/AddonManagerOptions.ui @@ -112,7 +112,7 @@ installed addons will be checked for available updates Hide Addons with non-FSF Free/Libre license - true + false HideNonFSFFreeLibre diff --git a/src/Mod/AddonManager/NetworkManager.py b/src/Mod/AddonManager/NetworkManager.py index 89531ff4ee..2970d8a3aa 100644 --- a/src/Mod/AddonManager/NetworkManager.py +++ b/src/Mod/AddonManager/NetworkManager.py @@ -502,7 +502,6 @@ if HAVE_QTNETWORK: current_index = index break - sender.abort() if current_index != -1: self.__launch_request(current_index, self.__create_get_request(url, timeout_ms)) @@ -579,7 +578,7 @@ if HAVE_QTNETWORK: return response_code = reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute) - if response_code == 301: # Permanently moved -- this is a redirect, bail out + if response_code == 301 or response_code == 302: # This is a redirect, bail out return if reply.error() != QtNetwork.QNetworkReply.NetworkError.OperationCanceledError: # It this was not a timeout, make sure we mark the queue task done diff --git a/src/Mod/AddonManager/package_list.py b/src/Mod/AddonManager/package_list.py index 8d8f8a07a8..d7a4a128eb 100644 --- a/src/Mod/AddonManager/package_list.py +++ b/src/Mod/AddonManager/package_list.py @@ -93,9 +93,9 @@ class PackageList(QtWidgets.QWidget): self.set_view_style(style) self.ui.view_bar.view_selector.set_current_view(style) - self.item_filter.setHidePy2(pref.GetBool("HidePy2", True)) - self.item_filter.setHideObsolete(pref.GetBool("HideObsolete", True)) - self.item_filter.setHideNonOSIApproved(pref.GetBool("HideNonOSIApproved", True)) + self.item_filter.setHidePy2(pref.GetBool("HidePy2", False)) + self.item_filter.setHideObsolete(pref.GetBool("HideObsolete", False)) + self.item_filter.setHideNonOSIApproved(pref.GetBool("HideNonOSIApproved", False)) self.item_filter.setHideNonFSFLibre(pref.GetBool("HideNonFSFFreeLibre", False)) self.item_filter.setHideNewerFreeCADRequired( pref.GetBool("HideNewerFreeCADRequired", False) diff --git a/src/Mod/Fem/femmesh/meshtools.py b/src/Mod/Fem/femmesh/meshtools.py index d1134f9dc1..d8ff9133f7 100644 --- a/src/Mod/Fem/femmesh/meshtools.py +++ b/src/Mod/Fem/femmesh/meshtools.py @@ -2508,4 +2508,21 @@ def compact_mesh( # may be return another value if the mesh was compacted, just check last map entries return (new_mesh, node_map, elem_map) +# ************************************************************************************************ +def beam_reduced_integration( + fileName +): + # replace B3x elements with B3xR elements + f = open(fileName, "r+") + lines = f.readlines() + f.seek(0) + for line in lines: + if line.find("B32") != -1: + line = line.replace("B32", "B32R") + if line.find("B31") != -1: + line = line.replace("B31", "B31R") + f.write(line) + + f.truncate() + f.close() ## @} diff --git a/src/Mod/Fem/femsolver/calculix/solver.py b/src/Mod/Fem/femsolver/calculix/solver.py index ca6707a893..eceeeac691 100644 --- a/src/Mod/Fem/femsolver/calculix/solver.py +++ b/src/Mod/Fem/femsolver/calculix/solver.py @@ -372,6 +372,15 @@ def add_attributes(obj, ccx_prefs): obj.BeamShellResultOutput3D = dimout + if not hasattr(obj, "BeamReducedIntegration"): + obj.addProperty( + "App::PropertyBool", + "BeamReducedIntegration", + "Fem", + "Set to True to use beam elements with reduced integration" + ) + obj.BeamReducedIntegration = True + """ Should there be some equation object for Calculix too? diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py index 9ca09fc318..19489c7d00 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py @@ -77,7 +77,7 @@ def write_femelement_geometry(f, ccxwriter): thickness = beamsec_obj.PipeThickness.getValueAs("mm").Value section_type = ", SECTION=PIPE" section_geo = "{:.13G},{:.13G}\n".format(radius, thickness) - section_def = "*BEAM GENERAL SECTION, {}{}{}\n".format( + section_def = "*BEAM SECTION, {}{}{}\n".format( elsetdef, material, section_type diff --git a/src/Mod/Fem/femsolver/calculix/write_mesh.py b/src/Mod/Fem/femsolver/calculix/write_mesh.py index 380a5628b0..acab0ce1c1 100644 --- a/src/Mod/Fem/femsolver/calculix/write_mesh.py +++ b/src/Mod/Fem/femsolver/calculix/write_mesh.py @@ -51,6 +51,10 @@ def write_mesh(ccxwriter): if ccxwriter.member.geos_fluidsection: meshtools.write_D_network_element_to_inputfile(ccxwriter.femmesh_file) + # Use reduced integration beam elements if this option is enabled in ccx solver settings + if ccxwriter.solver_obj.BeamReducedIntegration: + meshtools.beam_reduced_integration(ccxwriter.femmesh_file) + inpfile = codecs.open(ccxwriter.file_name, "w", encoding="utf-8") inpfile.write("{}\n".format(59 * "*")) inpfile.write("** {}\n".format(write_name)) @@ -69,6 +73,10 @@ def write_mesh(ccxwriter): # inpfile is closed meshtools.write_D_network_element_to_inputfile(ccxwriter.femmesh_file) + # Use reduced integration beam elements if this option is enabled in ccx solver settings + if ccxwriter.solver_obj.BeamReducedIntegration: + meshtools.beam_reduced_integration(ccxwriter.femmesh_file) + # reopen file with "append" to add all the rest inpfile = codecs.open(ccxwriter.femmesh_file, "a", encoding="utf-8") inpfile.write("\n\n") diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp index 99de648003..28c6659540 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp index 1fb6fb1330..b3580ae848 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 @@ -56,7 +56,7 @@ Eedges *********************************************************** ** Sections -*BEAM GENERAL SECTION, ELSET=M0B0RstdD0, MATERIAL=MechanicalMaterial, SECTION=PIPE +*BEAM SECTION, ELSET=M0B0RstdD0, MATERIAL=MechanicalMaterial, SECTION=PIPE 500,100 -0, 1, 0 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp index 49ca31c216..466bbbc895 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp index 30c3cd3896..18f22dfd05 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp @@ -61,7 +61,7 @@ ** Edge elements -*Element, TYPE=B31, ELSET=Eedges +*Element, TYPE=B31R, ELSET=Eedges 1, 1, 3 2, 3, 4 3, 4, 5 diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp index 104ce9d205..7c7d536051 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp @@ -17,7 +17,7 @@ ** Edge elements -*Element, TYPE=B32, ELSET=Eedges +*Element, TYPE=B32R, ELSET=Eedges 1, 1, 7, 3 2, 3, 8, 4 3, 4, 9, 5 diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 37db665359..e8b1163d86 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -214,6 +214,18 @@ enum class AsAngle yes }; +enum class CheckScale +{ + noScaleCheck, + checkScale +}; + +enum class Copy +{ + noCopy, + copy +}; + /** The representation for a CAD Shape */ // NOLINTNEXTLINE cppcoreguidelines-special-member-functions @@ -802,7 +814,7 @@ public: /** Make a hollowed solid by removing some faces from a given solid * - * @param source: input shape + * @param shape: input shape * @param faces: list of faces to remove, must be sub shape of the input shape * @param offset: thickness of the walls * @param tol: tolerance criterion for coincidence in generated shapes @@ -818,13 +830,12 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &makeElementThickSolid(const TopoShape &source, const std::vector &faces, + TopoShape &makeElementThickSolid(const TopoShape &shape, const std::vector &faces, double offset, double tol, bool intersection = false, bool selfInter = false, short offsetMode = 0, JoinType join = JoinType::arc, const char *op=nullptr); /** Make a hollowed solid by removing some faces from a given solid * - * @param source: input shape * @param faces: list of faces to remove, must be sub shape of the input shape * @param offset: thickness of the walls * @param tol: tolerance criterion for coincidence in generated shapes @@ -1240,6 +1251,43 @@ public: return TopoShape(0, Hasher).makeElementWires(*this, op, tol, policy, output); } + /** Make a new shape with transformation that may contain non-uniform scaling + * + * @param source: input shape + * @param mat: transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copy: whether to perform deep copy of the shape. If false, the + * shape will still be copied if there is scaling. + * + * @return The original content of this TopoShape is discarded and replaced + * with the new transformed shape. The function returns the + * TopoShape itself as a self reference so that multiple operations + * can be carried out for the same shape in the same line of code. + */ + TopoShape& makeElementGTransform(const TopoShape& source, + const Base::Matrix4D& mat, + const char* op = nullptr, + Copy copy = Copy::noCopy); + + /** Make a new shape with transformation that may contain non-uniform scaling + * + * @param source: input shape + * @param mat: transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copy: whether to perform deep copy of the shape. If false, the + * shape will still be copied if there is scaling. + * + * @return Return a new shape with transformation. The shape itself is not + * modified + */ + TopoShape makeElementGTransform(const Base::Matrix4D& mat, + const char* op = nullptr, + Copy copy = Copy::noCopy) const + { + return TopoShape(Tag, Hasher).makeElementGTransform(*this, mat, op, copy); + } /** Make a deep copy of the shape * @@ -1505,6 +1553,114 @@ public: .makeElementChamfer(*this, edges, radius1, radius2, op, flipDirection, asAngle); } + /** Make a new shape with transformation + * + * @param source: input shape + * @param mat: transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param checkScale: whether to check if the transformation matrix + * contains scaling factor. + * @param copy: whether to perform deep copy of the shape. If noCopy, and + * checkScale, then the shape will be copied if there + * is scaling. + * + * @return Returns true if scaling is performed. + * + * The original content of this TopoShape is discarded and replaced with + * the new transformed shape. + */ + bool _makeElementTransform(const TopoShape& source, + const Base::Matrix4D& mat, + const char* op = nullptr, + CheckScale checkScale = CheckScale::noScaleCheck, + Copy copy = Copy::noCopy); + + /** Make a new shape with transformation + * + * @param source: input shape + * @param mat: transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param checkScale: whether to check if the transformation matrix + * contains scaling factor. + * @param copy: whether to perform deep copy of the shape. If noCopy, and + * checkScale, then the shape will be copied if there + * is scaling. + * + * @return The original content of this TopoShape is discarded and replaced + * with the new transformed shape. The function returns the + * TopoShape itself as a self reference so that multiple operations + * can be carried out for the same shape in the same line of code. + */ + TopoShape& makeElementTransform(const TopoShape& source, + const Base::Matrix4D& mat, + const char* op = nullptr, + CheckScale checkScale = CheckScale::noScaleCheck, + Copy copy = Copy::noCopy) + { + _makeElementTransform(source, mat, op, checkScale, copy); + return *this; + } + + /** Make a new shape with transformation + * + * @param source: input shape + * @param mat: transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param checkScale: whether to check if the transformation matrix + * contains scaling factor. + * @param copy: whether to perform deep copy of the shape. If noCopy, and + * checkScale, then the shape will be copied if there + * is scaling. + * + * @return Return a new shape with transformation. The shape itself is not + * modified + */ + TopoShape makeElementTransform(const Base::Matrix4D& mat, + const char* op = nullptr, + CheckScale checkScale = CheckScale::noScaleCheck, + Copy copy = Copy::noCopy) const + { + return TopoShape(Tag, Hasher).makeElementTransform(*this, mat, op, checkScale, copy); + } + + /** Make a new shape with transformation + * + * @param source: input shape + * @param trsf: OCCT transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copy: whether to perform deep copy of the shape. + * + * @return The original content of this TopoShape is discarded and replaced + * with the new transformed shape. The function returns the + * TopoShape itself as a self reference so that multiple operations + * can be carried out for the same shape in the same line of code. + */ + TopoShape& makeElementTransform(const TopoShape& shape, + const gp_Trsf& trsf, + const char* op = nullptr, + Copy copy = Copy::noCopy); + + /** Make a new shape with transformation + * + * @param source: input shape + * @param trsf: OCCT transformation matrix + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copy: whether to perform deep copy of the shape. + * + * @return Return a new shape with transformation. The shape itself is not + * modified + */ + TopoShape + makeElementTransform(const gp_Trsf& trsf, const char* op = nullptr, Copy copy = Copy::noCopy) + { + return TopoShape(Tag, Hasher).makeElementTransform(*this, trsf, op, copy); + } + /* Make draft shape * * @param source: the source shape @@ -1743,7 +1899,7 @@ public: * makeShapeWithElementMap directly. For example: * makeElementShape(sewer, sources) * makeShapeWithElementMap(sewer.SewedShape(), MapperSewing(sewer), sources, OpCodes::Sewing); - * Note that if op exists in the method, it should be checked for null and overriden with + * Note that if op exists in the method, it should be checked for null and overridden with * the appropriate operation if so. */ diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index c1478031e0..4b66da9988 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -48,10 +48,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -85,8 +87,6 @@ #include "Geometry.h" #include -#include -#include "Geometry.h" FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -2508,6 +2508,110 @@ TopoShape& TopoShape::makeElementOrderedWires(const std::vector& shap return makeElementCompound(wires, nullptr, SingleShapeCompoundCreationPolicy::returnShape); } +bool TopoShape::_makeElementTransform(const TopoShape& shape, + const Base::Matrix4D& mat, + const char* op, + CheckScale checkScale, + Copy copy) +{ + if (checkScale == CheckScale::checkScale) { + auto scaleType = mat.hasScale(); + if (scaleType != Base::ScaleType::NoScaling && scaleType != Base::ScaleType::Uniform) { + makeElementGTransform(shape, mat, op, copy); + return true; + } + } + makeElementTransform(shape, convert(mat), op, copy); + return false; +} + +TopoShape& TopoShape::makeElementTransform(const TopoShape& shape, + const gp_Trsf& trsf, + const char* op, + Copy copy) +{ + if (copy == Copy::noCopy) { + // OCCT checks the ScaleFactor against gp::Resolution() which is DBL_MIN!!! + // No scaling is 1 as in 1:1 + const bool scaling = Abs(Abs(trsf.ScaleFactor()) - 1) > Precision::Confusion(); + const bool negative_scaling = + trsf.ScaleFactor() * trsf.HVectorialPart().Determinant() < 0.0; + copy = negative_scaling || scaling ? Copy::copy : Copy::noCopy; + } + TopoShape tmp(shape); + if (copy == Copy::copy) { + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + + BRepBuilderAPI_Transform mkTrf(shape.getShape(), trsf, Standard_True); + // TODO: calling Moved() is to make sure the shape has some Location, + // which is necessary for STEP export to work. However, if we reach + // here, it probably means BRepBuilderAPI_Transform has modified + // underlying shapes (because of scaling), it will break compound child + // parent relationship anyway. In short, STEP import/export will most + // likely break badly if there is any scaling involved + tmp.setShape(mkTrf.Shape().Moved(gp_Trsf()), false); + } + else { + tmp.move(trsf); + } + + if (op || (shape.Tag && shape.Tag != Tag)) { + setShape(tmp._Shape); + initCache(); + if (!Hasher) { + Hasher = tmp.Hasher; + } + copyElementMap(tmp, op); + } + else { + *this = tmp; + } + return *this; +} + +TopoShape& TopoShape::makeElementGTransform(const TopoShape& shape, + const Base::Matrix4D& mat, + const char* op, + Copy copy) +{ + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + + // if(!op) op = Part::OpCodes::Gtransform; + gp_GTrsf matrix; + matrix.SetValue(1, 1, mat[0][0]); + matrix.SetValue(2, 1, mat[1][0]); + matrix.SetValue(3, 1, mat[2][0]); + matrix.SetValue(1, 2, mat[0][1]); + matrix.SetValue(2, 2, mat[1][1]); + matrix.SetValue(3, 2, mat[2][1]); + matrix.SetValue(1, 3, mat[0][2]); + matrix.SetValue(2, 3, mat[1][2]); + matrix.SetValue(3, 3, mat[2][2]); + matrix.SetValue(1, 4, mat[0][3]); + matrix.SetValue(2, 4, mat[1][3]); + matrix.SetValue(3, 4, mat[2][3]); + + // geometric transformation + TopoShape tmp(shape); + BRepBuilderAPI_GTransform mkTrf(shape.getShape(), matrix, copy == Copy::copy); + tmp.setShape(mkTrf.Shape(), false); + if (op || (shape.Tag && shape.Tag != Tag)) { + setShape(tmp._Shape); + initCache(); + if (!Hasher) { + Hasher = tmp.Hasher; + } + copyElementMap(tmp, op); + } + else { + *this = tmp; + } + return *this; +} TopoShape& TopoShape::makeElementCopy(const TopoShape& shape, const char* op, bool copyGeom, bool copyMesh) diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index 9bdde05a58..1b7e665b0f 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1-or-later +#include #include "PartTestHelpers.h" // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) @@ -137,6 +138,32 @@ std::map elementMap(const TopoShape& shape) return result; } +std::string mappedElementVectorToString(std::vector& elements) +{ + std::stringstream output; + output << "{"; + for (const auto& element : elements) { + output << "\"" << element.name.toString() << "\", "; + } + output << "}"; + return output.str(); +} + +bool matchStringsWithoutClause(std::string first, std::string second, std::string regex) +{ + first = std::regex_replace(first, std::regex(regex), ""); + second = std::regex_replace(second, std::regex(regex), ""); + return first == second; +} + +/** + * Check to see if the elementMap in a shape contains all the names in a list + * The "Duplicate" clause in a name - ";Dnnn" can contain a random number, so we need to + * exclude those. + * @param shape The Shape + * @param names The vector of names + * @return An assertion usable by the gtest framework + */ testing::AssertionResult elementsMatch(const TopoShape& shape, const std::vector& names) { @@ -147,16 +174,12 @@ testing::AssertionResult elementsMatch(const TopoShape& shape, names.begin(), names.end(), [&](const Data::MappedElement& element, const std::string& name) { - return element.name.toString() == name; + return matchStringsWithoutClause(element.name.toString(), + name, + ";D[a-fA-F0-9]+"); }) == elements.end()) { - std::stringstream output; - output << "{"; - for (const auto& element : elements) { - output << "\"" << element.name.toString() << "\", "; - } - output << "}"; - return testing::AssertionFailure() << output.str(); + return testing::AssertionFailure() << mappedElementVectorToString(elements); } } return testing::AssertionSuccess(); @@ -168,7 +191,8 @@ testing::AssertionResult allElementsMatch(const TopoShape& shape, auto elements = shape.getElementMap(); if (elements.size() != names.size()) { return testing::AssertionFailure() - << elements.size() << " != " << names.size() << " elements in map"; + << elements.size() << " != " << names.size() + << " elements: " << mappedElementVectorToString(elements); } return elementsMatch(shape, names); } diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index b630f958fc..3ad7f17cd2 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1523,7 +1523,6 @@ TEST_F(TopoShapeExpansionTest, makeElementThickSolid) EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge11;THK;:H1:4,E")); } - TEST_F(TopoShapeExpansionTest, makeElementGeneralFuse) { // Arrange @@ -1860,19 +1859,19 @@ TEST_F(TopoShapeExpansionTest, makeElementSlice) EXPECT_EQ(TopAbs_ShapeEnum::TopAbs_WIRE, result.getShape().ShapeType()); // Assert that we're creating a correct element map EXPECT_TRUE(result.getMappedChildElements().empty()); - EXPECT_TRUE(allElementsMatch(result, - { - "Edge1;SLC;D1;MAK", - "Edge1;SLC;D2;MAK", - "Edge1;SLC;D3;MAK", - "Edge1;SLC;MAK", - "Vertex1;SLC;D1;MAK", - "Vertex1;SLC;D2;MAK", - "Vertex1;SLC;MAK", - "Vertex2;SLC;D1;MAK", - "Vertex2;SLC;D2;MAK", - "Vertex2;SLC;MAK", - })); + EXPECT_TRUE(elementsMatch(result, + { + "Edge1;SLC;D1;MAK", + "Edge1;SLC;D2;MAK", + "Edge1;SLC;D3;MAK", + "Edge1;SLC;MAK", + "Vertex1;SLC;D1;MAK", + "Vertex1;SLC;D2;MAK", + "Vertex1;SLC;MAK", + "Vertex2;SLC;D1;MAK", + "Vertex2;SLC;D2;MAK", + "Vertex2;SLC;MAK", + })); } TEST_F(TopoShapeExpansionTest, makeElementSlices) @@ -1954,4 +1953,99 @@ TEST_F(TopoShapeExpansionTest, makeElementMirror) "Vertex7;:M;MIR;:H1:7,V", "Vertex8;:M;MIR;:H1:7,V"})); } +TEST_F(TopoShapeExpansionTest, makeElementTransformWithoutMap) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto tr {gp_Trsf()}; + tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0))); + TopoShape topoShape1 {cube1, 1L}; + // Act + TopoShape& result = topoShape1.makeElementTransform(topoShape1, tr); + auto elements = elementMap(result); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -0.5, 0.0, 0.5, 0.5, 1.0))); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1); + // Assert elementMap is correct + EXPECT_EQ(elements.size(), 0); +} + +TEST_F(TopoShapeExpansionTest, makeElementTransformWithMap) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto tr {gp_Trsf()}; + tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0))); + cube2.Move(TopLoc_Location(tr)); + TopoShape topoShape1 {cube1, 1L}; + TopoShape topoShape2 {cube2, 2L}; + // Act + TopoShape& result = topoShape1.makeElementFuse({topoShape1, topoShape2}); // op, tolerance + topoShape1.makeElementTransform(result, tr); + auto elements = elementMap(result); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -1.0, 0.0, 1.0, 0.5, 1.0))); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1.75); + // Assert elementMap is correct + EXPECT_EQ(elements.size(), 66); + EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1); + EXPECT_EQ( + elements[IndexedName("Face", 1)], + MappedName( + "Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;" + "FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;" + "FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F")); +} + +TEST_F(TopoShapeExpansionTest, makeElementGTransformWithoutMap) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto tr {gp_Trsf()}; + tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0))); + TopoShape topoShape1 {cube1, 1L}; + // Act + TopoShape& result = topoShape1.makeElementGTransform(topoShape1, TopoShape::convert(tr)); + auto elements = elementMap(result); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -0.5, 0.0, 0.5, 0.5, 1.0))); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1); + // Assert elementMap is correct + EXPECT_EQ(elements.size(), 0); +} + +TEST_F(TopoShapeExpansionTest, makeElementGTransformWithMap) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto tr {gp_Trsf()}; + tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0))); + cube2.Move(TopLoc_Location(tr)); + TopoShape topoShape1 {cube1, 1L}; + TopoShape topoShape2 {cube2, 2L}; + // Act + TopoShape& result = topoShape1.makeElementFuse({topoShape1, topoShape2}); // op, tolerance + topoShape1.makeElementGTransform(result, TopoShape::convert(tr)); + auto elements = elementMap(result); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(-0.5, -1.0, 0.0, 1.0, 0.5, 1.0))); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1.75); + // Assert elementMap is correct + EXPECT_EQ(elements.size(), 66); + EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1); + EXPECT_EQ( + elements[IndexedName("Face", 1)], + MappedName( + "Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;" + "FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;" + "FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F")); +} + +// Not testing _makeElementTransform as it is a thin wrapper that calls the same places as the four +// preceding tests. + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp b/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp index e823ace748..84afef491b 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeElementRefine.cpp @@ -48,6 +48,6 @@ TEST_F(FeaturePartMakeElementRefineTest, makeElementRefineBoxes) EXPECT_EQ(refined.countSubElements("Face"), 6); // After refining it is one box EXPECT_EQ(refined.countSubElements("Edge"), 12); // 12 edges in a box // TODO: Make sure we have an elementMap for the refine. - // Refine doesn't work on compounds, so we're going to need a binary operation or the + // TODO: Refine doesn't work on compounds, so we're going to need a binary operation or the // like, and those don't exist yet. Once they do, this test can be expanded }