From 7d344e28183a53a9942e4add1fb704524e1c7290 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 18 Jul 2024 17:16:04 -0400 Subject: [PATCH 1/2] Toponaming: Transfer in SketchObject::InternalShape --- src/Mod/Sketcher/App/SketchObject.cpp | 141 ++++++++++++++++++++++++-- src/Mod/Sketcher/App/SketchObject.h | 19 +++- 2 files changed, 146 insertions(+), 14 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index f63db00b59..5e8c45ed75 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -82,6 +82,7 @@ #include #include #include +#include #include "SketchObject.h" #include "SketchObjectPy.h" @@ -125,6 +126,14 @@ SketchObject::SketchObject() (App::PropertyType)(App::Prop_Output | App::Prop_ReadOnly | App::Prop_Hidden), "Sketch is fully constrained"); + ADD_PROPERTY(InternalShape, + (Part::TopoShape())); + ADD_PROPERTY_TYPE(MakeInternals, + (false), + "Internal Geometry", + App::Prop_None, + "Make internal geometry, e.g. split intersecting edges, face of closed wires."); + Geometry.setOrderRelevant(true); allowOtherBody = true; @@ -173,6 +182,8 @@ SketchObject::SketchObject() internaltransaction = false; managedoperation = false; + + registerElementCache(internalPrefix(), &InternalShape); } SketchObject::~SketchObject() @@ -186,6 +197,17 @@ SketchObject::~SketchObject() delete analyser; } +void SketchObject::setupObject() +{ + ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Sketcher"); +// ArcFitTolerance.setValue(hGrpp->GetFloat("ArcFitTolerance", Precision::Confusion()*10.0)); +// ExternalBSplineMaxDegree.setValue(hGrpp->GetInt("ExternalBSplineMaxDegree", 5)); +// ExternalBSplineTolerance.setValue(hGrpp->GetFloat("ExternalBSplineTolerance", 1e-4)); + MakeInternals.setValue(hGrpp->GetBool("MakeInternals", false)); + inherited::setupObject(); +} + short SketchObject::mustExecute() const { if (Geometry.isTouched()) @@ -309,7 +331,11 @@ void SketchObject::buildShape() shapes.push_back(getEdge(geo, convertSubName( Data::IndexedName::fromConst("ExternalEdge", i-1), false).c_str())); } + + internalElementMap.clear(); + if(shapes.empty() && vertices.empty()) { + InternalShape.setValue(Part::TopoShape()); Shape.setValue(Part::TopoShape()); return; } @@ -337,9 +363,89 @@ void SketchObject::buildShape() result.makeElementCompound(results, Part::OpCodes::Sketch); } result.Tag = getID(); + InternalShape.setValue(buildInternals(result.located(TopLoc_Location()))); + // Must set Shape property after InternalShape so that + // GeoFeature::updateElementReference() can run properly on change of Shape + // property, because some reference may pointing to the InternalShape Shape.setValue(result); } +const std::map SketchObject::getInternalElementMap() const +{ + if (!internalElementMap.empty() || !MakeInternals.getValue()) + return internalElementMap; + + auto internalShape = InternalShape.getShape(); + auto shape = Shape.getShape().located(TopLoc_Location()); + if (!internalShape.isNull() && !shape.isNull()) { + std::vector names; + std::string prefix; + const std::array types = {TopAbs_VERTEX, TopAbs_EDGE}; + for (const auto &type : types) { + prefix = internalPrefix() + Part::TopoShape::shapeName(type); + std::size_t len = prefix.size(); + int i=0; + for (const auto &v : internalShape.getSubTopoShapes(type)) { + ++i; + shape.findSubShapesWithSharedVertex(v, &names, Data::SearchOption::CheckGeometry + |Data::SearchOption::SingleResult); + if (names.empty()) + continue; + prefix += std::to_string(i); + internalElementMap[prefix] = names.front(); + internalElementMap[names.front()] = prefix; + prefix.resize(len); + names.clear(); + } + } + } + return internalElementMap; +} + +Part::TopoShape SketchObject::buildInternals(const Part::TopoShape &edges) const +{ + if (!MakeInternals.getValue()) + return Part::TopoShape(); + + try { + Part::WireJoiner joiner; +// joiner.setTolerance(InternalTolerance.getValue()); + joiner.setTightBound(true); + joiner.setMergeEdges(true); + joiner.addShape(edges); + Part::TopoShape result(getID(), getDocument()->getStringHasher()); + if (!joiner.Shape().IsNull()) { + joiner.getResultWires(result, "SKF"); + + // NOTE: we set minElementNames to 2 (i.e to use at least two + // unused edge name to construct face name) in order to reduce the + // chance of face jumping. + result = result.makeElementFace(result.getSubTopoShapes(TopAbs_WIRE), + /*op*/"", + /*maker*/"Part::FaceMakerRing", + /*pln*/nullptr +#if 1 +// /*minElementNames (revert to 1 for now, see how it fares)*/1 +#else + /*minElementNames*/2 +#endif + ); + } + Part::TopoShape openWires(getID(), getDocument()->getStringHasher()); + joiner.getOpenWires(openWires, "SKF"); + if (openWires.isNull()) + return result; + if (result.isNull()) + return openWires; + return result.makeElementCompound({result, openWires}); + } catch (Base::Exception &e) { + FC_WARN("Failed to make face for sketch: " << e.what()); + } catch (Standard_Failure &e) { + FC_WARN("Failed to make face for sketch: " << e.GetMessageString()); + } + return Part::TopoShape(); +} + static const char *hasSketchMarker(const char *name) { static std::string marker(Part::TopoShape::elementMapPrefix()+Part::OpCodes::Sketch); if (!name) @@ -9514,11 +9620,11 @@ App::DocumentObject *SketchObject::getSubObject( if (auto realType = convertInternalName(indexedName.getType())) { if (realType[0] == '\0') - subshape = Shape.getShape(); + subshape = InternalShape.getShape(); else { auto shapeType = Part::TopoShape::shapeType(realType, true); if (shapeType != TopAbs_SHAPE) - subshape = Shape.getShape().getSubTopoShape(shapeType, indexedName.getIndex(), true); + subshape = InternalShape.getShape().getSubTopoShape(shapeType, indexedName.getIndex(), true); } if (subshape.isNull()) return nullptr; @@ -9662,13 +9768,12 @@ App::ElementNamePair SketchObject::getElementName( if (auto realName = convertInternalName(ret.oldName.c_str())) { Data::MappedElement mappedElement; (void)realName; -// Todo: Do we need to add the InternalShape? -// if (mapped) -// mappedElement = InternalShape.getShape().getElementName(name); -// else if (type == ElementNameType::Export) -// ret.newName = getExportElementName(InternalShape.getShape(), realName).first; -// else -// mappedElement = InternalShape.getShape().getElementName(realName); + if (mapped) + mappedElement = InternalShape.getShape().getElementName(name); + else if (type == ElementNameType::Export) + ret.first = getExportElementName(InternalShape.getShape(), realName).first; + else + mappedElement = InternalShape.getShape().getElementName(realName); if (mapped || type != ElementNameType::Export) { if (mappedElement.index) { @@ -9832,9 +9937,25 @@ bool SketchObject::geoIdFromShapeType(const Data::IndexedName & indexedName, return true; } -std::string SketchObject::convertSubName(const Data::IndexedName & indexedName, bool postfix) const +std::string SketchObject::convertSubName(const char *subname, bool postfix) const { + return convertSubName(checkSubName(subname), postfix); +} + +std::string SketchObject::convertSubName(const Data::IndexedName & indexedName, bool postfix) const{ std::ostringstream ss; + if (auto realType = convertInternalName(indexedName.getType())) { + auto mapped = InternalShape.getShape().getMappedName( + Data::IndexedName::fromConst(realType, indexedName.getIndex())); + if (!mapped) { + if (postfix) + ss << indexedName; + } else if (postfix) + ss << Data::ComplexGeoData::elementMapPrefix() << mapped << '.' << indexedName; + else + ss << mapped; + return ss.str(); + } int geoId; PointPos posId; if(!geoIdFromShapeType(indexedName,geoId,posId)) { diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index aafbca8ca9..3279273e68 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -46,6 +46,7 @@ class SketchAnalysis; class SketcherExport SketchObject: public Part::Part2DObject { + typedef Part::Part2DObject inherited; PROPERTY_HEADER_WITH_OVERRIDE(Sketcher::SketchObject); public: @@ -66,6 +67,8 @@ public: Sketcher::PropertyConstraintList Constraints; App ::PropertyLinkSubList ExternalGeometry; App ::PropertyBool FullyConstrained; + Part ::PropertyPartShape InternalShape; + App ::PropertyBool MakeInternals; /** @name methods override Feature */ //@{ short mustExecute() const override; @@ -78,6 +81,7 @@ public: { return "SketcherGui::ViewProviderSketch"; } + void setupObject() override; //@} /** SketchObject can work in two modes: Recompute Mode and noRecomputes Mode @@ -695,10 +699,7 @@ public: return geoIdFromShapeType(shapetype, geoId, posId); } - std::string convertSubName(const char* subname, bool postfix = true) const - { - return convertSubName(checkSubName(subname), postfix); - } + std::string convertSubName(const char* subname, bool postfix = true) const; std::string convertSubName(const std::string& subname, bool postfix = true) const { @@ -769,6 +770,15 @@ public: // Validation routines std::vector getOpenVertices() const; + // Signaled when solver has done update + boost::signals2::signal signalSolverUpdate; + boost::signals2::signal signalElementsChanged; + + Part::TopoShape buildInternals(const Part::TopoShape& edges) const; + + /// Get a map from internal element to the same geometry in normal shape + const std::map getInternalElementMap() const; + public: // geometry extension functionalities for single element sketch object user convenience int setGeometryId(int GeoId, long id); int getGeometryId(int GeoId, long& id) const; @@ -920,6 +930,7 @@ private: // indicates whether changes to properties are the deed of SketchObject or not (for input // validation) bool managedoperation; + mutable std::map internalElementMap; }; inline int SketchObject::initTemporaryMove(int geoId, PointPos pos, bool fine /*=true*/) From 5c80e553a8c835f87f485af0c46d0e391b826fe7 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sun, 21 Jul 2024 22:28:59 -0400 Subject: [PATCH 2/2] Cleanup ElementNamePair member, formatting --- src/Mod/Sketcher/App/SketchObject.cpp | 65 +++++++++++---------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 5e8c45ed75..69cd293dea 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -201,9 +201,6 @@ void SketchObject::setupObject() { ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher"); -// ArcFitTolerance.setValue(hGrpp->GetFloat("ArcFitTolerance", Precision::Confusion()*10.0)); -// ExternalBSplineMaxDegree.setValue(hGrpp->GetInt("ExternalBSplineMaxDegree", 5)); -// ExternalBSplineTolerance.setValue(hGrpp->GetFloat("ExternalBSplineTolerance", 1e-4)); MakeInternals.setValue(hGrpp->GetBool("MakeInternals", false)); inherited::setupObject(); } @@ -402,42 +399,31 @@ const std::map SketchObject::getInternalElementMap() co return internalElementMap; } -Part::TopoShape SketchObject::buildInternals(const Part::TopoShape &edges) const -{ +Part::TopoShape SketchObject::buildInternals(const Part::TopoShape &edges) const { if (!MakeInternals.getValue()) return Part::TopoShape(); try { Part::WireJoiner joiner; -// joiner.setTolerance(InternalTolerance.getValue()); joiner.setTightBound(true); joiner.setMergeEdges(true); joiner.addShape(edges); Part::TopoShape result(getID(), getDocument()->getStringHasher()); if (!joiner.Shape().IsNull()) { joiner.getResultWires(result, "SKF"); - - // NOTE: we set minElementNames to 2 (i.e to use at least two - // unused edge name to construct face name) in order to reduce the - // chance of face jumping. result = result.makeElementFace(result.getSubTopoShapes(TopAbs_WIRE), - /*op*/"", - /*maker*/"Part::FaceMakerRing", - /*pln*/nullptr -#if 1 -// /*minElementNames (revert to 1 for now, see how it fares)*/1 -#else - /*minElementNames*/2 -#endif - ); + /*op*/"", + /*maker*/"Part::FaceMakerRing", + /*pln*/nullptr + ); } Part::TopoShape openWires(getID(), getDocument()->getStringHasher()); joiner.getOpenWires(openWires, "SKF"); if (openWires.isNull()) - return result; + return result; // No open wires, return either face or empty toposhape if (result.isNull()) - return openWires; - return result.makeElementCompound({result, openWires}); + return openWires; // No face, but we have open wires to return as a shape + return result.makeElementCompound({result, openWires}); // Compound and return both } catch (Base::Exception &e) { FC_WARN("Failed to make face for sketch: " << e.what()); } catch (Standard_Failure &e) { @@ -9771,7 +9757,7 @@ App::ElementNamePair SketchObject::getElementName( if (mapped) mappedElement = InternalShape.getShape().getElementName(name); else if (type == ElementNameType::Export) - ret.first = getExportElementName(InternalShape.getShape(), realName).first; + ret.newName = getExportElementName(InternalShape.getShape(), realName).newName; else mappedElement = InternalShape.getShape().getElementName(realName); @@ -9937,12 +9923,11 @@ bool SketchObject::geoIdFromShapeType(const Data::IndexedName & indexedName, return true; } -std::string SketchObject::convertSubName(const char *subname, bool postfix) const -{ +std::string SketchObject::convertSubName(const char *subname, bool postfix) const { return convertSubName(checkSubName(subname), postfix); } -std::string SketchObject::convertSubName(const Data::IndexedName & indexedName, bool postfix) const{ +std::string SketchObject::convertSubName(const Data::IndexedName &indexedName, bool postfix) const { std::ostringstream ss; if (auto realType = convertInternalName(indexedName.getType())) { auto mapped = InternalShape.getShape().getMappedName( @@ -9958,40 +9943,40 @@ std::string SketchObject::convertSubName(const Data::IndexedName & indexedName, } int geoId; PointPos posId; - if(!geoIdFromShapeType(indexedName,geoId,posId)) { + if (!geoIdFromShapeType(indexedName, geoId, posId)) { ss << indexedName; return ss.str(); } - if(geoId == Sketcher::GeoEnum::HAxis || - geoId == Sketcher::GeoEnum::VAxis || - geoId == Sketcher::GeoEnum::RtPnt) { + if (geoId == Sketcher::GeoEnum::HAxis || + geoId == Sketcher::GeoEnum::VAxis || + geoId == Sketcher::GeoEnum::RtPnt) { if (postfix) ss << Data::ELEMENT_MAP_PREFIX; ss << indexedName; - if(postfix) - ss << '.' << indexedName; + if (postfix) + ss << '.' << indexedName; return ss.str(); } auto geo = getGeometry(geoId); - if(!geo) { + if (!geo) { std::string res = indexedName.toString(); return res; } if (postfix) ss << Data::ELEMENT_MAP_PREFIX; - ss << (geoId>=0?'g':'e') << GeometryFacade::getFacade(geo)->getId(); - if(posId!=PointPos::none) + ss << (geoId >= 0 ? 'g' : 'e') << GeometryFacade::getFacade(geo)->getId(); + if (posId != PointPos::none) ss << 'v' << static_cast(posId); - if(postfix) { + if (postfix) { // rename Edge to edge, and Vertex to vertex to avoid ambiguous of // element mapping of the public shape and internal geometry. if (indexedName.getIndex() <= 0) ss << '.' << indexedName; - else if(boost::starts_with(indexedName.getType(),"Edge")) - ss << ".e" << (indexedName.getType()+1) << indexedName.getIndex(); - else if(boost::starts_with(indexedName.getType(),"Vertex")) - ss << ".v" << (indexedName.getType()+1) << indexedName.getIndex(); + else if (boost::starts_with(indexedName.getType(), "Edge")) + ss << ".e" << (indexedName.getType() + 1) << indexedName.getIndex(); + else if (boost::starts_with(indexedName.getType(), "Vertex")) + ss << ".v" << (indexedName.getType() + 1) << indexedName.getIndex(); else ss << '.' << indexedName; }