diff --git a/src/App/ElementNamingUtils.cpp b/src/App/ElementNamingUtils.cpp index 7a27ba4213..1ceddd832f 100644 --- a/src/App/ElementNamingUtils.cpp +++ b/src/App/ElementNamingUtils.cpp @@ -56,6 +56,11 @@ std::string Data::noElementName(const char *name) { } const char *Data::findElementName(const char *subname) { +#ifdef FC_USE_TNP_FIX + // skip leading dots + while(subname && subname[0] == '.') + ++subname; +#endif if(!subname || !subname[0] || isMappedElement(subname)) return subname; const char *dot = strrchr(subname,'.'); diff --git a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py index 0721c27225..4d1d2acdc5 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py +++ b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py @@ -1436,6 +1436,321 @@ class TestTopologicalNamingProblem(unittest.TestCase): self.assertAlmostEqual(volume3, boxVolume - 3 * filletVolume - cutVolume, 4) self.assertAlmostEqual(volume4, boxVolume - 2 * filletVolume - cutVolume, 4) + # TODO: ENABLE THIS TEST WHEN MULTISOLIDS AND TNP PLAY NICELY + # def testPD_TNPSketchPadMultipleSolids(self): + # """ Prove that a sketch with multiple wires works correctly""" + # doc = App.ActiveDocument + # App.activeDocument().addObject('PartDesign::Body','Body') + # doc.Body.newObject('Sketcher::SketchObject','Sketch') + # doc.Sketch.AttachmentSupport = (doc.XY_Plane,['']) + # doc.Sketch.MapMode = 'FlatFace' + # radius = 15 + # geoList = [] + # geoList.append(Part.Circle(App.Vector(-20, 20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # geoList = [] + # geoList.append(Part.Circle(App.Vector(20, 20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # geoList = [] + # geoList.append(Part.Circle(App.Vector(20, -20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # geoList = [] + # geoList.append(Part.Circle(App.Vector(-20,-20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # del geoList + # doc.recompute() + # doc.Body.newObject('PartDesign::Pad','Pad') + # doc.Pad.Profile = (doc.Sketch, ['',]) + # doc.Pad.Length = 10 + # doc.Pad.ReferenceAxis = (doc.Sketch,['N_Axis']) + # doc.Sketch.Visibility = False + # doc.recompute() + # expected_volume = math.pi * radius * radius * 10 * 4 # Volume of 4 padded circles + # self.assertAlmostEqual(doc.Body.Shape.Volume, expected_volume ) + # # Add additional code to attach another sketch, then change the original sketch and check TNP + + def testPD_TNPSketchPadTouching(self): + """ Prove that a sketch with touching wires works correctly""" + doc = App.ActiveDocument + App.activeDocument().addObject('PartDesign::Body','Body') + doc.Body.newObject('Sketcher::SketchObject','Sketch') + doc.Sketch.AttachmentSupport = (doc.XY_Plane,['']) + doc.Sketch.MapMode = 'FlatFace' + radius = 20 + geoList = [] + geoList.append(Part.Circle(App.Vector(-20, 20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + doc.Sketch.addGeometry(geoList,False) + geoList = [] + geoList.append(Part.Circle(App.Vector(20, 20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + doc.Sketch.addGeometry(geoList,False) + geoList = [] + geoList.append(Part.Circle(App.Vector(20, -20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + doc.Sketch.addGeometry(geoList,False) + geoList = [] + geoList.append(Part.Circle(App.Vector(-20,-20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + doc.Sketch.addGeometry(geoList,False) + del geoList + doc.recompute() + doc.Body.newObject('PartDesign::Pad','Pad') + doc.Pad.Profile = (doc.Sketch, ['',]) + doc.Pad.Length = 10 + doc.Pad.ReferenceAxis = (doc.Sketch,['N_Axis']) + doc.Sketch.Visibility = False + doc.recompute() + expected_volume = math.pi * radius * radius * 10 * 4 # Volume of 4 padded circles + # self.assertAlmostEqual(doc.Body.Shape.Volume, expected_volume ) # TODO ENABLE THIS ASSERTION WHEN IT PASSES + # # Add additional code to attach another sketch, then change the original sketch and check TNP + + # TODO ENABLE THIS TEST IF CODE IS WRITTEN TO SUPPORT SKETCHES WITH OVERLAPS. + # def testPD_TNPSketchPadOverlapping(self): + # """ Prove that a sketch with overlapping wires works correctly""" + # doc = App.ActiveDocument + # App.activeDocument().addObject('PartDesign::Body','Body') + # doc.Body.newObject('Sketcher::SketchObject','Sketch') + # doc.Sketch.AttachmentSupport = (doc.XY_Plane,['']) + # doc.Sketch.MapMode = 'FlatFace' + # radius = 25 + # geoList = [] + # geoList.append(Part.Circle(App.Vector(-20, 20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # geoList = [] + # geoList.append(Part.Circle(App.Vector(20, 20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # geoList = [] + # geoList.append(Part.Circle(App.Vector(20, -20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # geoList = [] + # geoList.append(Part.Circle(App.Vector(-20,-20, 0.000000), App.Vector(0.000000, 0.000000, 1.000000), radius)) + # doc.Sketch.addGeometry(geoList,False) + # del geoList + # doc.recompute() + # doc.Body.newObject('PartDesign::Pad','Pad') + # doc.Pad.Profile = (doc.Sketch, ['',]) + # doc.Pad.Length = 10 + # doc.Pad.ReferenceAxis = (doc.Sketch,['N_Axis']) + # doc.Sketch.Visibility = False + # doc.recompute() + # expected_volume = math.pi * radius * radius * 10 * 4 # Volume of 4 padded circles + # length = 12 # FIXME arbitrary guess, figure out the right value for either this or the angle. + # angle = 2 * math.asin(length/2*radius) + # expected_volume = expected_volume - 1/2 * radius * radius * (angle-math.sin(angle)) # Volume of the overlap areas + # self.assertAlmostEqual(doc.Body.Shape.Volume, expected_volume ) + # # Add additional code to attach another sketch, then change the original sketch and check TNP + + def testPD_TNPSketchPadSketchMove(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has geometry move""" + doc = App.ActiveDocument + App.activeDocument().addObject('PartDesign::Body','Body') + doc.Body.newObject('Sketcher::SketchObject','Sketch') + doc.Sketch.AttachmentSupport = (doc.XY_Plane,['']) + doc.Sketch.MapMode = 'FlatFace' + geoList = [] + geoList.append(Part.LineSegment(App.Vector(0,0,0),App.Vector(40,0,0))) + geoList.append(Part.LineSegment(App.Vector(40,0,0),App.Vector(40,20,0))) + geoList.append(Part.LineSegment(App.Vector(40,20,0),App.Vector(0,20,0))) + geoList.append(Part.LineSegment(App.Vector(0,20,0),App.Vector(0,0,0))) + doc.Sketch.addGeometry(geoList,False) + constraintList = [] + constraintList.append(Sketcher.Constraint('Coincident', 0, 2, 1, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 1, 2, 2, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 2, 2, 3, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 3, 2, 0, 1)) + constraintList.append(Sketcher.Constraint('Horizontal', 0)) + constraintList.append(Sketcher.Constraint('Horizontal', 2)) + constraintList.append(Sketcher.Constraint('Vertical', 1)) + constraintList.append(Sketcher.Constraint('Vertical', 3)) + doc.Sketch.addConstraint(constraintList) + doc.recompute() + doc.Body.newObject('PartDesign::Pad','Pad') + doc.Pad.Profile = (doc.Sketch, ['',]) + doc.Pad.Length = 10 + doc.Pad.ReferenceAxis = (doc.Sketch,['N_Axis']) + doc.Sketch.Visibility = False + doc.Pad.Length = 10.000000 + doc.Pad.TaperAngle = 0.000000 + doc.Pad.UseCustomVector = 0 + doc.Pad.Direction = (0, 0, 1) + doc.Pad.ReferenceAxis = (doc.Sketch, ['N_Axis']) + doc.Pad.AlongSketchNormal = 1 + doc.Pad.Type = 0 + doc.Pad.UpToFace = None + doc.Pad.Reversed = 0 + doc.Pad.Midplane = 0 + doc.Pad.Offset = 0 + doc.recompute() + doc.Sketch.Visibility = False + doc.Body.newObject('Sketcher::SketchObject','Sketch001') + doc.Sketch001.AttachmentSupport = (doc.Pad,['Face6',]) + doc.Sketch001.MapMode = 'FlatFace' + geoList = [] + geoList.append(Part.LineSegment(App.Vector(5,5,0),App.Vector(5,10,0))) + geoList.append(Part.LineSegment(App.Vector(5,10,0),App.Vector(25,10,0))) + geoList.append(Part.LineSegment(App.Vector(25,10,0),App.Vector(25,5,0))) + geoList.append(Part.LineSegment(App.Vector(25,5,0),App.Vector(5,5,0))) + doc.Sketch001.addGeometry(geoList,False) + del geoList + constraintList = [] + constraintList.append(Sketcher.Constraint('Coincident', 0, 2, 1, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 1, 2, 2, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 2, 2, 3, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 3, 2, 0, 1)) + constraintList.append(Sketcher.Constraint('Vertical', 0)) + constraintList.append(Sketcher.Constraint('Vertical', 2)) + constraintList.append(Sketcher.Constraint('Horizontal', 1)) + constraintList.append(Sketcher.Constraint('Horizontal', 3)) + doc.Sketch001.addConstraint(constraintList) + doc.recompute() + doc.Body.newObject('PartDesign::Pad','Pad001') + doc.Pad001.Profile = (doc.Sketch001, ['',]) + doc.Pad001.Length = 10 + doc.Pad001.ReferenceAxis = (doc.Sketch001,['N_Axis']) + doc.Sketch001.Visibility = False + doc.Pad001.Length = 10.000000 + doc.Pad001.TaperAngle = 0.000000 + doc.Pad001.UseCustomVector = 0 + doc.Pad001.Direction = (0, 0, 1) + doc.Pad001.ReferenceAxis = (doc.Sketch001, ['N_Axis']) + doc.Pad001.AlongSketchNormal = 1 + doc.Pad001.Type = 0 + doc.Pad001.UpToFace = None + doc.Pad001.Reversed = 0 + doc.Pad001.Midplane = 0 + doc.Pad001.Offset = 0 + doc.recompute() + doc.Pad.Visibility = False + doc.Sketch001.Visibility = False + doc.Sketch.movePoint(3,0,App.Vector(-5,0,0),1) + doc.Sketch.movePoint(0,0,App.Vector(0.000000,-5,0),1) + doc.Sketch.movePoint(1,0,App.Vector(-5,0.000000,0),1) + doc.Sketch.movePoint(2,0,App.Vector(-0,-5,0),1) + doc.recompute() + # If Sketch001 is still at the right start point, we are good. + self.assertTrue(doc.Sketch001.AttachmentOffset.Matrix == App.Matrix()) + matrix1 = App.Matrix() + matrix1.A34 = 10 # Z offset by 10. + self.assertTrue(doc.Sketch001.Placement.Matrix == matrix1) + + def testPD_TNPSketchPadSketchDelete(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has geometry deleted""" + doc = App.ActiveDocument + App.activeDocument().addObject('PartDesign::Body','Body') + doc.Body.newObject('Sketcher::SketchObject','Sketch') + doc.Sketch.AttachmentSupport = (doc.XY_Plane,['']) + doc.Sketch.MapMode = 'FlatFace' + import ProfileLib.RegularPolygon + ProfileLib.RegularPolygon.makeRegularPolygon(doc.Sketch,6,App.Vector(0.000000,0.000000,0),App.Vector(24,12,0),False) + doc.Sketch.addConstraint(Sketcher.Constraint('Coincident', 6, 3, -1, 1)) + doc.Sketch.addConstraint(Sketcher.Constraint('PointOnObject',0,2,-2)) + doc.recompute() + doc.Body.newObject('PartDesign::Pad','Pad') + doc.Pad.Profile = (doc.Sketch, ['',]) + doc.Pad.Length = 10 + doc.Pad.ReferenceAxis = (doc.Sketch,['N_Axis']) + doc.Sketch.Visibility = False + doc.Pad.Length = 10.000000 + doc.Pad.TaperAngle = 0.000000 + doc.Pad.UseCustomVector = 0 + doc.Pad.Direction = (0, 0, 1) + doc.Pad.ReferenceAxis = (doc.Sketch, ['N_Axis']) + doc.Pad.AlongSketchNormal = 1 + doc.Pad.Type = 0 + doc.Pad.UpToFace = None + doc.Pad.Reversed = 0 + doc.Pad.Midplane = 0 + doc.Pad.Offset = 0 + doc.recompute() + doc.Sketch.Visibility = False + doc.Body.newObject('Sketcher::SketchObject','Sketch001') + doc.Sketch001.AttachmentSupport = (doc.Pad,['Face8',]) + doc.Sketch001.MapMode = 'FlatFace' + geoList = [] + geoList.append(Part.LineSegment(App.Vector(-5, 5, 0.000000),App.Vector(-5, -5, 0.000000))) + geoList.append(Part.LineSegment(App.Vector(-5, -5, 0.000000),App.Vector(5, -5, 0.000000))) + geoList.append(Part.LineSegment(App.Vector(5, -5, 0.000000),App.Vector(5, 5, 0.000000))) + geoList.append(Part.LineSegment(App.Vector(5, 5, 0.000000),App.Vector(-5, 5, 0.000000))) + doc.Sketch001.addGeometry(geoList,False) + del geoList + constraintList = [] + constraintList.append(Sketcher.Constraint('Coincident', 0, 2, 1, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 1, 2, 2, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 2, 2, 3, 1)) + constraintList.append(Sketcher.Constraint('Coincident', 3, 2, 0, 1)) + constraintList.append(Sketcher.Constraint('Vertical', 0)) + constraintList.append(Sketcher.Constraint('Vertical', 2)) + constraintList.append(Sketcher.Constraint('Horizontal', 1)) + constraintList.append(Sketcher.Constraint('Horizontal', 3)) + doc.Sketch001.addConstraint(constraintList) + constraintList = [] + doc.recompute() + doc.Body.newObject('PartDesign::Pad','Pad001') + doc.Pad001.Profile = (doc.Sketch001, ['',]) + doc.Pad001.Length = 10 + doc.Pad001.ReferenceAxis = (doc.Sketch001,['N_Axis']) + doc.Sketch001.Visibility = False + doc.Pad001.Length = 10.000000 + doc.Pad001.TaperAngle = 0.000000 + doc.Pad001.UseCustomVector = 0 + doc.Pad001.Direction = (0, 0, 1) + doc.Pad001.ReferenceAxis = (doc.Sketch001, ['N_Axis']) + doc.Pad001.AlongSketchNormal = 1 + doc.Pad001.Type = 0 + doc.Pad001.UpToFace = None + doc.Pad001.Reversed = 0 + doc.Pad001.Midplane = 0 + doc.Pad001.Offset = 0 + doc.recompute() + doc.Pad.Visibility = False + doc.Sketch001.Visibility = False + doc.Sketch.delGeometries([4]) + doc.Sketch.addConstraint(Sketcher.Constraint('Coincident',3,2,4,1)) + doc.Sketch.delConstraint(12) + doc.recompute() + # If Sketch001 is still at the right start point, we are good. + self.assertTrue(doc.Sketch001.AttachmentOffset.Matrix == App.Matrix()) + matrix1 = App.Matrix() + matrix1.A34 = 10 # Z offset by 10 + self.assertTrue(doc.Sketch001.Placement.Matrix == matrix1) + + def testPD_TNPSketchPadSketchConstructionChange(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has geometry changed from Construction""" + pass # TODO + + def testPD_TNPSketchPadSketchTrim(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has geometry trimmed""" + pass # TODO + + def testPD_TNPSketchPadSketchExternal(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has external geometry changed""" + pass # TODO + + def testPD_TNPSketchPadSketchTransform(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has a transformation applied""" + pass # TODO + + def testPD_TNPSketchPadSketchSymmetry(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has Symmetry applied""" + pass # TODO + + def testPD_TNPSketchPadSketchBSpline(self): + """ Prove that a sketch attached to a padded sketch shape does not have a problem when the initial sketch has BSpline changed""" + pass # TODO + + def testPD_TNPSketchRotSketchMove(self): + """ Prove that a sketch attached to a rotated sketch shape does not have a problem when the initial sketch has geometry moved""" + pass # TODO + + def testPD_TNPSketchPocketSketchMove(self): + """ Prove that a sketch attached to a pocketed sketch shape does not have a problem when the initial sketch has geometry moved""" + pass # TODO + + def testPD_TNPSketchLoftSketchMove(self): + """ Prove that a sketch attached to a lofted sketch shape does not have a problem when the initial sketch has geometry moved""" + pass # TODO + + def testPD_TNPSketchPipeSketchMove(self): + """ Prove that a sketch attached to a piped sketch shape does not have a problem when the initial sketch has geometry moved""" + pass # TODO + def testSubelementNames(self): # Arrange doc = App.ActiveDocument diff --git a/tests/src/App/CMakeLists.txt b/tests/src/App/CMakeLists.txt index 5973f6dfba..369b70a97e 100644 --- a/tests/src/App/CMakeLists.txt +++ b/tests/src/App/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Expression.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ExpressionParser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ElementMap.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ElementNamingUtils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/IndexedName.cpp ${CMAKE_CURRENT_SOURCE_DIR}/License.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MappedElement.cpp diff --git a/tests/src/App/ElementNamingUtils.cpp b/tests/src/App/ElementNamingUtils.cpp new file mode 100644 index 0000000000..5c04a1d3cc --- /dev/null +++ b/tests/src/App/ElementNamingUtils.cpp @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include + +#include +#include +#include + +// NOLINTBEGIN(readability-magic-numbers) + + +class ElementNamingUtilsTest: public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + _docName = App::GetApplication().getUniqueDocumentName("test"); + App::GetApplication().newDocument(_docName.c_str(), "testUser"); + _sids = &_sid; + _hasher = Base::Reference(new App::StringHasher); + ASSERT_EQ(_hasher.getRefCount(), 1); + } + + void TearDown() override + { + App::GetApplication().closeDocument(_docName.c_str()); + } + + std::string _docName; + Data::ElementIDRefs _sid; + QVector* _sids; + App::StringHasherRef _hasher; +}; + +TEST_F(ElementNamingUtilsTest, findElementName) +{ + // Act + Data::ElementMap elementMap = Data::ElementMap(); + auto name1 = Data::findElementName("Edge1"); + auto name2 = Data::findElementName(";g5v2;SKT;:Had6,V;:G;OFS;:Had6:7,V;:G;OFS;:Had6:7,V;WIR;:" + "Had6:4,V;:G;XTR;:Had6:7,E;:H,E.Face1.Edge2"); + auto name3 = Data::findElementName("An.Example.Assembly.Edge3"); + auto name4 = Data::findElementName(".Edge4"); + + // Assert + EXPECT_STREQ(name1, "Edge1"); + EXPECT_STREQ(name2, + ";g5v2;SKT;:Had6,V;:G;OFS;:Had6:7,V;:G;OFS;:Had6:7,V;WIR;:" + "Had6:4,V;:G;XTR;:Had6:7,E;:H,E.Face1.Edge2"); + EXPECT_STREQ(name3, "Edge3"); + EXPECT_STREQ(name4, "Edge4"); +} +// NOLINTEND(readability-magic-numbers)