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; }