From 5b7e03013eb486f2c72805e1d3e3b2af2bc97085 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Fri, 9 Aug 2024 22:04:05 -0400 Subject: [PATCH] Toponaming: Fix transformed; abstract index element name generation --- src/App/ElementNamingUtils.cpp | 14 +++- src/App/ElementNamingUtils.h | 3 + src/App/StringHasher.cpp | 13 +--- src/Mod/Part/App/CrossSection.cpp | 10 +-- src/Mod/PartDesign/App/FeatureTransformed.cpp | 17 +++-- .../PartDesignTests/TestMultiTransform.py | 64 +++++++++++++++++++ .../TestTopologicalNamingProblem.py | 18 ++++-- 7 files changed, 107 insertions(+), 32 deletions(-) diff --git a/src/App/ElementNamingUtils.cpp b/src/App/ElementNamingUtils.cpp index 1ceddd832f..4f4d95ac30 100644 --- a/src/App/ElementNamingUtils.cpp +++ b/src/App/ElementNamingUtils.cpp @@ -91,4 +91,16 @@ bool Data::hasMissingElement(const char *subname) { const char *Data::hasMappedElementName(const char *subname) { return isMappedElement(findElementName(subname)); -} \ No newline at end of file +} + +// Currently used by CrossSection.cpp and FeatureTransformed.cpp with different label types +// with a default of "I" and "_" used by CrossSection. +const std::string Data::indexSuffix(int index, const char *label) +{ + if ( index < 2 ) { // Don't add a suffix for item #1, begin appending at 2 + return {""}; + } + std::string name(label); + name += std::to_string(index); + return name; +} diff --git a/src/App/ElementNamingUtils.h b/src/App/ElementNamingUtils.h index a4c2484b35..75ee651d30 100644 --- a/src/App/ElementNamingUtils.h +++ b/src/App/ElementNamingUtils.h @@ -69,6 +69,8 @@ constexpr const char* POSTFIX_GEN = ";:G"; constexpr const char* POSTFIX_MODGEN = ";:MG"; constexpr const char* POSTFIX_DUPLICATE = ";D"; +constexpr const char* ELEMENT_MAP_INDEX = "I"; +constexpr const char* ELEMENT_MAP_INDEX2 = "_"; /// Check if a subname contains missing element AppExport bool hasMissingElement(const char *subname); @@ -95,6 +97,7 @@ AppExport const char *findElementName(const char *subname); AppExport const char *hasMappedElementName(const char *subname); +AppExport const std::string indexSuffix(int index, const char *label=ELEMENT_MAP_INDEX); } // namespace Data // clang-format on diff --git a/src/App/StringHasher.cpp b/src/App/StringHasher.cpp index 43537233d8..d78daf3d68 100644 --- a/src/App/StringHasher.cpp +++ b/src/App/StringHasher.cpp @@ -462,18 +462,7 @@ const std::string& StringHasher::getPersistenceFileName() const void StringHasher::Save(Base::Writer& writer) const { - size_t count = 0; - if (_hashes->SaveAll) { - count = _hashes->size(); - } - else { - count = 0; - for (auto& hasher : _hashes->right) { - if (hasher.second->isMarked() || hasher.second->isPersistent()) { - ++count; - } - } - } + std::size_t count = _hashes->SaveAll ? _hashes->size() : this->count(); writer.Stream() << writer.ind() << "SaveAll << "\" threshold=\"" << _hashes->Threshold << "\""; diff --git a/src/Mod/Part/App/CrossSection.cpp b/src/Mod/Part/App/CrossSection.cpp index b0ec954ef8..5c36701789 100644 --- a/src/Mod/Part/App/CrossSection.cpp +++ b/src/Mod/Part/App/CrossSection.cpp @@ -265,10 +265,7 @@ void TopoCrossSection::sliceNonSolid(int idx, BRepAlgoAPI_Section cs(shape.getShape(), gp_Pln(a, b, c, -d)); if (cs.IsDone()) { std::string prefix(op); - if (idx > 1) { - prefix += '_'; - prefix += std::to_string(idx); - } + prefix += Data::indexSuffix(idx,Data::ELEMENT_MAP_INDEX2); auto res = TopoShape() .makeElementShape(cs, shape, prefix.c_str()) .makeElementWires() @@ -297,10 +294,7 @@ void TopoCrossSection::sliceSolid(int idx, BRepPrimAPI_MakeHalfSpace mkSolid(TopoDS::Face(face.getShape()), refPoint); TopoShape solid(idx); std::string prefix(op); - if (idx > 1) { - prefix += '_'; - prefix += std::to_string(idx); - } + prefix += Data::indexSuffix(idx,Data::ELEMENT_MAP_INDEX2); solid.makeElementShape(mkSolid, face, prefix.c_str()); BRepAlgoAPI_Cut mkCut(shape.getShape(), solid.getShape()); diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index ac518d9da8..869adcaf08 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -50,6 +50,7 @@ #include "FeatureLinearPattern.h" #include "FeaturePolarPattern.h" #include "FeatureSketchBased.h" +#include "Mod/Part/App/TopoShapeOpCode.h" using namespace PartDesign; @@ -285,10 +286,12 @@ App::DocumentObjectExecReturn* Transformed::execute() auto getTransformedCompShape = [&](const auto& origShape) { std::vector shapes; - TopoShape shape = origShape; - auto transformIter = transformations.cbegin(); - for (; transformIter != transformations.end(); ++transformIter) { - shapes.emplace_back(shape.makeElementTransform(*transformIter)); + TopoShape shape (origShape); + int idx=1; + for ( const auto& transformIter : transformations ) { + auto opName = Data::indexSuffix(idx++); + auto transformed = shape.makeElementTransform(transformIter, opName.c_str()); + shapes.emplace_back(transformed); } return shapes; }; @@ -338,15 +341,15 @@ App::DocumentObjectExecReturn* Transformed::execute() #endif if (!fuseShape.isNull()) { - supportShape = supportShape.makeElementFuse(getTransformedCompShape(fuseShape.getShape())); + supportShape.makeElementFuse(getTransformedCompShape(fuseShape.getShape())); } if (!cutShape.isNull()) { - supportShape = supportShape.makeElementCut(getTransformedCompShape(cutShape.getShape())); + supportShape.makeElementFuse(getTransformedCompShape(cutShape.getShape())); } } break; case Mode::TransformBody: { - supportShape = supportShape.makeElementFuse(getTransformedCompShape(supportShape)); + supportShape.makeElementFuse(getTransformedCompShape(supportShape)); break; } } diff --git a/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py b/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py index 6d52132d9d..272f708284 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py +++ b/src/Mod/PartDesign/PartDesignTests/TestMultiTransform.py @@ -65,6 +65,70 @@ class TestMultiTransform(unittest.TestCase): self.Doc.recompute() self.assertAlmostEqual(self.MultiTransform.Shape.Volume, 20000) + def testMultiTransformDressup(self): + # Arrange + Doc = self.Doc + Body = Doc.addObject('PartDesign::Body','Body') + # Make first offset cube Pad + PadSketch = Doc.addObject('Sketcher::SketchObject', 'SketchPad') + Body.addObject(PadSketch) + TestSketcherApp.CreateRectangleSketch(PadSketch, (0, 0), (10, 10)) + Doc.recompute() + Pad = Doc.addObject("PartDesign::Pad", "Pad") + Body.addObject(Pad) + Pad.Profile = PadSketch + Pad.Length = 10 + Doc.recompute() + + PadSketch2 = Doc.addObject('Sketcher::SketchObject', 'SketchPad') + PadSketch2.AttachmentSupport = (Pad, ('Face6',)) + Body.addObject(PadSketch2) + TestSketcherApp.CreateRectangleSketch(PadSketch, (9, 9), (1, 1)) + Doc.recompute() + Pad2 = Doc.addObject("PartDesign::Pad", "Pad2") + Body.addObject(Pad2) + Pad2.Profile = PadSketch2 + Pad2.Length = 10 + Doc.recompute() + + + MultiTransform = Doc.addObject("PartDesign::MultiTransform","MultiTransform") + Doc.recompute() + MultiTransform.Originals = [Pad] + MultiTransform.Shape = Pad.Shape + Body.addObject(MultiTransform) + Doc.recompute() + Mirrored = Doc.addObject("PartDesign::Mirrored","Mirrored") + Mirrored.MirrorPlane = (Doc.getObject('XY_Plane'), ['']) + Body.addObject(Mirrored) + Mirrored2 = Doc.addObject("PartDesign::Mirrored","Mirrored") + Mirrored2.MirrorPlane = (Doc.getObject('XZ_Plane'), [""]) + Body.addObject(Mirrored2) + MultiTransform.Transformations = [Mirrored,Mirrored2] + Doc.recompute() + Fillet = Doc.addObject("PartDesign::Fillet","Fillet") + Fillet.Base = (MultiTransform, ['Face'+str(i+1) for i in range(2)]) + Fillet.Radius = 3 + Body.addObject(Fillet) + # Add a fillet here. + # Now do that copy thing... + Link = Doc.addObject('App::Link','Link001') + Link.setLink(Doc.Body) + Link.Label='Body001' + # Act + # There are properties on those objects with values + # Link.addProperty("App::PropertyInteger","test2","Table2") + # Assert + self.assertAlmostEqual(Body.Shape.Volume, 990) + self.assertAlmostEqual(Link.Shape.Volume, 990) + + def testMultiTransformBody(self): + pass + # TODO: Someone who understands the second mode added to transform needs to + # write a test here. Maybe close to testMultiTransform but with + # self.Mirrored.TransformMode="Transform body" instead of + # self.Mirrored.TransformMode="Transform tools" + def tearDown(self): #closing doc FreeCAD.closeDocument("PartDesignTestMultiTransform") diff --git a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py index a920a8344a..19a356d648 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py +++ b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py @@ -631,13 +631,14 @@ class TestTopologicalNamingProblem(unittest.TestCase): # Arrange body = self.Doc.addObject('PartDesign::Body', 'Body') sketch = self.Doc.addObject('Sketcher::SketchObject', 'Sketch') - TestSketcherApp.CreateRectangleSketch(sketch, (0, 0), (1, 1)) + TestSketcherApp.CreateRectangleSketch(sketch, (0,0), (1,1)) if body.Shape.ElementMapVersion == "": # Should be '4' as of Mar 2023. return # Act helix = self.Doc.addObject('PartDesign::AdditiveHelix', 'Helix') helix.Profile = sketch - helix.ReferenceAxis = (self.Doc.getObject('Sketch'), ['V_Axis']) + helix.ReferenceAxis = (self.Doc.getObject('Sketch'), ['N_Axis']) + # helix.Mode = 0 body.addObject(sketch) body.addObject(helix) self.Doc.recompute() @@ -646,8 +647,17 @@ class TestTopologicalNamingProblem(unittest.TestCase): self.assertGreaterEqual(body.Shape.childShapes()[0].ElementMapSize, 26) revMap = body.Shape.childShapes()[0].ElementReverseMap self.assertEqual(self.countFacesEdgesVertexes(revMap), (6, 12, 8)) - volume = 9.424696540407776 # TODO: math formula to calc this. - self.assertAlmostEqual(helix.Shape.Volume, volume) + Radius = 0 # Rectangle is on the axis, but wouldn't matter regardless here + Area = Part.Face(sketch.Shape).Area + # General helix formula; not actually used here since devolves to just the + # height in this orientation. + helixLength = (helix.Height.Value / helix.Pitch.Value * + math.sqrt( (math.pi * Radius) ** 2 + helix.Pitch.Value ** 2)) + Volume = Area * helixLength + self.assertAlmostEqual(Area, 1) + self.assertAlmostEqual(helixLength, helix.Height.Value) + self.assertAlmostEqual(helix.Shape.Volume, Volume, 2) + self.assertEqual(body.Shape.ElementMapSize,26) def testPartDesignElementMapPocket(self): # Arrange