diff --git a/src/Mod/Part/App/FeatureChamfer.cpp b/src/Mod/Part/App/FeatureChamfer.cpp index 1ba61d65f3..d6393040eb 100644 --- a/src/Mod/Part/App/FeatureChamfer.cpp +++ b/src/Mod/Part/App/FeatureChamfer.cpp @@ -53,7 +53,7 @@ App::DocumentObjectExecReturn *Chamfer::execute() TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace; TopExp::MapShapesAndAncestors(baseShape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace); TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfEdges); - +#ifndef FC_USE_TNP_FIX std::vector values = Edges.getValues(); for (const auto & value : values) { int id = value.edgeid; @@ -89,6 +89,36 @@ App::DocumentObjectExecReturn *Chamfer::execute() prop.touch(); return App::DocumentObject::StdReturn; +#else + const auto &vals = EdgeLinks.getSubValues(); + const auto &subs = EdgeLinks.getShadowSubs(); + if(subs.size()!=(size_t)Edges.getSize()) + return new App::DocumentObjectExecReturn("Edge link size mismatch"); + size_t i=0; + for(const auto &info : Edges.getValues()) { + auto &sub = subs[i]; + auto &ref = sub.first.size()?sub.first:vals[i]; + ++i; + TopoDS_Shape edge; + try { + edge = baseTopoShape.getSubShape(ref.c_str()); + }catch(...){} + if(edge.IsNull()) + return new App::DocumentObjectExecReturn("Invalid edge link"); + double radius1 = info.radius1; + double radius2 = info.radius2; + const TopoDS_Face& face = TopoDS::Face(mapEdgeFace.FindFromKey(edge).First()); + mkChamfer.Add(radius1, radius2, TopoDS::Edge(edge), face); + } + + TopoDS_Shape shape = mkChamfer.Shape(); + if (shape.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is null"); + + TopoShape res(0,getDocument()->getStringHasher()); + this->Shape.setValue(res.makEShape(mkChamfer,baseTopoShape,Part::OpCodes::Chamfer)); + return Part::Feature::execute(); +#endif } catch (Standard_Failure& e) { return new App::DocumentObjectExecReturn(e.GetMessageString()); diff --git a/src/Mod/Part/App/FeatureFillet.cpp b/src/Mod/Part/App/FeatureFillet.cpp index 3fb6d696f0..4090da8250 100644 --- a/src/Mod/Part/App/FeatureFillet.cpp +++ b/src/Mod/Part/App/FeatureFillet.cpp @@ -48,13 +48,15 @@ App::DocumentObjectExecReturn *Fillet::execute() if (!link) return new App::DocumentObjectExecReturn("No object linked"); - auto baseShape = Feature::getShape(link); try { #if defined(__GNUC__) && defined (FC_OS_LINUX) Base::SignalException se; #endif + auto baseShape = Feature::getShape(link); + TopoShape baseTopoShape = Feature::getTopoShape(link); BRepFilletAPI_MakeFillet mkFillet(baseShape); +#ifndef FC_USE_TNP_FIX TopTools_IndexedMapOfShape mapOfShape; TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfShape); @@ -92,6 +94,35 @@ App::DocumentObjectExecReturn *Fillet::execute() prop.touch(); return App::DocumentObject::StdReturn; +#else + const auto &vals = EdgeLinks.getSubValues(); + const auto &subs = EdgeLinks.getShadowSubs(); + if(subs.size()!=(size_t)Edges.getSize()) + return new App::DocumentObjectExecReturn("Edge link size mismatch"); + size_t i=0; + for(const auto &info : Edges.getValues()) { + auto &sub = subs[i]; + auto &ref = sub.first.size()?sub.first:vals[i]; + ++i; + TopoDS_Shape edge; + try { + edge = baseTopoShape.getSubShape(ref.c_str()); + }catch(...){} + if(edge.IsNull()) + return new App::DocumentObjectExecReturn("Invalid edge link"); + double radius1 = info.radius1; + double radius2 = info.radius2; + mkFillet.Add(radius1, radius2, TopoDS::Edge(edge)); + } + + TopoDS_Shape shape = mkFillet.Shape(); + if (shape.IsNull()) + return new App::DocumentObjectExecReturn("Resulting shape is null"); + + TopoShape res(0,getDocument()->getStringHasher()); + this->Shape.setValue(res.makEShape(mkFillet,baseTopoShape,Part::OpCodes::Fillet)); + return Part::Feature::execute(); +#endif } catch (Standard_Failure& e) { return new App::DocumentObjectExecReturn(e.GetMessageString()); diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index 5c0fca6fb1..9c0cb0088c 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -1217,16 +1217,69 @@ FilletBase::FilletBase() { ADD_PROPERTY(Base,(nullptr)); ADD_PROPERTY(Edges,(0,0,0)); + ADD_PROPERTY_TYPE(EdgeLinks,(0), 0, + (App::PropertyType)(App::Prop_ReadOnly|App::Prop_Hidden),0); Edges.setSize(0); } short FilletBase::mustExecute() const { - if (Base.isTouched() || Edges.isTouched()) + if (Base.isTouched() || Edges.isTouched() || EdgeLinks.isTouched()) return 1; return 0; } +void FilletBase::onChanged(const App::Property *prop) { + if(getDocument() && !getDocument()->testStatus(App::Document::Restoring)) { + if(prop == &Edges || prop == &Base) { + if(!prop->testStatus(App::Property::User3)) + syncEdgeLink(); + } + } + Feature::onChanged(prop); +} + +void FilletBase::onDocumentRestored() { + if(EdgeLinks.getSubValues().empty()) + syncEdgeLink(); + Feature::onDocumentRestored(); +} + +void FilletBase::syncEdgeLink() { + if(!Base.getValue() || !Edges.getSize()) { + EdgeLinks.setValue(0); + return; + } + std::vector subs; + std::string sub("Edge"); + for(auto &info : Edges.getValues()) + subs.emplace_back(sub+std::to_string(info.edgeid)); + EdgeLinks.setValue(Base.getValue(),subs); +} + +void FilletBase::onUpdateElementReference(const App::Property *prop) { + if(prop!=&EdgeLinks || !getNameInDocument()) + return; + auto values = Edges.getValues(); + const auto &subs = EdgeLinks.getSubValues(); + for(size_t i=0;i=subs.size()) { + FC_WARN("fillet edge count mismatch in object " << getFullName()); + break; + } + int idx = 0; + sscanf(subs[i].c_str(),"Edge%d",&idx); + if(idx) + values[i].edgeid = idx; + else + FC_WARN("invalid fillet edge link '" << subs[i] << "' in object " + << getFullName()); + } + Edges.setStatus(App::Property::User3,true); + Edges.setValues(values); + Edges.setStatus(App::Property::User3,false); +} + // --------------------------------------------------------- PROPERTY_SOURCE(Part::FeatureExt, Part::Feature) diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index 31975fdc08..34adae3b69 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -175,8 +175,15 @@ public: App::PropertyLink Base; PropertyFilletEdges Edges; + App::PropertyLinkSub EdgeLinks; short mustExecute() const override; + virtual void onUpdateElementReference(const App::Property *prop) override; + +protected: + virtual void onDocumentRestored() override; + virtual void onChanged(const App::Property *) override; + void syncEdgeLink(); }; using FeaturePython = App::FeaturePythonT; diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 225c114fa2..a60c75c618 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -102,6 +102,7 @@ #include "Geometry.h" #include "BRepOffsetAPI_MakeOffsetFix.h" +#include #include #include #include @@ -814,6 +815,7 @@ void TopoShape::mapSubElementForShape(const TopoShape& other, const char* op) void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool forceHasher) { +#ifndef FC_USE_TNP_FIX if (!canMapElement(other)) { return; } @@ -831,6 +833,102 @@ void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool force } mapSubElementForShape(other, op); +#else + if(!canMapElement(other)) + return; + + if (!getElementMapSize(false) && this->_Shape.IsPartner(other._Shape)) { + if (!this->Hasher) + this->Hasher = other.Hasher; + copyElementMap(other, op); + return; + } + + bool warned = false; + static const std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; + + auto checkHasher = [this](const TopoShape &other) { + if(Hasher) { + if(other.Hasher!=Hasher) { + if(!getElementMapSize(false)) { + if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("hasher mismatch"); + }else { + // FC_THROWM(Base::RuntimeError, "hasher mismatch"); + FC_ERR("hasher mismatch"); + } + Hasher = other.Hasher; + } + }else + Hasher = other.Hasher; + }; + + for(auto type : types) { + auto &shapeMap = _cache->getAncestry(type); + auto &otherMap = other._cache->getAncestry(type); + if(!shapeMap.count() || !otherMap.count()) + continue; + if(!forceHasher && other.Hasher) { + forceHasher = true; + checkHasher(other); + } + const char *shapetype = shapeName(type).c_str(); + std::ostringstream ss; + + bool forward; + int count; + if(otherMap.count()<=shapeMap.count()) { + forward = true; + count = otherMap.count(); + }else{ + forward = false; + count = shapeMap.count(); + } + for(int k=1;k<=count;++k) { + int i,idx; + if(forward) { + i = k; + idx = shapeMap.find(_Shape,otherMap.find(other._Shape,k)); + if(!idx) continue; + } else { + idx = k; + i = otherMap.find(other._Shape,shapeMap.find(_Shape,k)); + if(!i) continue; + } + Data::IndexedName element = Data::IndexedName::fromConst(shapetype, idx); + for(auto &v : other.getElementMappedNames( + Data::IndexedName::fromConst(shapetype,i),true)) + { + auto &name = v.first; + auto &sids = v.second; + if(sids.size()) { + if (!Hasher) + Hasher = sids[0].getHasher(); + else if (!sids[0].isFromSameHasher(Hasher)) { + if (!warned) { + warned = true; + FC_WARN("hasher mismatch"); + } + sids.clear(); + } + } + ss.str(""); + + // Originally in ComplexGeoData::setElementName + // LinkStable/src/App/ComplexGeoData.cpp#L1631 + // No longer possible after map separated in ElementMap.cpp + + if (!elementMap()) { + resetElementMap(std::make_shared()); + } + + elementMap()->encodeElementName(shapetype[0],name,ss,&sids,Tag,op,other.Tag); + elementMap()->setElementName(element,name,Tag,&sids); + } + } + } + +#endif } void TopoShape::mapSubElementsTo(std::vector& shapes, const char* op) const @@ -892,6 +990,7 @@ void TopoShape::mapCompoundSubElements(const std::vector& shapes, con void TopoShape::mapSubElement(const std::vector& shapes, const char* op) { +#ifndef FC_USE_TNP_FIX if (shapes.empty()) { return; } @@ -904,6 +1003,52 @@ void TopoShape::mapSubElement(const std::vector& shapes, const char* mapSubElement(shape, op); } } +#else + if (shapes.empty()) + return; + + if (shapeType(true) == TopAbs_COMPOUND) { + int count = 0; + for (auto & s : shapes) { + if (s.isNull()) + continue; + if (!getSubShape(TopAbs_SHAPE, ++count, true).IsPartner(s._Shape)) { + count = 0; + break; + } + } + if (count) { + std::vector children; + children.reserve(count*3); + TopAbs_ShapeEnum types[] = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; + for (unsigned i=0; i TopoShape::getSubShapes(TopAbs_ShapeEnum type, diff --git a/src/Mod/Part/Gui/DlgFilletEdges.cpp b/src/Mod/Part/Gui/DlgFilletEdges.cpp index 28971ff8df..99a562c78a 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.cpp +++ b/src/Mod/Part/Gui/DlgFilletEdges.cpp @@ -592,6 +592,16 @@ void DlgFilletEdges::setupFillet(const std::vector& objs) { App::DocumentObject* base = d->fillet->Base.getValue(); const std::vector& e = d->fillet->Edges.getValues(); + const auto &subs = d->fillet->EdgeLinks.getShadowSubs(); + if(subs.size()!=e.size()) { + FC_ERR("edge link size mismatch"); + return; + } + std::set subSet; + for(auto &sub : subs) + subSet.insert(sub.first.empty()?sub.second:sub.first); + + std::string tmp; std::vector::const_iterator it = std::find(objs.begin(), objs.end(), base); if (it != objs.end()) { // toggle visibility @@ -613,6 +623,40 @@ void DlgFilletEdges::setupFillet(const std::vector& objs) std::vector subElements; QStandardItemModel *model = qobject_cast(ui->treeView->model()); bool block = model->blockSignals(true); // do not call toggleCheckState + auto baseShape = Part::Feature::getTopoShape(base); + std::set elements; + for(size_t i=0;igetFullName() << "." << ref); + + for(auto &mapped : Part::Feature::getRelatedElements(base,ref.c_str())) { + tmp.clear(); + if(!subSet.insert(mapped.index.toString(tmp)).second + || !subSet.insert(mapped.name.toString(0)).second) + continue; + FC_WARN("guess element reference: " << ref << " -> " << mapped.index); + elements.emplace(mapped.index.getIndex(),e[i].radius1,e[i].radius2); + } + } + for (const auto & et : e) { std::vector::iterator it = std::find(d->edge_ids.begin(), d->edge_ids.end(), et.edgeid); if (it != d->edge_ids.end()) {