From c38ef04d7a84633450e956e672d840b77f88201b Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 28 Feb 2018 19:54:38 +0800 Subject: [PATCH] TopoShape: refactor element map Element mapping interface is now moved into TopoShape's parent class Data::ComplexGeoData. User can get/set single element mapping with function ComplexGeoData::get/setElementName() API in both C++ and Python. Multiple element mapping can be done with get/setElementMap() in C++, and through attribute ElementMap in Python. The ComplexGeoData object can optionally be assign a StringHasher object to encode its element mapped name with StringID. It is exposed in both C++ as public member variable (and attribute in Python) named Hasher. ComplexGeoData itself do not persist the string hasher. For TopoShape, its done by PropertyPartShape. The individual hashes are persisted inside the property only if the hasher is not assigned from the owner Document.Hasher. TopoShape adds a new public member variable called Tag to help auto generate element mapping. TopoShape adds two new element mapping aware function, makECompound() and makEWire(), exposed in both C++ and Python. These function will auto generate element mappings if both the shape's own Tag and input shape's Tag are non-zero. Part::Primitives and Part::Compound now supports element mapping --- src/App/ComplexGeoData.cpp | 139 +++++++++++- src/App/ComplexGeoData.h | 80 +++++++ src/App/ComplexGeoDataPy.xml | 43 ++++ src/App/ComplexGeoDataPyImp.cpp | 79 +++++++ src/Mod/Part/App/FeatureCompound.cpp | 16 +- src/Mod/Part/App/FeaturePartBox.cpp | 2 +- src/Mod/Part/App/FeaturePartCircle.cpp | 2 +- src/Mod/Part/App/FeaturePartPolygon.cpp | 2 +- src/Mod/Part/App/PartFeature.cpp | 53 +++-- src/Mod/Part/App/PartFeature.h | 6 + src/Mod/Part/App/PrimitiveFeature.cpp | 29 +-- src/Mod/Part/App/PropertyTopoShape.cpp | 71 ++++-- src/Mod/Part/App/PropertyTopoShape.h | 2 +- src/Mod/Part/App/TopoShape.cpp | 290 +++++++++++++++++------- src/Mod/Part/App/TopoShape.h | 93 ++++---- src/Mod/Part/App/TopoShapePy.xml | 53 ++++- src/Mod/Part/App/TopoShapePyImp.cpp | 256 ++++++++++----------- 17 files changed, 875 insertions(+), 341 deletions(-) diff --git a/src/App/ComplexGeoData.cpp b/src/App/ComplexGeoData.cpp index 7826787f29c1..b66983031801 100644 --- a/src/App/ComplexGeoData.cpp +++ b/src/App/ComplexGeoData.cpp @@ -28,10 +28,24 @@ #endif #include +#include +#include +#include +#include +#include +#include #include "ComplexGeoData.h" using namespace Data; +namespace Data { +typedef boost::bimap< + boost::bimaps::multiset_of, + boost::bimaps::unordered_set_of, + boost::bimaps::with_info > ElementMapBase; +class ElementMap: public ElementMapBase {}; +} + TYPESYSTEM_SOURCE_ABSTRACT(Data::Segment , Base::BaseClass); @@ -160,7 +174,7 @@ const std::string &ComplexGeoData::elementMapPrefix() { } const char *ComplexGeoData::isMappedElement(const char *name) { - if(boost::starts_with(name,elementMapPrefix())) + if(name && boost::starts_with(name,elementMapPrefix())) return name+elementMapPrefix().size(); return 0; } @@ -180,3 +194,126 @@ std::string ComplexGeoData::newElementName(const char *name) { return std::string(name,dot); return name; } + +size_t ComplexGeoData::getElementMapSize() const { + return _ElementMap?_ElementMap->size():0; +} + +const char *ComplexGeoData::getElementName(const char *name, bool reverse) const { + if(!name || !_ElementMap) + return name; + + if(reverse) { + auto it = _ElementMap->right.find(name); + if(it == _ElementMap->right.end()) + return name; + return it->second.c_str(); + } + const char *txt = isMappedElement(name); + if(!txt) + return name; + std::string _txt; + // Strip out the trailing '.XXXX' if any + const char *dot = strchr(txt,'.'); + if(dot) { + _txt = std::string(txt,dot-txt); + txt = _txt.c_str(); + } + auto it = _ElementMap->left.find(txt); + if(it == _ElementMap->left.end()) + return name; + return it->second.c_str(); +} + +std::vector ComplexGeoData::getElementMappedNames(const char *element) const { + std::vector names; + auto ret = _ElementMap->left.equal_range(element); + for(auto it=ret.first;it!=ret.second;++it) + names.push_back(it->second.c_str()); + return names; +} + +std::map ComplexGeoData::getElementMap() const { + std::map ret; + if(!_ElementMap) return ret; + for(auto &v : _ElementMap->left) + ret.emplace_hint(ret.cend(),v.first,v.second); + return ret; +} + +void ComplexGeoData::setElementMap(const std::map &map) { + if(!_ElementMap) + _ElementMap = std::make_shared(); + else + _ElementMap->clear(); + for(auto &v : map) + setElementName(v.first.c_str(),v.second.c_str()); +} + +const char *ComplexGeoData::setElementName(const char *element, const char *name, + bool overwrite, App::StringIDRef sid) +{ + if(!element || !element[0] || !name || !name[0]) + throw Base::ValueError("Invalid input"); + const char *mapped = isMappedElement(name); + if(mapped) + name = mapped; + if(!_ElementMap) _ElementMap = std::make_shared(); + if(overwrite) + _ElementMap->left.erase(name); + std::string _name; + if(!sid && !Hasher.isNull()) { + sid = Hasher->getID(name); + _name = sid->toString(); + name = _name.c_str(); + if(overwrite) + _ElementMap->left.erase(name); + } + auto ret = _ElementMap->left.insert(ElementMap::left_map::value_type(name,element,sid)); + if(!ret.second && ret.first->second!=element) { + std::ostringstream ss; + ss << "duplicate element mapping '" << name << "->" << element << '/' << ret.first->second; + throw Base::ValueError(ss.str().c_str()); + } + return ret.first->first.c_str(); +} + +void ComplexGeoData::Save(Base::Writer &writer) const { + writer.Stream() << writer.ind() << "empty()) + writer.Stream() << "/>" << std::endl; + else { + writer.Stream() << " count=\"" << _ElementMap->left.size() << "\">" << std::endl; + for(auto &v : _ElementMap->left) { + // We are omitting indentation here to save some space in case of long list of elements + writer.Stream() << "value(); + writer.Stream() << "\"/>" << std::endl; + } + writer.Stream() << writer.ind() << "" << std::endl ; + } +} + +void ComplexGeoData::Restore(Base::XMLReader &reader) { + resetElementMap(); + reader.readElement("ElementMap"); + if(reader.hasAttribute("count")) { + size_t count = reader.getAttributeAsUnsigned("count"); + for(size_t i=0;igetID(reader.getAttributeAsInteger("sid")); + setElementName(reader.getAttribute("value"),reader.getAttribute("key"),false,sid); + } + reader.readEndElement("ElementMap"); + } +} + +unsigned int ComplexGeoData::getMemSize(void) const { + if(_ElementMap) + return _ElementMap->size()*10; + return 0; +} diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index 484c60f79293..67b690a90082 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -24,12 +24,14 @@ #ifndef _AppComplexGeoData_h_ #define _AppComplexGeoData_h_ +#include #include #include #include #include #include #include +#include "StringHasher.h" #ifdef __GNUC__ # include @@ -39,6 +41,9 @@ namespace Data { +class ElementMap; +typedef std::shared_ptr ElementMapPtr; + /** Segments * Subelement type of the ComplexGeoData type * It is used to split an object in further sub-parts. @@ -170,8 +175,80 @@ class AppExport ComplexGeoData: public Base::Persistence, public Base::Handled /// Strip out the trailing element name if there is mapped element name preceeds it. static std::string newElementName(const char *name); + + /** Get element name + * + * @param name: the input name + * @param reverse: if false (default), the function try to map the name + * to the original \c Type + \c Index. If true, then the function map the + * other way round. + * + * @return Returns the found mapping, or else return the original input. The + * return pointer maybe invalidated when new element mapping is added. + */ + const char *getElementName(const char *name, bool reverse=false) const; + + /** Get mapped element names + * + * @param element: original element name with \c Type + \c Index + * + * @return a list of mapped names of the give element + */ + std::vector getElementMappedNames(const char *element) const; + + /** Add a sub-element name mapping. + * + * @param element: the original \c Type + \c Index element name + * @param name: the renamed sub-element name. May or may not start with + * elementMapPrefix(). + * @param hasher: in case the raw 'name' is too long. The caller can opt to + * use this hasher to hash the string and shorten it to an integer, which + * is reference counted and persistent. + * @param overwrite: if true, it will overwrite existing names + * @param sid: in case you use a hasher to hash the element name, pass in + * the string id reference using this parameter + * + * @return Returns the stored mapped element name. Note that if hasher is + * provided the stored name will be different from the input name. + * + * An element can have multiple mapped names. However, a name can only be + * mapped to one element + */ + const char *setElementName(const char *element, const char *name, + bool overwrite=false, App::StringIDRef sid=App::StringIDRef()); + + /** Reset/swap the element map + * + * @param elementMap: optional new element map + * + * @return Returns the existing element map. + */ + ElementMapPtr resetElementMap(ElementMapPtr elementMap=ElementMapPtr()) { + _ElementMap.swap(elementMap); + return elementMap; + } + + /// Get the entire element map + std::map getElementMap() const; + + /// Set the entire element map + void setElementMap(const std::map &map); + + /// Get the current element map size + size_t getElementMapSize() const; //@} + /** @name Save/restore */ + //@{ + void Save (Base::Writer &writer) const; + void Restore(Base::XMLReader &reader); + unsigned int getMemSize (void) const; + //@} + +public: + /// String hasher for element name shortening + App::StringHasherRef Hasher; + protected: /// from local to outside @@ -187,6 +264,9 @@ class AppExport ComplexGeoData: public Base::Persistence, public Base::Handled Base::Vector3d tmp = tmpM * vec; return Base::Vector3f((float)tmp.x,(float)tmp.y,(float)tmp.z); } + +protected: + ElementMapPtr _ElementMap; }; } //namespace App diff --git a/src/App/ComplexGeoDataPy.xml b/src/App/ComplexGeoDataPy.xml index f65ca406a473..4f9b2318130e 100644 --- a/src/App/ComplexGeoDataPy.xml +++ b/src/App/ComplexGeoDataPy.xml @@ -18,6 +18,25 @@ Return vertexes and faces from a sub-element + + + + +setElementName(element,name,overwrite=False), Set an element name + +element : the original element name, e.g. Edge1, Vertex2 +name : the new name for the element +overwrite: if true, it will overwrite exiting name + +An element can have multiple mapped names. However, a name can only be mapped +to one element + + + + + + getElementName(name,reverse=False) - Return a mapped element name or reverse + @@ -37,5 +56,29 @@ + + + Get/Set the string hasher of this object + + + + + + Get the current element map size + + + + + + Get/Set a dict of element mapping + + + + + + A list of support element types + + + diff --git a/src/App/ComplexGeoDataPyImp.cpp b/src/App/ComplexGeoDataPyImp.cpp index 0d74a2b3a8a9..cbecd6fa9b9f 100644 --- a/src/App/ComplexGeoDataPyImp.cpp +++ b/src/App/ComplexGeoDataPyImp.cpp @@ -33,6 +33,7 @@ #include #include #include +#include using namespace Data; using namespace Base; @@ -82,6 +83,75 @@ PyObject* ComplexGeoDataPy::getFacesFromSubelement(PyObject *args) return Py::new_reference_to(tuple); } +PyObject* ComplexGeoDataPy::getElementName(PyObject *args) +{ + char* input; + PyObject *reverse = Py_False; + if (!PyArg_ParseTuple(args, "s|O", &input,&reverse)) + return NULL; + const char *ret = getComplexGeoDataPtr()->getElementName(input,PyObject_IsTrue(reverse)); + return Py::new_reference_to(Py::String(ret)); +} + +PyObject *ComplexGeoDataPy::setElementName(PyObject *args) { + char *element; + char *name; + PyObject *overwrite = Py_False; + if (!PyArg_ParseTuple(args, "ss|O", &element,&name,&overwrite)) + return NULL; + PY_TRY { + const char *ret = getComplexGeoDataPtr()->setElementName(element,name, + PyObject_IsTrue(overwrite)); + return Py::new_reference_to(Py::String(ret)); + }PY_CATCH +} + +Py::Object ComplexGeoDataPy::getHasher() const { + auto self = getComplexGeoDataPtr(); + if(!self->Hasher) + return Py::None(); + return Py::Object(self->Hasher->getPyObject(),true); +} + +Py::Dict ComplexGeoDataPy::getElementMap() const { + Py::Dict ret; + for(auto &v : getComplexGeoDataPtr()->getElementMap()) + ret.setItem(v.first,Py::String(v.second)); + return ret; +} + +void ComplexGeoDataPy::setElementMap(Py::Dict dict) { + std::map map; + for(auto it=dict.begin();it!=dict.end();++it) { + const auto &value = *it; + if(!value.first.isString() || !value.second.isString()) + throw Py::TypeError("expect only strings in the dict"); + map.emplace_hint(map.cend(),value.first.as_string(),Py::Object(value.second).as_string()); + } + getComplexGeoDataPtr()->setElementMap(map); +} + +Py::Int ComplexGeoDataPy::getElementMapSize() const { + return Py::Int((long)getComplexGeoDataPtr()->getElementMapSize()); +} + +void ComplexGeoDataPy::setHasher(Py::Object obj) { + auto self = getComplexGeoDataPtr(); + if(obj.isNone()) { + if(self->Hasher) { + self->Hasher = App::StringHasherRef(); + self->resetElementMap(); + } + }else if(PyObject_TypeCheck(obj.ptr(),&App::StringHasherPy::Type)) { + App::StringHasherRef ref(static_cast(obj.ptr())->getStringHasherPtr()); + if(self->Hasher != ref) { + self->Hasher = ref; + self->resetElementMap(); + } + }else + throw Py::TypeError("invalid type"); +} + Py::Object ComplexGeoDataPy::getBoundBox(void) const { return Py::BoundingBox(getComplexGeoDataPtr()->getBoundBox()); @@ -126,6 +196,15 @@ void ComplexGeoDataPy::setMatrix(Py::Object arg) } } +Py::Tuple ComplexGeoDataPy::getElementTypes() const { + const auto &types = getComplexGeoDataPtr()->getElementTypes(); + Py::Tuple ret(types.size()); + int i=0; + for(auto const &type : types) + ret.setItem(i++,Py::String(type)); + return ret; +} + PyObject *ComplexGeoDataPy::getCustomAttributes(const char* /*attr*/) const { return 0; diff --git a/src/Mod/Part/App/FeatureCompound.cpp b/src/Mod/Part/App/FeatureCompound.cpp index 065fc7f91ead..e43da19291e4 100644 --- a/src/Mod/Part/App/FeatureCompound.cpp +++ b/src/Mod/Part/App/FeatureCompound.cpp @@ -62,17 +62,14 @@ App::DocumentObjectExecReturn *Compound::execute(void) std::vector history; int countFaces = 0; - BRep_Builder builder; - TopoDS_Compound comp; - builder.MakeCompound(comp); - const std::vector& links = Links.getValues(); + std::vector shapes; for (std::vector::const_iterator it = links.begin(); it != links.end(); ++it) { - const TopoDS_Shape& sh = Feature::getShape(*it); - if (!sh.IsNull()) { - builder.Add(comp, sh); + auto sh = Feature::getTopoShape(*it); + if(!sh.isNull()) { + shapes.push_back(sh); TopTools_IndexedMapOfShape faceMap; - TopExp::MapShapes(sh, TopAbs_FACE, faceMap); + TopExp::MapShapes(sh.getShape(), TopAbs_FACE, faceMap); ShapeHistory hist; hist.type = TopAbs_FACE; for (int i=1; i<=faceMap.Extent(); i++) { @@ -82,6 +79,9 @@ App::DocumentObjectExecReturn *Compound::execute(void) } } + TopoShape comp; + comp.Tag = getID(); + comp.makECompound(shapes); this->Shape.setValue(comp); // make sure the 'PropertyShapeHistory' is not safed in undo/redo (#0001889) diff --git a/src/Mod/Part/App/FeaturePartBox.cpp b/src/Mod/Part/App/FeaturePartBox.cpp index 2d728241934f..0415badae1c4 100644 --- a/src/Mod/Part/App/FeaturePartBox.cpp +++ b/src/Mod/Part/App/FeaturePartBox.cpp @@ -74,7 +74,7 @@ App::DocumentObjectExecReturn *Box::execute(void) // Build a box using the dimension attributes BRepPrimAPI_MakeBox mkBox(L, W, H); TopoDS_Shape ResultShape = mkBox.Shape(); - this->Shape.setValue(ResultShape); + this->Shape.setValue(ResultShape,false); return Primitive::execute(); } catch (Standard_Failure& e) { diff --git a/src/Mod/Part/App/FeaturePartCircle.cpp b/src/Mod/Part/App/FeaturePartCircle.cpp index 9bc1ef7da020..9ccad9786708 100644 --- a/src/Mod/Part/App/FeaturePartCircle.cpp +++ b/src/Mod/Part/App/FeaturePartCircle.cpp @@ -69,7 +69,7 @@ App::DocumentObjectExecReturn *Circle::execute(void) BRepBuilderAPI_MakeEdge clMakeEdge(circle, Base::toRadians(this->Angle0.getValue()), Base::toRadians(this->Angle1.getValue())); const TopoDS_Edge& edge = clMakeEdge.Edge(); - this->Shape.setValue(edge); + this->Shape.setValue(edge,false); return Primitive::execute(); } diff --git a/src/Mod/Part/App/FeaturePartPolygon.cpp b/src/Mod/Part/App/FeaturePartPolygon.cpp index e85f2d4a5039..103518ff5213 100644 --- a/src/Mod/Part/App/FeaturePartPolygon.cpp +++ b/src/Mod/Part/App/FeaturePartPolygon.cpp @@ -68,7 +68,7 @@ App::DocumentObjectExecReturn *Part::Polygon::execute(void) if (!poly.IsDone()) throw Base::Exception("Cannot create polygon because less than two vetices are given"); TopoDS_Wire wire = poly.Wire(); - this->Shape.setValue(wire); + this->Shape.setValue(wire,false); return App::DocumentObject::StdReturn; } diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index d8a36d6cba5b..f3ffc7af6809 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -163,7 +163,7 @@ App::DocumentObject *Feature::getSubObject(const char *subname, } ts.transformShape(*pmat,copy,true); } - *pyObj = Py::new_reference_to(shape2pyshape(ts.getShape())); + *pyObj = Py::new_reference_to(shape2pyshape(ts)); return const_cast(this); }catch(Standard_Failure &e) { std::ostringstream str; @@ -181,9 +181,16 @@ App::DocumentObject *Feature::getSubObject(const char *subname, TopoDS_Shape Feature::getShape(const App::DocumentObject *obj, const char *subname, bool needSubElement, Base::Matrix4D *pmat, App::DocumentObject **powner, - bool resolveLink, bool transform) + bool resolveLink, bool transform) { - if(!obj) return TopoDS_Shape(); + return getTopoShape(obj,subname,needSubElement,pmat,powner,resolveLink,transform,true).getShape(); +} + +TopoShape Feature::getTopoShape(const App::DocumentObject *obj, const char *subname, + bool needSubElement, Base::Matrix4D *pmat, App::DocumentObject **powner, + bool resolveLink, bool transform, bool noElementMap) +{ + if(!obj) return TopoShape(); PyObject *pyobj = 0; Base::Matrix4D mat; @@ -215,26 +222,27 @@ TopoDS_Shape Feature::getShape(const App::DocumentObject *obj, const char *subna *pmat = mat; if(pyobj && PyObject_TypeCheck(pyobj,&TopoShapePy::Type)) { - auto shape = static_cast(pyobj)->getTopoShapePtr()->getShape(); + auto shape = *static_cast(pyobj)->getTopoShapePtr(); Py_DECREF(pyobj); + // TODO: owner tag is not a reliable identifier because the object + // hierarchy may cross document boundary. Need to find a way to + // accumulate sub-object ID along the hierarchy + shape.Tag = owner?owner->getID():obj->getID(); return shape; } Py_XDECREF(pyobj); if(!owner) - return TopoDS_Shape(); + return TopoShape(); const char *subelement = subname?strrchr(subname,'.'):0; // nothing can be done if there is sub-element references if(subelement && subelement[1]) - return TopoDS_Shape(); + return TopoShape(); // If no subelement reference, then try to create compound of sub objects - BRep_Builder builder; - TopoDS_Compound comp; - builder.MakeCompound(comp); - int count = 0; + std::vector shapes; for(auto name : owner->getSubObjects()) { if(name.empty()) continue; int visible; @@ -245,22 +253,23 @@ TopoDS_Shape Feature::getShape(const App::DocumentObject *obj, const char *subna if(visible==0) continue; DocumentObject *subObj = 0; - auto shape = getShape(owner,name.c_str(),needSubElement,0,&subObj,false,false); + auto shape = getTopoShape(owner,name.c_str(),needSubElement,0,&subObj,false,false,noElementMap); if(visible<0 && subObj && !subObj->Visibility.getValue()) continue; - if(!shape.IsNull()) { - ++count; - builder.Add(comp,shape); - } + if(!shape.isNull()) + shapes.push_back(shape); } - if(!count) - return TopoDS_Shape(); - TopoShape ts(comp); + if(shapes.empty()) + return TopoShape(); + TopoShape ts; + if(!noElementMap) + ts.Tag = owner->getID(); + ts.makECompound(shapes); ts.transformShape(mat,false,true); - return ts.getShape(); + return ts; } -App::DocumentObject *Part::Feature::getShapeOwner(const App::DocumentObject *obj, const char *subname) +App::DocumentObject *Feature::getShapeOwner(const App::DocumentObject *obj, const char *subname) { if(!obj) return 0; auto owner = obj->getSubObject(subname); @@ -272,6 +281,10 @@ App::DocumentObject *Part::Feature::getShapeOwner(const App::DocumentObject *obj return owner; } +void Feature::onDocumentRestored() { + Shape.getShape().Tag = getID(); +} + void Feature::onChanged(const App::Property* prop) { // if the placement has changed apply the change to the point data as well diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index b20216d79bcb..fa368d50d442 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -93,6 +93,11 @@ class PartExport Feature : public App::GeoFeature const char *subname=0, bool needSubElement=false, Base::Matrix4D *pmat=0, App::DocumentObject **owner=0, bool resolveLink=true, bool transform=true); + static TopoShape getTopoShape(const App::DocumentObject *obj, + const char *subname=0, bool needSubElement=false, Base::Matrix4D *pmat=0, + App::DocumentObject **owner=0, bool resolveLink=true, bool transform=true, + bool noElementMap=false); + static App::DocumentObject *getShapeOwner(const App::DocumentObject *obj, const char *subname=0); static bool hasShapeOwner(const App::DocumentObject *obj, const char *subname=0) { @@ -108,6 +113,7 @@ class PartExport Feature : public App::GeoFeature /// recalculate the feature virtual App::DocumentObjectExecReturn *execute(void); virtual void onChanged(const App::Property* prop); + virtual void onDocumentRestored() override; }; class FilletBase : public Part::Feature diff --git a/src/Mod/Part/App/PrimitiveFeature.cpp b/src/Mod/Part/App/PrimitiveFeature.cpp index 1488a1c94a87..1eee0b8b4e8c 100644 --- a/src/Mod/Part/App/PrimitiveFeature.cpp +++ b/src/Mod/Part/App/PrimitiveFeature.cpp @@ -104,6 +104,7 @@ short Primitive::mustExecute(void) const } App::DocumentObjectExecReturn* Primitive::execute(void) { + Shape.getShape().Tag = getID(); return Part::Feature::execute(); } @@ -227,7 +228,7 @@ App::DocumentObjectExecReturn *Vertex::execute(void) BRepBuilderAPI_MakeVertex MakeVertex(point); const TopoDS_Vertex& vertex = MakeVertex.Vertex(); - this->Shape.setValue(vertex); + this->Shape.setValue(vertex,false); return Primitive::execute(); } @@ -292,7 +293,7 @@ App::DocumentObjectExecReturn *Line::execute(void) if (!mkEdge.IsDone()) return new App::DocumentObjectExecReturn("Failed to create edge"); const TopoDS_Edge& edge = mkEdge.Edge(); - this->Shape.setValue(edge); + this->Shape.setValue(edge,false); return Primitive::execute(); } @@ -378,7 +379,7 @@ App::DocumentObjectExecReturn *Plane::execute(void) } TopoDS_Shape ResultShape = mkFace.Shape(); - this->Shape.setValue(ResultShape); + this->Shape.setValue(ResultShape,false); return Primitive::execute(); } @@ -421,7 +422,7 @@ App::DocumentObjectExecReturn *Sphere::execute(void) Angle2.getValue()/180.0f*M_PI, Angle3.getValue()/180.0f*M_PI); TopoDS_Shape ResultShape = mkSphere.Shape(); - this->Shape.setValue(ResultShape); + this->Shape.setValue(ResultShape,false); } catch (Standard_Failure& e) { @@ -503,7 +504,7 @@ App::DocumentObjectExecReturn *Ellipsoid::execute(void) mat.SetValue(3,3,scaleZ); BRepBuilderAPI_GTransform mkTrsf(mkSphere.Shape(), mat); TopoDS_Shape ResultShape = mkTrsf.Shape(); - this->Shape.setValue(ResultShape); + this->Shape.setValue(ResultShape,false); } catch (Standard_Failure& e) { @@ -546,7 +547,7 @@ App::DocumentObjectExecReturn *Cylinder::execute(void) Height.getValue(), Angle.getValue()/180.0f*M_PI); TopoDS_Shape ResultShape = mkCylr.Shape(); - this->Shape.setValue(ResultShape); + this->Shape.setValue(ResultShape,false); } catch (Standard_Failure& e) { @@ -604,7 +605,7 @@ App::DocumentObjectExecReturn *Prism::execute(void) mkPoly.Add(gp_Pnt(v.x,v.y,v.z)); BRepBuilderAPI_MakeFace mkFace(mkPoly.Wire()); BRepPrimAPI_MakePrism mkPrism(mkFace.Face(), gp_Vec(0,0,Height.getValue())); - this->Shape.setValue(mkPrism.Shape()); + this->Shape.setValue(mkPrism.Shape(),false); } catch (Standard_Failure& e) { @@ -656,7 +657,7 @@ App::DocumentObjectExecReturn *RegularPolygon::execute(void) v = mat * v; } mkPoly.Add(gp_Pnt(v.x,v.y,v.z)); - this->Shape.setValue(mkPoly.Shape()); + this->Shape.setValue(mkPoly.Shape(),false); } catch (Standard_Failure& e) { @@ -706,7 +707,7 @@ App::DocumentObjectExecReturn *Cone::execute(void) Height.getValue(), Angle.getValue()/180.0f*M_PI); TopoDS_Shape ResultShape = mkCone.Shape(); - this->Shape.setValue(ResultShape); + this->Shape.setValue(ResultShape,false); } catch (Standard_Failure& e) { @@ -778,7 +779,7 @@ App::DocumentObjectExecReturn *Torus::execute(void) Angle3.getValue()/180.0f*Standard_PI); const TopoDS_Solid& ResultShape = mkTorus.Solid(); #endif - this->Shape.setValue(ResultShape); + this->Shape.setValue(ResultShape,false); } catch (Standard_Failure& e) { @@ -855,7 +856,7 @@ App::DocumentObjectExecReturn *Helix::execute(void) // work around for OCC bug #23314 (FC #0954) // the exact conditions for failure are unknown. building the helix 1 turn at a time // seems to always work. - this->Shape.setValue(helix.makeLongHelix(myPitch, myHeight, myRadius, myAngle, myLocalCS)); + this->Shape.setValue(helix.makeLongHelix(myPitch, myHeight, myRadius, myAngle, myLocalCS),false); // if (myHeight / myPitch > 50.0) // this->Shape.setValue(helix.makeLongHelix(myPitch, myHeight, myRadius, myAngle, myLocalCS)); // else @@ -955,7 +956,7 @@ App::DocumentObjectExecReturn *Spiral::execute(void) #endif ); BRepProj_Projection proj(wire, mkFace.Face(), gp::DZ()); - this->Shape.setValue(proj.Shape()); + this->Shape.setValue(proj.Shape(),false); return Primitive::execute(); } @@ -1039,7 +1040,7 @@ App::DocumentObjectExecReturn *Wedge::execute(void) xmax, ymax, zmax, z2max, x2max); BRepBuilderAPI_MakeSolid mkSolid; mkSolid.Add(mkWedge.Shell()); - this->Shape.setValue(mkSolid.Solid()); + this->Shape.setValue(mkSolid.Solid(),false); } catch (Standard_Failure& e) { return new App::DocumentObjectExecReturn(e.GetMessageString()); @@ -1105,7 +1106,7 @@ App::DocumentObjectExecReturn *Ellipse::execute(void) BRepBuilderAPI_MakeEdge clMakeEdge(ellipse, Base::toRadians(this->Angle0.getValue()), Base::toRadians(this->Angle1.getValue())); const TopoDS_Edge& edge = clMakeEdge.Edge(); - this->Shape.setValue(edge); + this->Shape.setValue(edge,false); return Primitive::execute(); } diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index 419a7f4a3a75..6a7bec10c70f 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -87,10 +88,10 @@ void PropertyPartShape::setValue(const TopoShape& sh) hasSetValue(); } -void PropertyPartShape::setValue(const TopoDS_Shape& sh) +void PropertyPartShape::setValue(const TopoDS_Shape& sh, bool resetElementMap) { aboutToSetValue(); - _Shape.setShape(sh); + _Shape.setShape(sh,resetElementMap); hasSetValue(); } @@ -212,19 +213,31 @@ void PropertyPartShape::Save (Base::Writer &writer) const writer.Stream() << writer.ind() << "empty()) - writer.Stream() << "\"/>" << std::endl; - else { - writer.Stream() << "\" map=\"" << elementMap->left.size() << "\">" << std::endl; - writer.incInd(); - for(auto &v : elementMap->left) - writer.Stream() << writer.ind() << "" << std::endl; - writer.decInd(); - writer.Stream() << writer.ind() << "" << endl ; + bool saveHasher=false, saveMap=false; + auto owner = dynamic_cast(getContainer()); + if(owner && !_Shape.Hasher.isNull()) { + auto ret = owner->getDocument()->addStringHasher(_Shape.Hasher); + writer.Stream() << "\" HasherIndex=\"" << ret.second; + if(ret.first) { + saveHasher = true; + writer.Stream() << "\" SaveHasher=\"1"; + } + } + if(_Shape.getElementMapSize()) { + if(owner && owner->getDocument()->isExporting()) + // when exporting, do not export mapped element name, but still make a mark + writer.Stream() << "\" ElementMap=\"1"; + else { + writer.Stream() << "\" ElementMap=\"2"; + saveMap = true; + } } - _Shape.resetElementMap(elementMap); + writer.Stream() << "\"/>" << std::endl; + + if(saveHasher) + _Shape.Hasher->Save(writer); + if(saveMap) + _Shape.Save(writer); } } @@ -233,19 +246,26 @@ void PropertyPartShape::Restore(Base::XMLReader &reader) reader.readElement("Part"); std::string file (reader.getAttribute("file") ); - if(reader.hasAttribute("map")) { - size_t count = reader.getAttributeAsUnsigned("map"); - for(size_t i=0;i(getContainer()); + int hasher_idx = reader.hasAttribute("HasherIndex")?reader.getAttributeAsInteger("HasherIndex"):-1; + int map = reader.hasAttribute("ElementMap")?reader.getAttributeAsInteger("ElementMap"):0; + if(owner && hasher_idx>=0) { + _Shape.Hasher = owner->getDocument()->getStringHasher(hasher_idx); + if(reader.hasAttribute("SaveHasher")) + _Shape.Hasher->Restore(reader); + } + if(map) { + if(map == 1) { + // type 1 marks the need for recompute after import + if(owner) owner->getDocument()->addRecomputeObject(owner); + }else if(map==2) + _Shape.Restore(reader); + } } void PropertyPartShape::SaveDocFile (Base::Writer &writer) const @@ -326,6 +346,7 @@ void PropertyPartShape::RestoreDocFile(Base::Reader &reader) { // save the element map auto elementMap = _Shape.resetElementMap(); + auto hasher = _Shape.Hasher; Base::FileInfo brep(reader.getFileName()); if (brep.hasExtension("bin")) { @@ -387,6 +408,10 @@ void PropertyPartShape::RestoreDocFile(Base::Reader &reader) // restore the element map _Shape.resetElementMap(elementMap); + _Shape.Hasher = hasher; + auto owner = dynamic_cast(getContainer()); + if(owner) + _Shape.Tag = owner->getID(); } // ------------------------------------------------------------------------- diff --git a/src/Mod/Part/App/PropertyTopoShape.h b/src/Mod/Part/App/PropertyTopoShape.h index c8807d5b17d4..8c9ef33a326b 100644 --- a/src/Mod/Part/App/PropertyTopoShape.h +++ b/src/Mod/Part/App/PropertyTopoShape.h @@ -52,7 +52,7 @@ class PartExport PropertyPartShape : public App::PropertyComplexGeoData /// 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(void) const; const TopoShape& getShape() const; diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 917679457553..56057ef69cf6 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -167,6 +167,7 @@ # include # include # include +# include #if OCC_VERSION_HEX >= 0x060600 #include @@ -194,6 +195,11 @@ using namespace Part; +namespace Part { + PartExport std::list sort_Edges2(double tol3d, std::list& edges, + std::deque *hashes); +} + const char* BRepBuilderAPI_FaceErrorText(BRepBuilderAPI_FaceError et) { switch (et) @@ -231,6 +237,7 @@ std::string ShapeSegment::getName() const TYPESYSTEM_SOURCE(Part::TopoShape , Data::ComplexGeoData); TopoShape::TopoShape() + :Tag(0) { } @@ -239,13 +246,15 @@ TopoShape::~TopoShape() } TopoShape::TopoShape(const TopoDS_Shape& shape) - : _Shape(shape) + : Tag(0), _Shape(shape) { } TopoShape::TopoShape(const TopoShape& shape) - : _Shape(shape._Shape), _ElementMap(shape._ElementMap) + : Tag(shape.Tag),_Shape(shape._Shape) { + Hasher = shape.Hasher; + _ElementMap = shape._ElementMap; } std::vector TopoShape::getElementTypes(void) const @@ -271,81 +280,102 @@ Data::Segment* TopoShape::getSubElement(const char* Type, unsigned long n) const return new ShapeSegment(getSubShape(temp.c_str())); } -const char *TopoShape::getElementName(const char *Type, bool reverse) const { - if(!Type || !_ElementMap) - return Type; +TopoDS_Shape TopoShape::getSubShape(const char* Type) const { + TopoShape s(*this); + s.Tag = 0; + return s.getSubTopoShape(Type).getShape(); +} - if(reverse) { - auto it = _ElementMap->right.find(Type); - if(it == _ElementMap->right.end()) - return Type; - return it->second.c_str(); - } - const char *type = Data::ComplexGeoData::isMappedElement(Type); - if(!type) - return Type; - std::string _type; - const char *dot = strchr(type,'.'); - if(dot) { - _type = std::string(type,dot-type); - type = _type.c_str(); - } - auto it = _ElementMap->left.find(type); - if(it == _ElementMap->left.end()) - return Type; - return it->second.c_str(); +void TopoShape::mapSubElement(TopAbs_ShapeEnum type, + const TopoShape &other, const char *op, bool mapAll) { + if(!other.Tag || !Tag || isNull() || other.isNull()) + return; + TopTools_IndexedMapOfShape shapeMap; + TopExp::MapShapes(_Shape, type, shapeMap); + mapSubElement(type,shapeMap,other,op,mapAll); } -void TopoShape::setElementName(const char *element, const char *name, bool overwrite) const{ - if(!element || !name) - throw Base::ValueError("Invalid input"); - const char *mapped = Data::ComplexGeoData::isMappedElement(name); - if(mapped) - name = mapped; - if(!_ElementMap) _ElementMap.reset(new ElementMapType); - if(overwrite) { - _ElementMap->left.erase(name); - _ElementMap->right.erase(element); - } - _ElementMap->insert(ElementMapType::relation(name,element)); +void TopoShape::mapSubElement(TopAbs_ShapeEnum type, const TopoShape &other, + const TopTools_IndexedMapOfShape &otherMap, const char *op, bool mapAll) +{ + if(!otherMap.Extent() || !other.Tag || !Tag || isNull() || other.isNull()) + return; + TopTools_IndexedMapOfShape shapeMap; + TopExp::MapShapes(_Shape, type, shapeMap); + mapSubElement(type,shapeMap,other,otherMap,op,mapAll); } -TopoDS_Shape TopoShape::getSubShape(const char* Type) const { - return getSubTopoShape(Type,false).getShape(); +void TopoShape::mapSubElement(TopAbs_ShapeEnum type, + const TopTools_IndexedMapOfShape &shapeMap, + const TopoShape &other, const char *op, bool mapAll) +{ + if(!shapeMap.Extent() || !other.Tag || !Tag || isNull() || other.isNull()) + return; + TopTools_IndexedMapOfShape otherMap; + TopExp::MapShapes(other._Shape, type, otherMap); + mapSubElement(type,shapeMap,other,otherMap,op,mapAll); } -void TopoShape::mapSubElement(TopoShape &ret, TopAbs_ShapeEnum type) const { +void TopoShape::mapSubElement(TopAbs_ShapeEnum type, + const TopTools_IndexedMapOfShape &shapeMap, const TopoShape &other, + const TopTools_IndexedMapOfShape &otherMap, const char *op, bool mapAll) +{ + if(!shapeMap.Extent() || !otherMap.Extent() || !other.Tag || !Tag || isNull() || other.isNull()) + return; + const char *shapetype; - if(type == TopAbs_EDGE) + switch(type) { + case TopAbs_EDGE: shapetype = "Edge"; - else if(type == TopAbs_VERTEX) + break; + case TopAbs_VERTEX: shapetype = "Vertex"; - else + break; + case TopAbs_FACE: + shapetype = "Face"; + break; + default: + Standard_Failure::Raise("invlaid shape type"); return; - TopTools_IndexedMapOfShape shapeMap,subShapeMap; - TopExp::MapShapes(_Shape, type, shapeMap); - TopExp::MapShapes(ret.getShape(), type, subShapeMap); - for(int i=1;i<=subShapeMap.Extent();++i) { - int idx = shapeMap.FindIndex(subShapeMap.FindKey(i)); - std::ostringstream str; - str << shapetype << idx; - std::string name = str.str(); - auto mapped = getElementName(name.c_str()); - if(mapped!=name.c_str()) { + } + std::ostringstream str; + for(int i=1;i<=otherMap.Extent();++i) { + auto shape = otherMap.FindKey(i); + int idx = shapeMap.FindIndex(shape); + if(!idx) continue; + str.str(""); + str << shapetype << i; + std::string name = other.getElementName(str.str().c_str(),true); + if(other.Tag!=Tag) { str.str(""); - str << shapetype << i; - ret.setElementName(str.str().c_str(),mapped); + str << (op?op:"T") << other.Tag << '_' << name; + name = str.str(); + }else if(op) { + str.str(""); + str << op << '_' << name; + name = str.str(); } + str.str(""); + str << shapetype << idx; + + setElementName(str.str().c_str(),name.c_str()); + } + if(mapAll) { + if(type == TopAbs_FACE) + mapSubElement(TopAbs_EDGE,other,op,true); + else if(type == TopAbs_EDGE) + mapSubElement(TopAbs_VERTEX,other,op,false); } } -TopoShape TopoShape::getSubTopoShape(const char *Type, bool buildMap) const { +TopoShape TopoShape::getSubTopoShape(const char *Type) const { if (!Type) Standard_Failure::Raise("No sub-shape type given"); if (this->_Shape.IsNull()) Standard_Failure::Raise("Cannot get sub-shape from empty shape"); TopoShape ret; + ret.Tag = Tag; std::string shapetype(getElementName(Type)); if (shapetype.size() > 4 && shapetype.substr(0,4) == "Face") { int index=std::atoi(&shapetype[4]); @@ -354,13 +384,7 @@ TopoShape TopoShape::getSubTopoShape(const char *Type, bool buildMap) const { // To avoid a segmentation fault we have to check if container is empty if(index>=1 && index<=anIndices.Extent()) { ret.setShape(anIndices.FindKey(index)); - if(buildMap && _ElementMap) { - auto mapped = getElementName(shapetype.c_str(),true); - if(mapped) - ret.setElementName("Face1",mapped); - mapSubElement(ret,TopAbs_EDGE); - mapSubElement(ret,TopAbs_VERTEX); - } + ret.mapSubElement(TopAbs_FACE,*this,anIndices); return ret; } } @@ -371,12 +395,7 @@ TopoShape TopoShape::getSubTopoShape(const char *Type, bool buildMap) const { // To avoid a segmentation fault we have to check if container is empty if(index>=1 && index<=anIndices.Extent()) { ret.setShape(anIndices.FindKey(index)); - if(buildMap && _ElementMap) { - auto mapped = getElementName(shapetype.c_str(),true); - if(mapped) - ret.setElementName("Edge1",mapped); - mapSubElement(ret,TopAbs_VERTEX); - } + ret.mapSubElement(TopAbs_EDGE,*this,anIndices); return ret; } } @@ -386,11 +405,7 @@ TopoShape TopoShape::getSubTopoShape(const char *Type, bool buildMap) const { TopExp::MapShapes(this->_Shape, TopAbs_VERTEX, anIndices); if(index>=1 && index<=anIndices.Extent()) { ret.setShape(anIndices.FindKey(index)); - if(buildMap && _ElementMap) { - auto mapped = getElementName(shapetype.c_str(),true); - if(mapped) - ret.setElementName("Vertex1",mapped); - } + ret.mapSubElement(TopAbs_VERTEX,*this,anIndices); return ret; } } @@ -401,6 +416,112 @@ TopoShape TopoShape::getSubTopoShape(const char *Type, bool buildMap) const { return ret; } +TopoShape &TopoShape::makECompound(const std::vector &shapes, bool appendTag, const char *op) { + _Shape.Nullify(); + resetElementMap(); + + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + int count = 0; + for(auto &s : shapes) { + if(!s.isNull()){ + builder.Add(comp,s.getShape()); + ++count; + } + } + if(!count) return *this; + _Shape = comp; + if(!Tag) return *this; + + TopTools_IndexedMapOfShape fmap,emap,vmap; + TopExp::MapShapes(_Shape, TopAbs_FACE, fmap); + TopExp::MapShapes(_Shape, TopAbs_EDGE, emap); + TopExp::MapShapes(_Shape, TopAbs_VERTEX, vmap); + if(appendTag && op==0) + op = "C"; + for(auto s : shapes) { + if(s.isNull()) continue; + if(!appendTag) + s.Tag = Tag; + else if(!s.Tag) + continue; + mapSubElement(TopAbs_FACE,fmap,s,op,false); + mapSubElement(TopAbs_EDGE,emap,s,op,false); + mapSubElement(TopAbs_VERTEX,vmap,s,op,false); + } + return *this; +} + +TopoShape &TopoShape::makEWires(const TopoShape &shape, const char *op) { + _Shape.Nullify(); + resetElementMap(); + + if(!op) op = "W"; + + std::list edgeList; + std::map refMap; + + std::map edgeMap; + TopTools_IndexedMapOfShape emap,vmap; + TopExp::MapShapes(shape._Shape, TopAbs_EDGE, emap); + TopExp::MapShapes(shape._Shape, TopAbs_VERTEX, vmap); + + for(TopExp_Explorer exp(shape._Shape,TopAbs_EDGE);exp.More();exp.Next()) { + auto edge = TopoDS::Edge(exp.Current()); + edgeList.push_back(edge); + if(!Tag || !shape.Tag) continue; + TopoShape s(edge); + s.Tag = shape.Tag; + s.mapSubElement(TopAbs_EDGE,shape,emap,0,false); + s.mapSubElement(TopAbs_VERTEX,shape,vmap,0,false); + s.Tag = Tag; + edgeMap[edge.HashCode(INT_MAX)] = s; + } + + std::vector wires; + std::vector sourceEdges; + std::deque hashes; + while(edgeList.size()) { + BRepBuilderAPI_MakeWire mkWire; + hashes.clear(); + auto edges = Part::sort_Edges2(Precision::Confusion(),edgeList,&hashes); + auto hit = hashes.begin(); + sourceEdges.clear(); + sourceEdges.reserve(edges.size()); + for(auto &edge : edges){ + mkWire.Add(edge); + if(!Tag || !shape.Tag) continue; + + auto e = mkWire.Edge(); + auto hash = *hit++; + auto it = edgeMap.find(hash); + assert(it!=edgeMap.end()); + // sort_Edge2 may rebuild the edge to reverse the orientation, we + // swap the edge to save the need for name remapping + it->second._Shape = e; + sourceEdges.push_back(it->second); + } + wires.push_back(TopoShape(mkWire.Wire())); + auto &wire = wires.back(); + if(!Tag || !shape.Tag) continue; + wire.Tag = Tag; + TopTools_IndexedMapOfShape emap,vmap; + TopExp::MapShapes(wire._Shape, TopAbs_EDGE, emap); + TopExp::MapShapes(wire._Shape, TopAbs_VERTEX, vmap); + for(auto &e : sourceEdges) { + wire.mapSubElement(TopAbs_EDGE,emap,e,op,false); + wire.mapSubElement(TopAbs_VERTEX,vmap,e,op,false); + } + } + if(wires.size() == 1) { + resetElementMap(wires[0]._ElementMap); + _Shape = wires[0]._Shape; + }else if(wires.size()) + makECompound(wires,false); + return *this; +} + unsigned long TopoShape::countSubShapes(const char* Type) const { std::string shapetype(Type); @@ -451,17 +572,18 @@ PyObject * TopoShape::getPySubShape(const char* Type) const void TopoShape::operator = (const TopoShape& sh) { if (this != &sh) { + this->Tag = sh.Tag; this->_Shape = sh._Shape; this->_ElementMap = sh._ElementMap; + this->Hasher = sh.Hasher; } } TopoShape TopoShape::copy() const{ - TopoShape ret; + TopoShape ret(*this); if(isNull()) return ret; - auto elementMap = _ElementMap; - ret.setShape(BRepBuilderAPI_Copy(_Shape).Shape()); - ret.resetElementMap(elementMap); + ret._Shape = BRepBuilderAPI_Copy(_Shape).Shape(); + ret.Hasher = Hasher; return ret; } @@ -1101,12 +1223,14 @@ bool TopoShape::getCenterOfGravity(Base::Vector3d& center) const return true; } -void TopoShape::Save (Base::Writer & ) const +void TopoShape::Save (Base::Writer &writer ) const { + Data::ComplexGeoData::Save(writer); } -void TopoShape::Restore(Base::XMLReader &) +void TopoShape::Restore(Base::XMLReader &reader) { + Data::ComplexGeoData::Restore(reader); } void TopoShape::SaveDocFile (Base::Writer &) const @@ -1248,11 +1372,11 @@ unsigned int TopoShape::getMemSize (void) const } // estimated memory usage - return memsize; + return memsize + Data::ComplexGeoData::getMemSize(); } // in case the shape is invalid - return sizeof(TopoDS_Shape); + return sizeof(TopoDS_Shape) + Data::ComplexGeoData::getMemSize(); } bool TopoShape::isNull() const @@ -2793,7 +2917,7 @@ TopoDS_Shape TopoShape::makeThickSolid(const TopTools_ListOfShape& remFace, void TopoShape::transformGeometry(const Base::Matrix4D &rclMat) { this->_Shape = transformGShape(rclMat); - _ElementMap.reset(); + // TODO: check the element map here } TopoDS_Shape TopoShape::transformGShape(const Base::Matrix4D& rclTrf) const @@ -2859,7 +2983,7 @@ void TopoShape::transformShape(const Base::Matrix4D& rclTrf, bool copy, bool che // location transformation BRepBuilderAPI_Transform mkTrf(this->_Shape, mat, copy ? Standard_True : Standard_False); this->_Shape = mkTrf.Shape(); - _ElementMap.reset(); + // TODO: check the element map here } TopoDS_Shape TopoShape::mirror(const gp_Ax2& ax2) const diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index d8dd0897eee7..199838750294 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -26,10 +26,10 @@ #include #include -#include #include #include #include +#include #include class gp_Ax1; @@ -61,18 +61,21 @@ class PartExport TopoShape : public Data::ComplexGeoData public: TopoShape(); + TopoShape(long Tag):Tag(Tag) {} TopoShape(const TopoDS_Shape&); TopoShape(const TopoShape&); ~TopoShape(); - inline void setShape(const TopoDS_Shape& shape) { - this->_Shape = shape; - this->_ElementMap.reset(); + inline void setShape(const TopoDS_Shape& shape, bool resetElementMap=true) { + _Shape = shape; + if(resetElementMap) { + Hasher = App::StringHasherRef(); + this->resetElementMap(); + } } inline void setShape(const TopoShape& shape) { - this->_Shape = shape.getShape(); - this->_ElementMap = shape._ElementMap; + *this = shape; } inline const TopoDS_Shape& getShape() const { @@ -123,7 +126,7 @@ class PartExport TopoShape : public Data::ComplexGeoData //@} /// get the Topo"sub"Shape with the given name TopoDS_Shape getSubShape(const char* Type) const; - TopoShape getSubTopoShape(const char *Type, bool buildMap=true) const; + TopoShape getSubTopoShape(const char *Type) const; unsigned long countSubShapes(const char* Type) const; bool hasSubShape(const char *Type) const; /// get the Topo"sub"Shape with the given name @@ -263,55 +266,55 @@ class PartExport TopoShape : public Data::ComplexGeoData void getDomains(std::vector&) const; //@} - /** @name Sub-element name mapping functions */ + /** @name Element name mapping aware shape maker */ //@{ + TopoShape &makECompound(const std::vector &shapes, bool appendTag=true, const char *op=0); + TopoShape &makEWires(const TopoShape &shape, const char *op=0); + //@} + + void mapSubElement(TopAbs_ShapeEnum type, + const TopoShape &other,const char *op=0,bool mapAll=true); + void mapSubElement(TopAbs_ShapeEnum type, const TopTools_IndexedMapOfShape &shapeMap, + const TopoShape &other,const char *op=0,bool mapAll=true); + void mapSubElement(TopAbs_ShapeEnum type, const TopoShape &other, + const TopTools_IndexedMapOfShape &otherMap,const char *op=0,bool mapAll=true); + void mapSubElement(TopAbs_ShapeEnum type, const TopTools_IndexedMapOfShape &shapeMap, const TopoShape &other, + const TopTools_IndexedMapOfShape &otherMap,const char *op=0,bool mapAll=true); - /** Map element name +public: + /** Shape tag * - * @param name: the input name - * @param reverse: if false (default), \c name must start with - * App::ComplexGeoData::elementMapPrefix(). The function shall map the name - * to the original \c Type + \c Index. If true, then the function map the - * other way round. + * A very loosely controlled tag, which is why it is made public. Its main + * purpose is not for unique identification, but as a way for the user to + * disambiguate input shape and generate mappable topological names. It is + * the user's job to assign sensible tags before generating element maps. + * The default value is 0, and will disable auto element mapping. * - * @return Returns the found mapping, or else return the original input. The - * return pointer maybe invalidated when the shape is modified. - */ - const char *getElementName(const char *name, bool reverse=false) const; - - /** Add a sub-element name mapping. + * A very simple example, for a compound C created by shape A and B. + * TopoShape will map A's Edge1 and B's Edge1 to C as * - * @param element: the original \c Type + \c Index element name - * @param name: the renamed sub-element name. May or may not start with - * App::ComplexGeoData::elementMapPrefix(). - * @param overwrite: if true, it will overwrite existing names, or else it - * will be an error to have duplicate enames - */ - void setElementName(const char *element, const char *name, bool overwrite=false) const; - - /// The internal storage of element map type - typedef boost::bimap ElementMapType; - - /** Reset/swap the element map + * _Edge1 + * _Edge1 + * + * If C is a fusion, then for elements that are unmodified, it will be same + * as compound. For modified of generated elements, it will be some thing + * like * - * @param elementMap: optional new element map + * _Face1__Face2 * - * @return Returns the existing element map. + * If A and/or B has its own element mapping, then the trailing _EdgeX or + * _FaceX will be replaced by those mappings. + * + * Now, you may be thinking that if this process continues, the mapped + * element name will grow without control. The caller can pass a + * App::StringHasher when calling Data::ComplexGeoData::setElementName(), + * which can hash on the mapped name and shorten it to a integer index. + * App::Document provides a persistent StringHasher. */ - std::shared_ptr resetElementMap( - std::shared_ptr elementMap = std::shared_ptr()) const - { - _ElementMap.swap(elementMap); - return elementMap; - } - //@} - -private: - void mapSubElement(TopoShape &ret, TopAbs_ShapeEnum type) const; + mutable long Tag; private: TopoDS_Shape _Shape; - mutable std::shared_ptr _ElementMap; }; } //namespace Part diff --git a/src/Mod/Part/App/TopoShapePy.xml b/src/Mod/Part/App/TopoShapePy.xml index 7581aa2cca1c..b4b6b75feb11 100644 --- a/src/Mod/Part/App/TopoShapePy.xml +++ b/src/Mod/Part/App/TopoShapePy.xml @@ -427,6 +427,42 @@ Returns: result of offsetting (wire or face or compound of those). Compounding structure follows that of source shape. + + + +makECompound([shape],appendTag=True,op=None): make compound of a list of input shapes + +appendTag: if true, element mapping will append shape's Tag into mapped name + +This function is element mapping aware. It will map the sub-element name of any +shape that has a non-zero Tag into itself. + +appendTag : whether append input shape's Tag into auto generated element mapping +op : an optional string to be appended when auto generates element mapping. + +The function return itself. + + + + + + +makEWires(shape,op=None): make compound of a list of input shapes + +The function will sort any edges inside the input shape, and connect them into +wire. If more than one wire is found, then it will make a compound out of all +found wires. + +This function is element mapping aware. If the input shape has non-zero Tag, +it will map any edge and vertex element name inside the input shape into the +itself. + +op: an optional string to be appended when auto generates element mapping. + +The function return itself. + + + Reverses the orientation of this shape. @@ -609,9 +645,16 @@ infos contains additional info on the solutions. It is a list of tuples: Returns a SubElement - + - getElementName(element,reverse=False) -> String, Returns a mapped element name + +mapSubElement(type, shape, op=None,mapAll=True) - maps the sub element of other shape + +type: sub element type (defined in attribute ElementTypes) +shape: other shape to map the sub-elements +op: optional string prefix to append before the mapped sub element names +mapAll: whether to map all lower level sub-element types + @@ -786,5 +829,11 @@ infos contains additional info on the solutions. It is a list of tuples: + + + Shape Tag + + + diff --git a/src/Mod/Part/App/TopoShapePyImp.cpp b/src/Mod/Part/App/TopoShapePyImp.cpp index 60c529767e1e..e1ec26ca20db 100644 --- a/src/Mod/Part/App/TopoShapePyImp.cpp +++ b/src/Mod/Part/App/TopoShapePyImp.cpp @@ -1978,6 +1978,43 @@ PyObject* TopoShapePy::makeShapeFromMesh(PyObject *args) } } +PyObject* TopoShapePy::makECompound(PyObject *args) { + PyObject *pyshapes; + PyObject *appendTag = Py_True; + const char *op = 0; + if (!PyArg_ParseTuple(args, "O|Os", &pyshapes,&appendTag,&op)) + return NULL; + + std::vector shapes; + if(PyObject_TypeCheck(pyshapes,&TopoShapePy::Type)) + shapes.push_back(*static_cast(pyshapes)->getTopoShapePtr()); + else if(PySequence_Check(pyshapes)) { + Py::Sequence shapeSeq(pyshapes); + for(auto it=shapeSeq.begin();it!=shapeSeq.end();++it) { + if(!PyObject_TypeCheck((*it).ptr(),&TopoShapePy::Type)) + throw Py::TypeError("expect item type to be of Shape"); + shapes.push_back(*static_cast((*it).ptr())->getTopoShapePtr()); + } + } + PY_TRY { + getTopoShapePtr()->makECompound(shapes,PyObject_IsTrue(appendTag),op); + Py_INCREF(this); + return this; + }PY_CATCH; +} + +PyObject* TopoShapePy::makEWires(PyObject *args) { + PyObject *shape; + const char *op = 0; + if (!PyArg_ParseTuple(args, "O!|s", &TopoShapePy::Type, &shape,&op)) + return NULL; + PY_TRY { + getTopoShapePtr()->makEWires(*static_cast(shape)->getTopoShapePtr(),op); + Py_INCREF(this); + return this; + }PY_CATCH; +} + PyObject* TopoShapePy::toNurbs(PyObject *args) { if (!PyArg_ParseTuple(args, "")) @@ -2058,16 +2095,6 @@ PyObject* TopoShapePy::getElement(PyObject *args) return 0; } -PyObject* TopoShapePy::getElementName(PyObject *args) -{ - char* input; - PyObject *reverse = Py_False; - if (!PyArg_ParseTuple(args, "s|O", &input,&reverse)) - return NULL; - const char *ret = getTopoShapePtr()->getElementName(input,PyObject_IsTrue(reverse)); - return Py::new_reference_to(Py::String(ret)); -} - PyObject* TopoShapePy::getTolerance(PyObject *args) { int mode; @@ -2738,172 +2765,80 @@ void TopoShapePy::setOrientation(Py::String arg) getTopoShapePtr()->setShape(sh); } -Py::List TopoShapePy::getFaces(void) const -{ +static Py::List getElements(const TopoShape &sh, TopAbs_ShapeEnum type, TopAbs_ShapeEnum subType) { Py::List ret; TopTools_IndexedMapOfShape M; - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_FACE); + TopExp_Explorer Ex(sh.getShape(),type); while (Ex.More()) { M.Add(Ex.Current()); Ex.Next(); } - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeFacePy(new TopoShape(shape)),true)); + if(!sh.Tag) { + for (Standard_Integer k = 1; k <= M.Extent(); k++) + ret.append(shape2pyshape(M(k))); + }else{ + TopTools_IndexedMapOfShape fmap,emap,vmap; + if(subType == TopAbs_FACE) { + TopExp::MapShapes(sh.getShape(), TopAbs_FACE, fmap); + TopExp::MapShapes(sh.getShape(), TopAbs_EDGE, emap); + } else if(subType == TopAbs_EDGE) + TopExp::MapShapes(sh.getShape(), TopAbs_EDGE, emap); + TopExp::MapShapes(sh.getShape(), TopAbs_VERTEX, vmap); + for (Standard_Integer k = 1; k <= M.Extent(); k++) + { + TopoShape shape(M(k)); + shape.Tag = sh.Tag; + shape.mapSubElement(TopAbs_FACE,sh,fmap,0,false); + shape.mapSubElement(TopAbs_EDGE,sh,emap,0,false); + shape.mapSubElement(TopAbs_VERTEX,sh,vmap,0,false); + ret.append(shape2pyshape(shape)); + } } - return ret; } -Py::List TopoShapePy::getVertexes(void) const -{ - Py::List ret; - TopTools_IndexedMapOfShape M; - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_VERTEX); - while (Ex.More()) - { - M.Add(Ex.Current()); - Ex.Next(); - } - - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeVertexPy(new TopoShape(shape)),true)); - } +Py::List TopoShapePy::getFaces(void) const +{ + return getElements(*getTopoShapePtr(),TopAbs_FACE,TopAbs_FACE); +} - return ret; +Py::List TopoShapePy::getVertexes(void) const +{ + return getElements(*getTopoShapePtr(),TopAbs_VERTEX,TopAbs_VERTEX); } Py::List TopoShapePy::getShells(void) const { - Py::List ret; - TopTools_IndexedMapOfShape M; - - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_SHELL); - while (Ex.More()) - { - M.Add(Ex.Current()); - Ex.Next(); - } - - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeShellPy(new TopoShape(shape)),true)); - } - - return ret; + return getElements(*getTopoShapePtr(),TopAbs_SHELL,TopAbs_FACE); } Py::List TopoShapePy::getSolids(void) const { - Py::List ret; - TopTools_IndexedMapOfShape M; - - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_SOLID); - while (Ex.More()) - { - M.Add(Ex.Current()); - Ex.Next(); - } - - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeSolidPy(new TopoShape(shape)),true)); - } - - return ret; + return getElements(*getTopoShapePtr(),TopAbs_SOLID,TopAbs_FACE); } Py::List TopoShapePy::getCompSolids(void) const { - Py::List ret; - TopTools_IndexedMapOfShape M; - - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_COMPSOLID); - while (Ex.More()) - { - M.Add(Ex.Current()); - Ex.Next(); - } - - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeCompSolidPy(new TopoShape(shape)),true)); - } - - return ret; + return getElements(*getTopoShapePtr(),TopAbs_COMPSOLID,TopAbs_FACE); } Py::List TopoShapePy::getEdges(void) const { - Py::List ret; - TopTools_IndexedMapOfShape M; - - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_EDGE); - while (Ex.More()) - { - M.Add(Ex.Current()); - Ex.Next(); - } - - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeEdgePy(new TopoShape(shape)),true)); - } - - return ret; + return getElements(*getTopoShapePtr(),TopAbs_EDGE,TopAbs_EDGE); } Py::List TopoShapePy::getWires(void) const { - Py::List ret; - TopTools_IndexedMapOfShape M; - - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_WIRE); - while (Ex.More()) - { - M.Add(Ex.Current()); - Ex.Next(); - } - - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeWirePy(new TopoShape(shape)),true)); - } - - return ret; + return getElements(*getTopoShapePtr(),TopAbs_WIRE,TopAbs_EDGE); } Py::List TopoShapePy::getCompounds(void) const { - Py::List ret; - TopTools_IndexedMapOfShape M; - - TopExp_Explorer Ex(getTopoShapePtr()->getShape(),TopAbs_COMPOUND); - while (Ex.More()) - { - M.Add(Ex.Current()); - Ex.Next(); - } - - for (Standard_Integer k = 1; k <= M.Extent(); k++) - { - const TopoDS_Shape& shape = M(k); - ret.append(Py::Object(new TopoShapeCompoundPy(new TopoShape(shape)),true)); - } - - return ret; + return getElements(*getTopoShapePtr(),TopAbs_COMPOUND,TopAbs_FACE); } Py::Float TopoShapePy::getLength(void) const @@ -2936,16 +2871,55 @@ Py::Float TopoShapePy::getVolume(void) const return Py::Float(props.Mass()); } +Py::Int TopoShapePy::getTag() const { + return Py::Int(getTopoShapePtr()->Tag); +} + +void TopoShapePy::setTag(Py::Int tag) { + getTopoShapePtr()->Tag = tag; +} + +PyObject *TopoShapePy::mapSubElement(PyObject *args) { + const char *type; + const char *op = 0; + PyObject *sh; + PyObject *mapAll = Py_True; + if (!PyArg_ParseTuple(args, "sO!|sO", &type,&TopoShapePy::Type,&sh,&op,&mapAll)) + return 0; + TopoShape &shape = *static_cast(sh)->getTopoShapePtr(); + TopAbs_ShapeEnum shapeType; + if(strcmp(type,"Edge")==0) + shapeType = TopAbs_EDGE; + else if(strcmp(type,"Face")==0) + shapeType = TopAbs_FACE; + else if(strcmp(type,"Vertex")==0) + shapeType = TopAbs_VERTEX; + else + throw Py::ValueError("invalid element type type"); + auto self = getTopoShapePtr(); + if(!self->Tag) + self->Tag = shape.Tag; + PY_TRY { + self->mapSubElement(shapeType,shape,op,PyObject_IsTrue(mapAll)); + Py_Return; + }PY_CATCH +} + PyObject *TopoShapePy::getCustomAttributes(const char* attr) const { if (!attr) return 0; - std::string _name(attr); - boost::replace_all(_name,"_",Data::ComplexGeoData::elementMapPrefix()); - std::string name = getTopoShapePtr()->getElementName(_name.c_str()); + std::string name; + if(*attr != '_') + name = attr; + else{ + std::string _name(attr); + boost::replace_first(_name,"_",Data::ComplexGeoData::elementMapPrefix()); + name = getTopoShapePtr()->getElementName(_name.c_str()); + } if ((name.size() > 4 && name.substr(0,4) == "Face" && name[4]>=48 && name[4]<=57) || (name.size() > 4 && name.substr(0,4) == "Edge" && name[4]>=48 && name[4]<=57) || (name.size() > 6 && name.substr(0,6) == "Vertex" && name[6]>=48 && name[6]<=57)) - return getTopoShapePtr()->getPySubShape(_name.c_str()); + return getTopoShapePtr()->getPySubShape(name.c_str()); return 0; }