From 2adff99c14632dcf16fa1b3fc92bdb32e12dbcec Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 29 Feb 2024 20:58:51 -0500 Subject: [PATCH] Toposhape/Part: Transfer in PropoertyTopoShape and related --- src/App/ComplexGeoData.cpp | 24 ++ src/App/ComplexGeoData.h | 12 + src/App/PropertyGeo.cpp | 34 ++ src/App/PropertyGeo.h | 10 + src/Mod/Part/App/PropertyTopoShape.cpp | 433 ++++++++++++++++++++++++- src/Mod/Part/App/PropertyTopoShape.h | 73 ++++- 6 files changed, 575 insertions(+), 11 deletions(-) diff --git a/src/App/ComplexGeoData.cpp b/src/App/ComplexGeoData.cpp index 95df883b8f..717e173cb7 100644 --- a/src/App/ComplexGeoData.cpp +++ b/src/App/ComplexGeoData.cpp @@ -189,6 +189,17 @@ const std::string &ComplexGeoData::elementMapPrefix() { return prefix; } +std::string ComplexGeoData::getElementMapVersion() const { + return "4"; +} + +bool ComplexGeoData::checkElementMapVersion(const char * ver) const +{ + return !boost::equals(ver, "3") + && !boost::equals(ver, "4") + && !boost::starts_with(ver, "3."); +} + size_t ComplexGeoData::getElementMapSize(bool flush) const { if (flush) { @@ -653,5 +664,18 @@ void ComplexGeoData::beforeSave() const } } +void ComplexGeoData::hashChildMaps() +{ + flushElementMap(); + if (_elementMap) + _elementMap->hashChildMaps(Tag); +} + +bool ComplexGeoData::hasChildElementMap() const +{ + flushElementMap(); + return _elementMap && _elementMap->hasChildElementMap(); +} + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index a7a4fa290c..6fb11ee915 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -242,6 +242,12 @@ public: std::vector > getElementMappedNames(const IndexedName & element, bool needUnmapped=false) const; + /// Hash the child element map postfixes to shorten element name from hierarchical maps + void hashChildMaps(); + + /// Check if there is child element map + bool hasChildElementMap() const; + /// Append the Tag (if and only if it is non zero) into the element map virtual void reTagElementMap(long tag, App::StringHasherRef hasher, @@ -289,6 +295,12 @@ public: /// Get the current element map size size_t getElementMapSize(bool flush=true) const; + /// Return the current element map version + virtual std::string getElementMapVersion() const; + + /// Return true to signal element map version change + virtual bool checkElementMapVersion(const char * ver) const; + /// Check if the given sub-name only contains an element name static bool isElementName(const char *subName) { return (subName != nullptr) && (*subName != 0) && findElementName(subName)==subName; diff --git a/src/App/PropertyGeo.cpp b/src/App/PropertyGeo.cpp index bddff7d9a0..6ce4186d76 100644 --- a/src/App/PropertyGeo.cpp +++ b/src/App/PropertyGeo.cpp @@ -1244,6 +1244,40 @@ PropertyComplexGeoData::PropertyComplexGeoData() = default; PropertyComplexGeoData::~PropertyComplexGeoData() = default; +std::string PropertyComplexGeoData::getElementMapVersion(bool) const { + auto data = getComplexData(); + if(!data) + return std::string(); + auto owner = Base::freecad_dynamic_cast(getContainer()); + std::ostringstream ss; + if(owner && owner->getDocument() + && owner->getDocument()->getStringHasher()==data->Hasher) + ss << "1."; + else + ss << "0."; + ss << data->getElementMapVersion(); + return ss.str(); +} + +bool PropertyComplexGeoData::checkElementMapVersion(const char * ver) const +{ + auto data = getComplexData(); + if(!data) + return false; + auto owner = Base::freecad_dynamic_cast(getContainer()); + std::ostringstream ss; + const char *prefix; + if(owner && owner->getDocument() + && owner->getDocument()->getStringHasher() == data->Hasher) + prefix = "1."; + else + prefix = "0."; + if (!boost::starts_with(ver, prefix)) + return true; + return data->checkElementMapVersion(ver+2); +} + + void PropertyComplexGeoData::afterRestore() { auto data = getComplexData(); diff --git a/src/App/PropertyGeo.h b/src/App/PropertyGeo.h index 861442ed57..d6c78f0a52 100644 --- a/src/App/PropertyGeo.h +++ b/src/App/PropertyGeo.h @@ -541,6 +541,16 @@ public: Base::BoundBox3d getBoundingBox() const override = 0; //@} + /** Return the element map version + * + * @param persisted: if true, return the restored element map version. Or + * else, return the current element map version + */ + virtual std::string getElementMapVersion(bool restored=false) const; + + /// Return true to signal element map version change + virtual bool checkElementMapVersion(const char * ver) const; + void afterRestore() override; }; diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index 4ecd86916e..c32889ab99 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -36,6 +36,7 @@ #endif // _PreComp_ #include +#include #include #include #include @@ -45,10 +46,14 @@ #include #include +#include "PartFeature.h" +#include "PartPyCXX.h" #include "PropertyTopoShape.h" #include "TopoShapePy.h" +FC_LOG_LEVEL_INIT("App", true, true) +namespace sp = std::placeholders; using namespace Part; TYPESYSTEM_SOURCE(Part::PropertyPartShape , App::PropertyComplexGeoData) @@ -61,14 +66,32 @@ void PropertyPartShape::setValue(const TopoShape& sh) { aboutToSetValue(); _Shape = sh; + auto obj = Base::freecad_dynamic_cast(getContainer()); + if(obj) { + auto tag = obj->getID(); + if(_Shape.Tag && tag!=_Shape.Tag) { + auto hasher = _Shape.Hasher?_Shape.Hasher:obj->getDocument()->getStringHasher(); + _Shape.reTagElementMap(tag,hasher); + } else + _Shape.Tag = obj->getID(); + if (!_Shape.Hasher && _Shape.hasChildElementMap()) { + _Shape.Hasher = obj->getDocument()->getStringHasher(); + _Shape.hashChildMaps(); + } + } hasSetValue(); + _Ver.clear(); } -void PropertyPartShape::setValue(const TopoDS_Shape& sh) +void PropertyPartShape::setValue(const TopoDS_Shape& sh, bool resetElementMap) { aboutToSetValue(); - _Shape.setShape(sh); + auto obj = dynamic_cast(getContainer()); + if(obj) + _Shape.Tag = obj->getID(); + _Shape.setShape(sh,resetElementMap); hasSetValue(); + _Ver.clear(); } const TopoDS_Shape& PropertyPartShape::getValue() const @@ -76,13 +99,23 @@ const TopoDS_Shape& PropertyPartShape::getValue() const return _Shape.getShape(); } -const TopoShape& PropertyPartShape::getShape() const +TopoShape PropertyPartShape::getShape() const { - return this->_Shape; + _Shape.initCache(-1); + auto res = _Shape; +// if (Feature::isElementMappingDisabled(getContainer())) + if ( false ) // TODO Implement + res.Tag = -1; + else if (!res.Tag) { + if (auto parent = Base::freecad_dynamic_cast(getContainer())) + res.Tag = parent->getID(); + } + return res; } const Data::ComplexGeoData* PropertyPartShape::getComplexData() const { + _Shape.initCache(-1); return &(this->_Shape); } @@ -140,8 +173,21 @@ PyObject *PropertyPartShape::getPyObject() void PropertyPartShape::setPyObject(PyObject *value) { if (PyObject_TypeCheck(value, &(TopoShapePy::Type))) { - TopoShapePy *pcObject = static_cast(value); - setValue(*pcObject->getTopoShapePtr()); + auto shape = *static_cast(value)->getTopoShapePtr(); + auto owner = dynamic_cast(getContainer()); + if(owner && owner->getDocument()) { + if(shape.Tag || shape.getElementMapSize()) { + // We can't trust the meaning of the input shape tag, so we + // remap anyway + TopoShape res(owner->getID(),owner->getDocument()->getStringHasher(),shape.getShape()); + res.mapSubElement(shape); + shape = res; + }else{ + shape.Tag = owner->getID(); + shape.Hasher->clear(); // reset(); + } + } + setValue(shape); } else { std::string error = std::string("type must be 'Shape', not "); @@ -153,6 +199,15 @@ void PropertyPartShape::setPyObject(PyObject *value) App::Property *PropertyPartShape::Copy() const { PropertyPartShape *prop = new PropertyPartShape(); + +// if (PartParams::getShapePropertyCopy()) { + if ( false ) { // TODO: PartParams + // makeElementCopy() consume too much memory for complex geometry. + prop->_Shape = this->_Shape.makeElementCopy(); + } else + prop->_Shape = this->_Shape; + prop->_Ver = this->_Ver; + prop->_Shape = this->_Shape; if (!_Shape.getShape().IsNull()) { BRepBuilderAPI_Copy copy(_Shape.getShape()); @@ -164,9 +219,11 @@ App::Property *PropertyPartShape::Copy() const void PropertyPartShape::Paste(const App::Property &from) { - aboutToSetValue(); - _Shape = dynamic_cast(from)._Shape; - hasSetValue(); + auto prop = Base::freecad_dynamic_cast(&from); + if(prop) { + setValue(prop->_Shape); + _Ver = prop->_Ver; + } } unsigned int PropertyPartShape::getMemSize () const @@ -188,6 +245,19 @@ void PropertyPartShape::getPaths(std::vector &paths) cons << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String("Volume"))); } +void PropertyPartShape::beforeSave() const +{ + _HasherIndex = 0; + _SaveHasher = false; + auto owner = Base::freecad_dynamic_cast(getContainer()); + if(owner && !_Shape.isNull() && _Shape.getElementMapSize()>0) { + auto ret = owner->getDocument()->addStringHasher(_Shape.Hasher); + _HasherIndex = ret.second; + _SaveHasher = ret.first; + _Shape.beforeSave(); + } +} + void PropertyPartShape::Save (Base::Writer &writer) const { if(!writer.isForceXML()) { @@ -205,6 +275,69 @@ void PropertyPartShape::Save (Base::Writer &writer) const } } +#ifdef NOT_YET_AND_MAYBE_NEVER +void PropertyPartShape::Save (Base::Writer &writer) const +{ + //See SaveDocFile(), RestoreDocFile() + writer.Stream() << writer.ind() << "(getContainer()); + if(owner && !_Shape.isNull() + && _Shape.getElementMapSize()>0 + && !_Shape.Hasher.isNull()) { + writer.Stream() << " HasherIndex=\"" << _HasherIndex << '"'; + if(_SaveHasher) + writer.Stream() << " SaveHasher=\"1\""; + } + std::string version; + // If exporting, do not export mapped element name, but still make a mark + if(owner) { + if(!owner->isExporting()) + version = _Ver.size()?_Ver:owner->getElementMapVersion(this); + }else + version = _Ver.size()?_Ver:_Shape.getElementMapVersion(); + writer.Stream() << " ElementMap=\"" << version << '"'; + + bool binary = writer.getMode("BinaryBrep"); + bool toXML = writer.getFileVersion()>1 && writer.isForceXML()>=(binary?3:2); + if(!toXML) { + writer.Stream() << " file=\"" + << writer.addFile(getFileName(binary?".bin":".brp"), this) + << "\"/>\n"; + } else if(binary) { + writer.Stream() << " binary=\"1\">\n"; + TopoShape shape; + shape.setShape(_Shape.getShape()); + shape.exportBinary(writer.beginCharStream(true)); + writer.endCharStream() << writer.ind() << "\n"; + } else { + writer.Stream() << " brep=\"1\">\n"; + _Shape.exportBrep(writer.beginCharStream(false)<<'\n'); + writer.endCharStream() << '\n' << writer.ind() << "\n"; + } + + if(_SaveHasher) { + if(!toXML) + _Shape.Hasher->setPersistenceFileName(getFileName(".Table").c_str()); + else + _Shape.Hasher->setPersistenceFileName(0); + _Shape.Hasher->Save(writer); + } + if(version.size()) { + if(!toXML) + _Shape.setPersistenceFileName(getFileName(".Map").c_str()); + else + _Shape.setPersistenceFileName(0); + _Shape.Save(writer); + } +} +#endif + +std::string PropertyPartShape::getElementMapVersion(bool restored) const { + if(restored) + return _Ver; + return PropertyComplexGeoData::getElementMapVersion(false); +} + void PropertyPartShape::Restore(Base::XMLReader &reader) { reader.readElement("Part"); @@ -215,6 +348,105 @@ void PropertyPartShape::Restore(Base::XMLReader &reader) reader.addFile(file.c_str(),this); } } +#ifdef NOT_YET_AND_MAYBE_NEVER +void PropertyPartShape::Restore(Base::XMLReader &reader) +{ + reader.readElement("Part"); + + auto owner = Base::freecad_dynamic_cast(getContainer()); + _Ver = "?"; + bool has_ver = reader.hasAttribute("ElementMap"); + if(has_ver) + _Ver = reader.getAttribute("ElementMap"); + + int hasher_idx = reader.getAttributeAsInteger("HasherIndex","-1"); + int save_hasher = reader.getAttributeAsInteger("SaveHasher",""); + + TopoDS_Shape sh; + + if(reader.hasAttribute("file")) { + std::string file = reader.getAttribute("file"); + if (!file.empty()) { + // initiate a file read + reader.addFile(file.c_str(),this); + } + } else if(reader.getAttributeAsInteger("binary","")) { + TopoShape shape; + shape.importBinary(reader.beginCharStream(true)); + sh = shape.getShape(); + } else if(reader.getAttributeAsInteger("brep","")) { + BRep_Builder builder; + BRepTools::Read(sh, reader.beginCharStream(false), builder); + } + + reader.readEndElement("Part"); + + if(owner && hasher_idx>=0) { + _Shape.Hasher = owner->getDocument()->getStringHasher(hasher_idx); + if(save_hasher) + _Shape.Hasher->Restore(reader); + } + + if(has_ver) { + // The file name here is not used for restore, but just a way to get + // more useful error message if something wrong when restoring + _Shape.setPersistenceFileName(getFileName().c_str()); + if(owner && owner->getDocument()->testStatus(App::Document::PartialDoc)) + _Shape.Restore(reader); + else if(_Ver == "?" || _Ver.empty()) { + // This indicate the shape is saved by legacy version without + // element map info. + if(owner) { + // This will ask user for recompute after import + owner->getDocument()->addRecomputeObject(owner); + } + }else{ + _Shape.Restore(reader); + if (owner ? owner->checkElementMapVersion(this, _Ver.c_str()) + : _Shape.checkElementMapVersion(_Ver.c_str())) { + auto ver = owner?owner->getElementMapVersion(this):_Shape.getElementMapVersion(); + if(!owner || !owner->getNameInDocument() || !_Shape.getElementMapSize()) { + _Ver = ver; + } else { + // version mismatch, signal for regenerating. + static const char *warnedDoc=0; + if(warnedDoc != owner->getDocument()->getName()) { + warnedDoc = owner->getDocument()->getName(); + FC_WARN("Recomputation required for document '" << warnedDoc + << "' on geo element version change in " << getFullName() + << ": " << _Ver << " -> " << ver); + } + owner->getDocument()->addRecomputeObject(owner); + } + } + } + } else if(owner && !owner->getDocument()->testStatus(App::Document::PartialDoc)) { + if(App::DocumentParams::getWarnRecomputeOnRestore()) { + FC_WARN("Pending recompute for generating element map: " << owner->getFullName()); + owner->getDocument()->addRecomputeObject(owner); + } + } + + if (!sh.IsNull() || !_Shape.isNull()) { + aboutToSetValue(); + _Shape.setShape(sh,false); + hasSetValue(); + } +} +#endif + +void PropertyPartShape::afterRestore() +{ + if (_Shape.isRestoreFailed()) { + // this cause GeoFeature::updateElementReference() to call + // PropertyLinkBase::updateElementReferences() with reverse = true, in + // order to try to regenerate the element map + _Ver = "?"; + } + else if (_Shape.getElementMapSize() == 0) + _Shape.Hasher->clear(); //reset(); + PropertyComplexGeoData::afterRestore(); +} // The following function is copied from OCCT BRepTools.cxx and modified // to disable saving of triangulation @@ -406,6 +638,88 @@ void PropertyPartShape::RestoreDocFile(Base::Reader &reader) // ------------------------------------------------------------------------- +ShapeHistory::ShapeHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS) +{ + reset(mkShape,type,newS,oldS); +} + +void ShapeHistory::reset(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS) +{ + shapeMap.clear(); + this->type = type; + + TopTools_IndexedMapOfShape newM, oldM; + TopExp::MapShapes(newS, type, newM); // map containing all old objects of type "type" + TopExp::MapShapes(oldS, type, oldM); // map containing all new objects of type "type" + + // Look at all objects in the old shape and try to find the modified object in the new shape + for (int i=1; i<=oldM.Extent(); i++) { + bool found = false; + TopTools_ListIteratorOfListOfShape it; + // Find all new objects that are a modification of the old object (e.g. a face was resized) + for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) { + found = true; + for (int j=1; j<=newM.Extent(); j++) { // one old object might create several new ones! + if (newM(j).IsPartner(it.Value())) { + shapeMap[i-1].push_back(j-1); // adjust indices to start at zero + break; + } + } + } + + // Find all new objects that were generated from an old object (e.g. a face generated from an edge) + for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) { + found = true; + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(it.Value())) { + shapeMap[i-1].push_back(j-1); + break; + } + } + } + + if (!found) { + // Find all old objects that don't exist any more (e.g. a face was completely cut away) + if (mkShape.IsDeleted(oldM(i))) { + shapeMap[i-1] = std::vector(); + } + else { + // Mop up the rest (will this ever be reached?) + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(oldM(i))) { + shapeMap[i-1].push_back(j-1); + break; + } + } + } + } + } +} + +void ShapeHistory::join(const ShapeHistory& newH) +{ + ShapeHistory join; + + for (ShapeHistory::MapList::const_iterator it = shapeMap.begin(); it != shapeMap.end(); ++it) { + int old_shape_index = it->first; + if (it->second.empty()) + join.shapeMap[old_shape_index] = ShapeHistory::List(); + for (ShapeHistory::List::const_iterator jt = it->second.begin(); jt != it->second.end(); ++jt) { + ShapeHistory::MapList::const_iterator kt = newH.shapeMap.find(*jt); + if (kt != newH.shapeMap.end()) { + ShapeHistory::List& ary = join.shapeMap[old_shape_index]; + ary.insert(ary.end(), kt->second.begin(), kt->second.end()); + } + } + } + + shapeMap.swap(join.shapeMap); +} + +// ------------------------------------------------------------------------- + TYPESYSTEM_SOURCE(Part::PropertyShapeHistory , App::PropertyLists) PropertyShapeHistory::PropertyShapeHistory() = default; @@ -577,3 +891,104 @@ void PropertyFilletEdges::Paste(const Property &from) _lValueList = dynamic_cast(from)._lValueList; hasSetValue(); } + +// ------------------------------------------------------------------------- + +TYPESYSTEM_SOURCE(Part::PropertyShapeCache, App::Property); + +App::Property *PropertyShapeCache::Copy(void) const { + return new PropertyShapeCache(); +} + +void PropertyShapeCache::Paste(const App::Property &) { + cache.clear(); +} + +void PropertyShapeCache::Save (Base::Writer &) const +{ +} + +void PropertyShapeCache::Restore(Base::XMLReader &) +{ +} + +PyObject *PropertyShapeCache::getPyObject() { + Py::List res; + for(auto &v : cache) + res.append(Py::TupleN(Py::String(v.first),shape2pyshape(v.second))); + return Py::new_reference_to(res); +} + +void PropertyShapeCache::setPyObject(PyObject *value) { + if(!value) + return; + if(value == Py_None) { + cache.clear(); + return; + } + App::PropertyStringList prop; + prop.setPyObject(value); + for(const auto &sub : prop.getValues()) + cache.erase(sub); +} + +#define SHAPE_CACHE_NAME "_Part_ShapeCache" +PropertyShapeCache *PropertyShapeCache::get(const App::DocumentObject *obj, bool create) { + auto prop = Base::freecad_dynamic_cast( + obj->getDynamicPropertyByName(SHAPE_CACHE_NAME)); + if(prop && prop->getContainer()==obj) + return prop; + if(!create) + return 0; + + prop = static_cast( + const_cast(obj)->addDynamicProperty("Part::PropertyShapeCache", + SHAPE_CACHE_NAME,"Part","Shape cache", + App::Prop_NoPersist|App::Prop_Output|App::Prop_Hidden)); + if(!prop) + FC_ERR("Failed to add shape cache for " << obj->getFullName()); + else + prop->connChanged = const_cast(obj)->signalEarlyChanged.connect( + std::bind(&PropertyShapeCache::slotChanged,prop,sp::_1,sp::_2)); + return prop; +} + +bool PropertyShapeCache::getShape(const App::DocumentObject *obj, TopoShape &shape, const char *subname) { +// if (PartParams::getDisableShapeCache()) +// return false; //TODO PartParams + auto prop = get(obj,false); + if(!prop) + return false; + if(!subname) subname = ""; + auto it = prop->cache.find(subname); + if(it!=prop->cache.end()) { + shape = it->second; + return !shape.isNull(); + } + return false; +} + +void PropertyShapeCache::setShape( + const App::DocumentObject *obj, const TopoShape &shape, const char *subname) +{ +// if (PartParams::getDisableShapeCache()) +// return; // TODO: Part Params + auto prop = get(obj,true); + if(!prop) + return; + if(!subname) subname = ""; + prop->cache[subname] = shape; +} + +void PropertyShapeCache::slotChanged(const App::DocumentObject &, const App::Property &prop) { + auto propName = prop.getName(); + if(!propName) return; + if(strcmp(propName,"Group")==0 || + strcmp(propName,"Shape")==0 || + strstr(propName,"Touched")!=0) + { + FC_LOG("clear shape cache on changed " << prop.getFullName()); + cache.clear(); + } +} + diff --git a/src/Mod/Part/App/PropertyTopoShape.h b/src/Mod/Part/App/PropertyTopoShape.h index 54c495fdc4..32df7f4ff3 100644 --- a/src/Mod/Part/App/PropertyTopoShape.h +++ b/src/Mod/Part/App/PropertyTopoShape.h @@ -35,6 +35,7 @@ namespace Part { +class Feature; /** The part shape property class. * @author Werner Mayer */ @@ -51,10 +52,10 @@ public: /// set the part shape void setValue(const TopoShape&); /// set the part shape - void setValue(const TopoDS_Shape&); + void setValue(const TopoDS_Shape&, bool resetElementMap=true); /// get the part shape const TopoDS_Shape& getValue() const; - const TopoShape& getShape() const; + TopoShape getShape() const; const Data::ComplexGeoData* getComplexData() const override; //@} @@ -85,6 +86,8 @@ public: void Save (Base::Writer &writer) const override; void Restore(Base::XMLReader &reader) override; + virtual void beforeSave() const override; + void SaveDocFile (Base::Writer &writer) const override; void RestoreDocFile(Base::Reader &reader) override; @@ -96,6 +99,13 @@ public: /// Get valid paths for this property; used by auto completer void getPaths(std::vector & paths) const override; + virtual std::string getElementMapVersion(bool restored=false) const override; + void resetElementMapVersion() {_Ver.clear();} + + virtual void afterRestore() override; + + friend class Feature; + private: void saveToFile(Base::Writer &writer) const; void loadFromFile(Base::Reader &reader); @@ -103,6 +113,9 @@ private: private: TopoShape _Shape; + std::string _Ver; + mutable int _HasherIndex = 0; + mutable bool _SaveHasher = false; }; struct PartExport ShapeHistory { @@ -115,6 +128,20 @@ struct PartExport ShapeHistory { TopAbs_ShapeEnum type; MapList shapeMap; + ShapeHistory() {} + /** + * Build a history of changes + * MakeShape: The operation that created the changes, e.g. BRepAlgoAPI_Common + * type: The type of object we are interested in, e.g. TopAbs_FACE + * newS: The new shape that was created by the operation + * oldS: The original shape prior to the operation + */ + ShapeHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS); + void reset(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS); + void join(const ShapeHistory &newH); + }; class PartExport PropertyShapeHistory : public App::PropertyLists @@ -168,6 +195,20 @@ private: struct PartExport FilletElement { int edgeid; double radius1, radius2; + + FilletElement(int id=0,double r1=1.0,double r2=1.0) + :edgeid(id),radius1(r1),radius2(r2) + {} + + bool operator<(const FilletElement &other) const { + return edgeid < other.edgeid; + } + + bool operator==(const FilletElement &other) const { + return edgeid == other.edgeid + && radius1 == other.radius1 + && radius2 == other.radius2; + } }; class PartExport PropertyFilletEdges : public App::PropertyLists @@ -215,6 +256,34 @@ private: std::vector _lValueList; }; + +class PartExport PropertyShapeCache: public App::Property { + TYPESYSTEM_HEADER_WITH_OVERRIDE(); +public: + virtual App::Property *Copy(void) const override; + + virtual void Paste(const App::Property &) override; + + virtual PyObject *getPyObject() override; + + virtual void setPyObject(PyObject *value) override; + + virtual void Save (Base::Writer &writer) const override; + + virtual void Restore(Base::XMLReader &reader) override; + + static PropertyShapeCache *get(const App::DocumentObject *obj, bool create); + static bool getShape(const App::DocumentObject *obj, TopoShape &shape, const char *subname=0); + static void setShape(const App::DocumentObject *obj, const TopoShape &shape, const char *subname=0); + +private: + void slotChanged(const App::DocumentObject &, const App::Property &prop); + +private: + std::unordered_map cache; + boost::signals2::scoped_connection connChanged; +}; + } //namespace Part