From 8dd6863414f2e34ab0272c69393d9fb91ef781ba Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 21 Mar 2024 15:17:32 -0400 Subject: [PATCH 1/2] Toponaming/Part: Move sketcher override of getElementName over --- src/Mod/Sketcher/App/SketchObject.cpp | 44 +++++++++++++++++++++++++++ src/Mod/Sketcher/App/SketchObject.h | 3 ++ 2 files changed, 47 insertions(+) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 6452b8607f..0192bdaf7b 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -306,6 +306,13 @@ void SketchObject::buildShape() // } } +static const char *hasSketchMarker(const char *name) { + static std::string marker(Part::TopoShape::elementMapPrefix()+Part::OpCodes::Sketch); + if (!name) + return nullptr; + return strstr(name,marker.c_str()); +} + int SketchObject::hasConflicts() const { if (lastDoF < 0)// over-constrained sketch @@ -9514,6 +9521,43 @@ void SketchObject::setExpression(const App::ObjectIdentifier& path, } } +std::pair SketchObject::getElementName( + const char *name, ElementNameType type) const +{ + std::pair ret; + if(!name) return ret; + + if(hasSketchMarker(name)) + return Part2DObject::getElementName(name,type); + + const char *mapped = Data::isMappedElement(name); + if(!mapped) { + auto occindex = Part::TopoShape::shapeTypeAndIndex(name); + if (occindex.second) + return Part2DObject::getElementName(name,type); + + Data::IndexedName index = checkSubName(name); + ret.first = convertSubName(index, true); + if(!Data::isMappedElement(ret.first.c_str())) + ret.first.clear(); + index.appendToStringBuffer(ret.second); + return ret; + } + + Data::IndexedName index = checkSubName(name); + if(index) { + index.appendToStringBuffer(ret.second); + ret.first = convertSubName(index, true); + if(type==ElementNameType::Export) { + if(boost::starts_with(ret.second,"Vertex")) + ret.second[0] = 'v'; + else if(boost::starts_with(ret.second,"Edge")) + ret.second[0] = 'e'; + } + } + return ret; +} + Part::TopoShape SketchObject::getEdge(const Part::Geometry *geo, const char *name) const { Part::TopoShape shape(geo->toShape()); diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 1d64977edc..629157faff 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -701,6 +701,9 @@ public: std::string convertSubName(const Data::IndexedName&, bool postfix = true) const; + std::pair getElementName(const char* name, + ElementNameType type) const override; + bool isPerformingInternalTransaction() const { return internaltransaction; From 8d5385fcd2d11bad4b2ac1b0eb374ff502d4f560 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sun, 24 Mar 2024 18:13:21 -0400 Subject: [PATCH 2/2] Clean, enable disabled element map code in sketcher, add tests --- src/App/ComplexGeoData.h | 30 ++++++++++ src/Mod/Sketcher/App/SketchObject.cpp | 64 +++++++++++---------- tests/src/Mod/Sketcher/App/SketchObject.cpp | 30 ++++++++++ 3 files changed, 95 insertions(+), 29 deletions(-) diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index 9fb24abc27..5b0ec19c83 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -230,6 +230,36 @@ public: ElementIDRefs *sid = nullptr, bool copy = false) const; + /** Add a sub-element name mapping. + * + * @param element: the original \c Type + \c Index element name + * @param name: the mapped sub-element name. May or may not start with + * elementMapPrefix(). + * @param sid: in case you use a hasher to hash the element name, pass in + * the string id reference using this parameter. You can have more than one + * string id associated with the same name. + * @param overwrite: if true, it will overwrite existing names + * + * @return Returns the stored mapped element name. + * + * An element can have multiple mapped names. However, a name can only be + * mapped to one element + * + * Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access, + * now you must pass in `long masterTag` explicitly. + */ + MappedName setElementName(const IndexedName& element, + const MappedName& name, + long masterTag, + const ElementIDRefs* sid = nullptr, + bool overwrite = false) { + return _elementMap -> setElementName(element, name, masterTag, sid, overwrite); + } + + bool hasElementMap() { + return _elementMap != nullptr; + } + /** Get mapped element names * * @param element: original element name with \c Type + \c Index diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 0192bdaf7b..5a7931e05d 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -243,7 +243,7 @@ App::DocumentObjectExecReturn* SketchObject::execute() // this is not necessary for sketch representation in edit mode, unless we want to trigger an // update of the objects that depend on this sketch (like pads) - Shape.setValue(solvedSketch.toShape()); + buildShape(); return App::DocumentObject::StdReturn; } @@ -280,30 +280,30 @@ void SketchObject::buildShape() // shapes.push_back(getEdge(geo, convertSubName( // Data::IndexedName::fromConst("ExternalEdge", i-1), false).c_str())); // } - // if(shapes.empty() && vertices.empty()) - // Shape.setValue(Part::TopoShape()); - // else if (vertices.empty()) { - // // Notice here we supply op code Part::OpCodes::Sketch to makEWires(). - // Shape.setValue(Part::TopoShape().makEWires(shapes,Part::OpCodes::Sketch)); - // } else { - // std::vector results; - // if (!shapes.empty()) { - // // This call of makEWires() does not have the op code, in order to - // // avoid duplication. Because we'll going to make a compound (to - // // include the vertices) below with the same op code. - // // - // // Note, that we HAVE TO add the Part::OpCodes::Sketch op code to all - // // geometry exposed through the Shape property, because - // // SketchObject::getElementName() relies on this op code to - // // differentiate geometries that are exposed with those in edit - // // mode. - // auto wires = Part::TopoShape().makEWires(shapes); - // for (const auto &wire : wires.getSubTopoShapes(TopAbs_WIRE)) - // results.push_back(wire); - // } - // results.insert(results.end(), vertices.begin(), vertices.end()); - // Shape.setValue(Part::TopoShape().makECompound(results, Part::OpCodes::Sketch)); - // } + if(shapes.empty() && vertices.empty()) + Shape.setValue(Part::TopoShape()); + else if (vertices.empty()) { + // Notice here we supply op code Part::OpCodes::Sketch to makEWires(). + Shape.setValue(Part::TopoShape().makeElementWires(shapes,Part::OpCodes::Sketch)); + } else { + std::vector results; + if (!shapes.empty()) { + // This call of makeElementWires() does not have the op code, in order to + // avoid duplication. Because we'll going to make a compound (to + // include the vertices) below with the same op code. + // + // Note, that we HAVE TO add the Part::OpCodes::Sketch op code to all + // geometry exposed through the Shape property, because + // SketchObject::getElementName() relies on this op code to + // differentiate geometries that are exposed with those in edit + // mode. + auto wires = Part::TopoShape().makeElementWires(shapes); + for (const auto &wire : wires.getSubTopoShapes(TopAbs_WIRE)) + results.push_back(wire); + } + results.insert(results.end(), vertices.begin(), vertices.end()); + Shape.setValue(Part::TopoShape().makeElementCompound(results, Part::OpCodes::Sketch)); + } } static const char *hasSketchMarker(const char *name) { @@ -9561,8 +9561,14 @@ std::pair SketchObject::getElementName( Part::TopoShape SketchObject::getEdge(const Part::Geometry *geo, const char *name) const { Part::TopoShape shape(geo->toShape()); - // shape.setElementName(Data::IndexedName::fromConst("Edge", 1), - // Data::MappedName::fromRawData(name)); + // Originally in ComplexGeoData::setElementName + // LinkStable/src/App/ComplexGeoData.cpp#L1631 + // No longer possible after map separated in ElementMap.cpp + if ( !shape.hasElementMap() ) { + shape.resetElementMap(std::make_shared()); + } + shape.setElementName(Data::IndexedName::fromConst("Edge", 1), + Data::MappedName::fromRawData(name),0L); TopTools_IndexedMapOfShape vmap; TopExp::MapShapes(shape.getShape(), TopAbs_VERTEX, vmap); std::ostringstream ss; @@ -9574,8 +9580,8 @@ Part::TopoShape SketchObject::getEdge(const Part::Geometry *geo, const char *nam if(getPoint(geo,pos[j]) == pt) { ss.str(""); ss << name << 'v' << static_cast(pos[j]); - // shape.setElementName(Data::IndexedName::fromConst("Vertex", i), - // Data::MappedName::fromRawData(ss.str().c_str())); + shape.setElementName(Data::IndexedName::fromConst("Vertex", i), + Data::MappedName::fromRawData(ss.str().c_str()),0L); break; } } diff --git a/tests/src/Mod/Sketcher/App/SketchObject.cpp b/tests/src/Mod/Sketcher/App/SketchObject.cpp index 7b4e5ecfbb..23c4c30aa8 100644 --- a/tests/src/Mod/Sketcher/App/SketchObject.cpp +++ b/tests/src/Mod/Sketcher/App/SketchObject.cpp @@ -253,3 +253,33 @@ TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionAppl // Assert EXPECT_EQ(std::string("32 °"), getObject()->getConstraintExpression(id)); } + +TEST_F(SketchObjectTest, testGetElementName) +{ + // Arrange + Base::Vector3d p1(0.0, 0.0, 0.0), p2(1.0, 0.0, 0.0); + std::unique_ptr geoline(new Part::GeomLineSegment()); + static_cast(geoline.get())->setPoints(p1, p2); + getObject()->addGeometry(geoline.get()); + getObject()->recomputeFeature(); // or ->execute() + // Act + // unless it's Export, we are really just testing the superclass App::GeoFeature::getElementName + // call. + auto forward_normal_name = + getObject()->getElementName("g39;SKT", App::GeoFeature::ElementNameType::Normal); + auto reverse_normal_name = + getObject()->getElementName("Vertex2", App::GeoFeature::ElementNameType::Normal); + auto reverse_export_name = + getObject()->getElementName("Vertex1", App::GeoFeature::ElementNameType::Export); + auto map = getObject()->Shape.getShape().getElementMap(); + ASSERT_EQ(map.size(), 3); + EXPECT_STREQ(map[0].name.toString().c_str(), "g39;SKT"); + EXPECT_EQ(map[0].index.toString(), "Edge1"); + // Assert + EXPECT_STREQ(forward_normal_name.first.c_str(), ";g39;SKT.Edge1"); + EXPECT_STREQ(forward_normal_name.second.c_str(), "Edge1"); + EXPECT_STREQ(reverse_normal_name.first.c_str(), ";g39v2;SKT.Vertex2"); + EXPECT_STREQ(reverse_normal_name.second.c_str(), "Vertex2"); + EXPECT_STREQ(reverse_export_name.first.c_str(), ";g39v1;SKT.Vertex1"); + EXPECT_STREQ(reverse_export_name.second.c_str(), "Vertex1"); +}