Toponaming: Fix transformed; abstract index element name generation
This commit is contained in:
@@ -91,4 +91,16 @@ bool Data::hasMissingElement(const char *subname) {
|
||||
|
||||
const char *Data::hasMappedElementName(const char *subname) {
|
||||
return isMappedElement(findElementName(subname));
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() << "<StringHasher saveall=\"" << _hashes->SaveAll
|
||||
<< "\" threshold=\"" << _hashes->Threshold << "\"";
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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<TopoShape> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user