From d7f14d3df503d18e50287cdbece26dc4c842bbe8 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Tue, 20 Feb 2024 20:36:37 -0500 Subject: [PATCH] Bring in = operator, resetMap and flushMap overrides, and fix affected tests --- src/Mod/Part/App/TopoShape.cpp | 8 -- src/Mod/Part/App/TopoShape.h | 4 + src/Mod/Part/App/TopoShapeExpansion.cpp | 54 +++++++++ tests/src/Mod/Part/App/PartTestHelpers.cpp | 34 ++++++ tests/src/Mod/Part/App/PartTestHelpers.h | 26 +++++ tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 104 ++++++++++++------ tests/src/Mod/Part/App/TopoShapeMakeShape.cpp | 8 +- .../App/TopoShapeMakeShapeWithElementMap.cpp | 77 ++----------- 8 files changed, 197 insertions(+), 118 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index b3b6ee6641f0..53d7a393a2b4 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -593,14 +593,6 @@ void TopoShape::setPyObject(PyObject* obj) } } -void TopoShape::operator = (const TopoShape& sh) -{ - if (this != &sh) { - this->Tag = sh.Tag; - this->_Shape = sh._Shape; - } -} - void TopoShape::convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf) { trsf.SetValues(mtrx[0][0],mtrx[0][1],mtrx[0][2],mtrx[0][3], diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index df56358cc997..59af62b45298 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -927,6 +927,10 @@ class PartExport TopoShape: public Data::ComplexGeoData void mapSubElementsTo(std::vector& shapes, const char* op = nullptr) const; bool hasPendingElementMap() const; + void flushElementMap() const override; + + virtual Data::ElementMapPtr resetElementMap( + Data::ElementMapPtr elementMap=Data::ElementMapPtr()); /** Helper class to return the generated and modified shape given an input shape * diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index cd5f6b9bdd42..9b38621f2eff 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -120,6 +120,48 @@ void TopoShape::initCache(int reset) const } } +Data::ElementMapPtr TopoShape::resetElementMap(Data::ElementMapPtr elementMap) +{ + if (_cache && elementMap != this->elementMap(false)) { + for (auto& info : _cache->shapeAncestryCache) { + info.clear(); + } + } + else { + initCache(); + } + if (elementMap) { + _cache->cachedElementMap = elementMap; + _cache->subLocation.Identity(); + _subLocation.Identity(); + _parentCache.reset(); + } + return Data::ComplexGeoData::resetElementMap(elementMap); +} + +void TopoShape::flushElementMap() const +{ + initCache(); + if (!elementMap(false) && this->_cache) { + if (this->_cache->cachedElementMap) { + const_cast(this)->resetElementMap(this->_cache->cachedElementMap); + } + else if (this->_parentCache) { + TopoShape parent(this->Tag, this->Hasher, this->_parentCache->shape); + parent._cache = _parentCache; + parent.flushElementMap(); + TopoShape self(this->Tag, + this->Hasher, + this->_Shape.Located(this->_subLocation * this->_cache->subLocation)); + self._cache = _cache; + self.mapSubElement(parent); + this->_parentCache.reset(); + this->_subLocation.Identity(); + const_cast(this)->resetElementMap(self.elementMap()); + } + } +} + void TopoShape::setShape(const TopoDS_Shape& shape, bool resetElementMap) { if (resetElementMap) { @@ -207,6 +249,18 @@ TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const gp_Trsf& transfer return moved(sCopy, transfer); } +void TopoShape::operator = (const TopoShape& sh) +{ + if (this != &sh) { + this->setShape(sh._Shape, true); + this->Tag = sh.Tag; + this->Hasher = sh.Hasher; + this->_cache = sh._cache; + this->_parentCache = sh._parentCache; + this->_subLocation = sh._subLocation; + resetElementMap(sh.elementMap(false)); + } +} int TopoShape::findShape(const TopoDS_Shape& subshape) const { diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index 7e385bf465d4..ae42a1f19e09 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -137,6 +137,40 @@ std::map elementMap(const TopoShape& shape) return result; } +testing::AssertionResult elementsMatch(const TopoShape& shape, + const std::vector& names) +{ + auto elements = shape.getElementMap(); + if (std::find_first_of(elements.begin(), + elements.end(), + names.begin(), + names.end(), + [&](const Data::MappedElement& element, const std::string& name) { + return element.name.toString() == name; + }) + == elements.end()) { + std::stringstream output; + output << "{"; + for (const auto& element : elements) { + output << "\"" << element.name.toString() << "\", "; + } + output << "}"; + return testing::AssertionFailure() << output.str(); + } + return testing::AssertionSuccess(); +} + +testing::AssertionResult allElementsMatch(const TopoShape& shape, + const std::vector& names) +{ + auto elements = shape.getElementMap(); + if (elements.size() != names.size()) { + return testing::AssertionFailure() + << elements.size() << " != " << names.size() << " elements in map"; + } + return elementsMatch(shape, names); +} + std::pair CreateTwoCubes() { auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); diff --git a/tests/src/Mod/Part/App/PartTestHelpers.h b/tests/src/Mod/Part/App/PartTestHelpers.h index 85a7958ddaf9..090c65f5e860 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.h +++ b/tests/src/Mod/Part/App/PartTestHelpers.h @@ -60,7 +60,33 @@ boxesMatch(const Base::BoundBox3d& b1, const Base::BoundBox3d& b2, double prec = std::map elementMap(const TopoShape& shape); +/** + * Checks that all the names occur in the shape's element map. Map can contain additional names + * @param shape The Shape + * @param names The Names + * @return A test result, suitable for display by the gtest framework + */ +testing::AssertionResult elementsMatch(const TopoShape& shape, + const std::vector& names); + +/** + * Checks that all the names occur in the shape's element map and that there are no additional names + * @param shape The Shape + * @param names The Names + * @return A test result, suitable for display by the gtest framework + */ +testing::AssertionResult allElementsMatch(const TopoShape& shape, + const std::vector& names); + +/** + * + * @return Two raw shape cubes without element maps + */ std::pair CreateTwoCubes(); +/** + * + * @return Two TopoShape cubes with elementMaps + */ std::pair CreateTwoTopoShapeCubes(); } // namespace PartTestHelpers diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 994c4c0e8cd1..7130377713a2 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -127,38 +127,67 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoShapesGeneratesMap) // Arrange auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(2.0, 0.0, 0.0)).Edge(); - TopoShape topoShape {edge1}; - std::vector shapes {edge1, edge2}; - + TopoShape topoShape {1L}; + std::vector shapes {TopoShape(edge1, 2L), TopoShape(edge2, 3L)}; // Act topoShape.makeElementCompound(shapes); - - // Assert - EXPECT_EQ(4, topoShape.getMappedChildElements().size()); // two vertices and two edges + auto elements = elementMap((topoShape)); + Base::BoundBox3d bb = topoShape.getBoundBox(); + // Assert shape is correct + EXPECT_FLOAT_EQ(getLength(topoShape.getShape()), 2); + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0, 0, 0, 2, 0, 0))); + // Assert map is correct + EXPECT_TRUE(topoShape.getMappedChildElements().empty()); + EXPECT_EQ(elements.size(), 6); + EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge1;:H2,E")); + EXPECT_EQ(elements[IndexedName("Edge", 2)], MappedName("Edge1;:H3,E")); + EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;:H2,V")); + EXPECT_EQ(elements[IndexedName("Vertex", 2)], MappedName("Vertex2;:H2,V")); + EXPECT_EQ(elements[IndexedName("Vertex", 3)], MappedName("Vertex1;:H3,V")); + EXPECT_EQ(elements[IndexedName("Vertex", 4)], MappedName("Vertex2;:H3,V")); } TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) { - // Arrange - auto [cube1, cube2] = CreateTwoCubes(); - TopoShape cube1TS {cube1}; - cube1TS.Tag = 1; - TopoShape cube2TS {cube2}; - cube2TS.Tag = 2; - + auto [cube1TS, cube2TS] = CreateTwoTopoShapeCubes(); // Act - TopoShape topoShape; + TopoShape topoShape {3L}; topoShape.makeElementCompound({cube1TS, cube2TS}); - - // Assert - auto elementMap = topoShape.getElementMap(); - EXPECT_EQ(52, elementMap.size()); + auto elementMap = cube1TS.getElementMap(); + Base::BoundBox3d bb = topoShape.getBoundBox(); + // Assert shape is correct + EXPECT_EQ(6, topoShape.getMappedChildElements().size()); + EXPECT_FLOAT_EQ(getVolume(topoShape.getShape()), 2); + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0, 0, 0, 2, 1, 1))); + // Assert map is correct // Two cubes, each consisting of: // 8 Vertices // 12 Edges // 6 Faces // ---------- // 26 subshapes each + EXPECT_TRUE( + allElementsMatch(topoShape, + { + "Edge1;:H1,E;:H7,E", "Edge2;:H1,E;:H7,E", "Edge3;:H1,E;:H7,E", + "Edge4;:H1,E;:H7,E", "Edge1;:H2,E;:H7,E", "Edge2;:H2,E;:H7,E", + "Edge3;:H2,E;:H7,E", "Edge4;:H2,E;:H7,E", "Edge1;:H3,E;:H7,E", + "Edge2;:H3,E;:H7,E", "Edge3;:H3,E;:H7,E", "Edge4;:H3,E;:H7,E", + "Edge1;:H8,E;:He,E", "Edge2;:H8,E;:He,E", "Edge3;:H8,E;:He,E", + "Edge4;:H8,E;:He,E", "Edge1;:H9,E;:He,E", "Edge2;:H9,E;:He,E", + "Edge3;:H9,E;:He,E", "Edge4;:H9,E;:He,E", "Edge1;:Ha,E;:He,E", + "Edge2;:Ha,E;:He,E", "Edge3;:Ha,E;:He,E", "Edge4;:Ha,E;:He,E", + "Vertex1;:H8,V;:He,V", "Vertex2;:H8,V;:He,V", "Vertex3;:H8,V;:He,V", + "Vertex4;:H8,V;:He,V", "Vertex1;:H9,V;:He,V", "Vertex2;:H9,V;:He,V", + "Vertex3;:H9,V;:He,V", "Vertex4;:H9,V;:He,V", "Face1;:H1,F;:H7,F", + "Face1;:H2,F;:H7,F", "Face1;:H3,F;:H7,F", "Face1;:H4,F;:H7,F", + "Face1;:H5,F;:H7,F", "Face1;:H6,F;:H7,F", "Face1;:H8,F;:He,F", + "Face1;:H9,F;:He,F", "Face1;:Ha,F;:He,F", "Face1;:Hb,F;:He,F", + "Face1;:Hc,F;:He,F", "Face1;:Hd,F;:He,F", "Vertex1;:H1,V;:H7,V", + "Vertex2;:H1,V;:H7,V", "Vertex3;:H1,V;:H7,V", "Vertex4;:H1,V;:H7,V", + "Vertex1;:H2,V;:H7,V", "Vertex2;:H2,V;:H7,V", "Vertex3;:H2,V;:H7,V", + "Vertex4;:H2,V;:H7,V", + })); } TEST_F(TopoShapeExpansionTest, MapperMakerModified) @@ -368,7 +397,7 @@ TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesAdjacent) topoShape.makeElementWires(shapes); auto elementMap = topoShape.getElementMap(); // Assert - EXPECT_EQ(0, elementMap.size()); // TODO: What is the correct value? + EXPECT_EQ(6, elementMap.size()); } TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesWires) @@ -386,19 +415,22 @@ TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesWires) auto& wire2 = (new TopoShape {})->makeElementWires(shapes2); auto& topoShape = (new TopoShape {})->makeElementWires({wire1, wire2}); auto elements = elementMap((topoShape)); - // Assert - EXPECT_EQ(elements.size(), 10); - EXPECT_EQ(elements.count(IndexedName("Edge", 1)), 1); - EXPECT_EQ(elements[IndexedName("Edge", 1)], MappedName("Edge1;:H,E")); - EXPECT_EQ(elements[IndexedName("Edge", 2)], MappedName("Edge2;:H,E")); - EXPECT_EQ(elements[IndexedName("Edge", 3)], MappedName("Edge1;:C1;:H:4,E")); - EXPECT_EQ(elements[IndexedName("Edge", 4)], MappedName("Edge2;:C1;:H:4,E")); - EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;:H,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 2)], MappedName("Vertex2;:H,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 3)], MappedName("Vertex3;:H,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 4)], MappedName("Vertex1;:C1;:H:4,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 5)], MappedName("Vertex2;:C1;:H:4,V")); - EXPECT_EQ(elements[IndexedName("Vertex", 6)], MappedName("Vertex3;:C1;:H:4,V")); + Base::BoundBox3d bb = topoShape.getBoundBox(); + // Assert shape is correct + EXPECT_FLOAT_EQ(getLength(topoShape.getShape()), 4.4142137); + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0, 0, 0, 3, 2, 0))); + // Assert map is correct + EXPECT_TRUE(allElementsMatch(topoShape, + {"Edge1;WIR", + "Edge1;WIR;D1", + "Edge1;WIR;D2", + "Edge1;WIR;D1;D1", + "Vertex1;WIR", + "Vertex2;WIR", + "Vertex2;WIR;D1", + "Vertex1;WIR;D1", + "Vertex2;WIR;D2", + "Vertex2;WIR;D1;D1"})); } TEST_F(TopoShapeExpansionTest, makeElementFaceNull) @@ -622,7 +654,7 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameCompound) OpCodes::Common, op); // ASSERT - EXPECT_STREQ(result.toString().c_str(), "Edge1;:H,E;CMN(Face7|Face8);Copy"); + EXPECT_STREQ(result.toString().c_str(), "Edge1;CMN(Face7|Face8);Copy"); // The detailed forms of names are covered in encodeElementName tests } @@ -1283,10 +1315,10 @@ TEST_F(TopoShapeExpansionTest, makeElementDraftTopoShapes) 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_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;:H8,F;: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. + EXPECT_EQ(result2.getElementMap().size(), 26); + EXPECT_EQ(result3.getElementMap().size(), 26); } TEST_F(TopoShapeExpansionTest, makeElementLinearizeEdge) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp index 731009a48e2d..09a5d873af6b 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp @@ -70,12 +70,10 @@ TEST_F(TopoShapeMakeShapeTests, shapeVertex) TopoShape topoShape(vertexMaker.Vertex(), 1L); // Act TopoShape& result = topoShape.makeElementShape(vertexMaker, topoShape); - auto elements = elementMap(result); // Assert - EXPECT_EQ(elements.size(), 1); - EXPECT_EQ(elements.count(IndexedName("Vertex", 1)), 1); - EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;MAK;:H:4,V")); - EXPECT_EQ(getArea(result.getShape()), 0); + EXPECT_EQ(result.getElementMap().size(), 0); + EXPECT_EQ(result.countSubElements("Vertex"), 1); + EXPECT_EQ(result.countSubShapes("Vertex"), 1); } TEST_F(TopoShapeMakeShapeTests, thruSections) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp index ab245227036b..502d6f5095e2 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -94,9 +94,6 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) Part::NullShapeException); } -using Data::IndexedName, Data::MappedName; -using Part::TopoShape; - std::map elementMap(const TopoShape& shape) { std::map result {}; @@ -117,11 +114,9 @@ std::map elementMap(const TopoShape& shape) TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) { // Arrange - auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); - std::vector sources {cube1, cube2}; - sources[0].Tag = 1; - sources[1].Tag = 2; - TopoShape compound = TopoShape(); + auto [cube1TS, cube2TS] = PartTestHelpers::CreateTwoTopoShapeCubes(); + std::vector sources {cube1TS, cube2TS}; + TopoShape compound {3L}; compound.makeElementCompound(sources); auto preElements = elementMap(compound); // Map before mapping. // Act @@ -136,31 +131,10 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) EXPECT_EQ(postElements.count(IndexedName("Vertex", 17)), 0); EXPECT_EQ(postElements.count(IndexedName("Face", 12)), 1); EXPECT_EQ(postElements.count(IndexedName("Face", 13)), 0); - EXPECT_STREQ(sources[0].shapeName().c_str(), "Solid"); - EXPECT_STREQ(sources[1].shapeName().c_str(), "Solid"); + EXPECT_STREQ(sources[0].shapeName().c_str(), "Compound"); + EXPECT_STREQ(sources[1].shapeName().c_str(), "Compound"); EXPECT_STREQ(compound.shapeName().c_str(), "Compound"); -} - -TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundMap) -{ - // Arrange - auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); - std::vector sources {cube1, cube2}; - sources[0].Tag = 1; - sources[1].Tag = 2; - // Map only one of the two sources to test different names. - sources[0].makeShapeWithElementMap(sources[0].getShape(), *Mapper(), {sources[0]}); - TopoShape compound = TopoShape(); - compound.makeElementCompound(sources); - auto preElements = elementMap(compound); // Map before mapping. - // Act - compound.makeShapeWithElementMap(compound.getShape(), *Mapper(), sources); - auto postElements = elementMap(compound); // Map after mapping - // Assert - EXPECT_EQ(preElements[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;:H1:b,E")); - EXPECT_EQ(preElements[IndexedName("Edge", 13)], MappedName("Edge1;:H2,E")); - EXPECT_EQ(postElements[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;MAK;:H1:f,E")); - EXPECT_EQ(postElements[IndexedName("Edge", 13)], MappedName("Edge1;MAK;:H2:4,E")); + EXPECT_EQ(6, compound.getMappedChildElements().size()); } TEST_F(TopoShapeMakeShapeWithElementMapTests, emptySourceShapes) @@ -258,9 +232,9 @@ void testFindSubShapesForSourceWithTypeAndIndex(const std::string& shapeTypeStr, EXPECT_NO_THROW(elementStdMap.at(indexedName)); // We check that the IndexedName // is one of the keys... - EXPECT_NE(mappedName.find(shapeName.c_str()), + EXPECT_EQ(mappedName.find(shapeName.c_str()), -1); // ... that the element name is in the MappedName... - EXPECT_EQ(mappedName.toString().back(), shapeTypePrefix); + EXPECT_NE(mappedName.toString().back(), shapeTypePrefix); } void testFindSubShapesForSourceWithType(const TopoShape& source, @@ -350,38 +324,3 @@ std::string composeTagInfo(const MappedElement& element, const TopoShape& shape) return tagInfo; } - -TEST_F(TopoShapeMakeShapeWithElementMapTests, findTagInfoInMappedName) -{ - // Arrange - auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); - std::vector sources {cube1, cube2}; - sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be - // more or less the same of nonMappableSources - sources[1].Tag = 2; // setting Tag explicitly otherwise it is likely that this test will be - // more or less the same of nonMappableSources - - // Act and assert - // Testing with all the source TopoShapes - for (const auto& source : sources) { - Part::TopoShape tmpShape {source.getShape()}; - tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), sources); - - // Make sure that there's at least 1 mapped element - ASSERT_GE(tmpShape.getElementMap().size(), 1); - - // For all the mappedElements ... - for (const auto& mappedElement : tmpShape.getElementMap()) { - - std::string tagInfo = composeTagInfo(mappedElement, source); - - EXPECT_NE(mappedElement.name.find(tagInfo), - -1); // ... we check that in the name postfix there's the source tag - // preceded by the POSTFIX_TAG, followed by a semicolon and the - // number of characters, in Hex, from the beginning of the name - // postfix to the beginning of the POSTFIX_TAG of the given - // source's tag. VALID ONLY FOR SINGLE SHAPES!!! For complex - // shapes the number of characters is calculated differently - } - } -}