From 4612190cb804ac50f26e3778b4b728ca99f37f1b Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Fri, 12 Jul 2019 10:10:03 +0800 Subject: [PATCH] Part: changes to Part Module * Added Part::Feature::getTopoShape/getShape() function that can obtain shape from any object with proper implementation of getSubObject(). It can even construct compound from group object with proper implementation of getSubObjects(). * Modified ViewProviderExt to work on any object, because it now obtain the shape using Part::Feature::getShape() * Modified various Part features to obtain base/tool shapes using Part::getShape(), which allows them to be any type of object, including Link and groups. * Modified various Part command to relax type requirement on selected objects. * Add support of link and group to dimension, and add dimension refresh command * Support link and group in simple command command, and add a few more copy command variations. * Add special handling of 'Shape' attribute in PropertyContainerPy and use Part::Feature::getShape() to return shape for any object without Shape property. This allows many python feature work with any object without modification. * GeometrySurface/CurvePy, add convenience attribute 'Rotation' * TopoShapePy: * Extended support of sub shape attribute, e.g. Compound1, Solid2, SubShape3 ('SubShape' is used to access child shape of a compound) * makeWires(), new API to sort and return wires given a list of edges. * transformed/translated/rotated/scaled(), return a new shape with some transformation. * findPlane(), find the plane of a planar shape * isCoplanar(), check if two shape are coplanar --- src/App/DocumentObject.h | 2 + src/App/PropertyContainerPyImp.cpp | 21 + src/Mod/Part/App/AppPart.cpp | 1 + src/Mod/Part/App/AppPartPy.cpp | 206 +++++- src/Mod/Part/App/FeatureChamfer.cpp | 12 +- src/Mod/Part/App/FeatureCompound.cpp | 20 +- src/Mod/Part/App/FeatureCompound.h | 8 + src/Mod/Part/App/FeatureExtrusion.cpp | 30 +- src/Mod/Part/App/FeatureFace.cpp | 4 +- src/Mod/Part/App/FeatureFillet.cpp | 11 +- src/Mod/Part/App/FeatureMirroring.cpp | 5 +- src/Mod/Part/App/FeatureOffset.cpp | 4 +- src/Mod/Part/App/FeaturePartBoolean.cpp | 8 +- src/Mod/Part/App/FeaturePartBox.cpp | 32 +- src/Mod/Part/App/FeaturePartCommon.cpp | 4 +- src/Mod/Part/App/FeaturePartFuse.cpp | 4 +- src/Mod/Part/App/FeatureRevolution.cpp | 14 +- src/Mod/Part/App/GeometryCurvePy.xml | 6 + src/Mod/Part/App/GeometryCurvePyImp.cpp | 12 + src/Mod/Part/App/GeometryPy.xml | 4 +- src/Mod/Part/App/GeometrySurfacePy.xml | 6 + src/Mod/Part/App/GeometrySurfacePyImp.cpp | 14 + src/Mod/Part/App/OCCError.h | 81 +-- src/Mod/Part/App/OpenCascadeAll.h | 3 + src/Mod/Part/App/PartFeature.cpp | 346 ++++++++- src/Mod/Part/App/PartFeature.h | 38 + src/Mod/Part/App/PartFeatures.cpp | 22 +- src/Mod/Part/App/PartPyCXX.cpp | 11 +- src/Mod/Part/App/PartPyCXX.h | 7 + src/Mod/Part/App/TopoShape.cpp | 561 ++++++++++++--- src/Mod/Part/App/TopoShape.h | 76 +- src/Mod/Part/App/TopoShapePy.xml | 83 ++- src/Mod/Part/App/TopoShapePyImp.cpp | 216 +++--- src/Mod/Part/Gui/Command.cpp | 177 +++-- src/Mod/Part/Gui/CommandSimple.cpp | 168 +++-- src/Mod/Part/Gui/DlgExtrusion.cpp | 21 +- src/Mod/Part/Gui/DlgFilletEdges.cpp | 12 +- src/Mod/Part/Gui/Resources/Part.qrc | 3 + .../Gui/Resources/icons/Part_Element_Copy.svg | 246 +++++++ .../Resources/icons/Part_Measure_Refresh.svg | 659 ++++++++++++++++++ .../Resources/icons/Part_Transformed_Copy.svg | 290 ++++++++ src/Mod/Part/Gui/TaskAttacher.cpp | 29 +- src/Mod/Part/Gui/TaskCheckGeometry.cpp | 29 +- src/Mod/Part/Gui/TaskDimension.cpp | 197 ++++-- src/Mod/Part/Gui/TaskDimension.h | 15 +- src/Mod/Part/Gui/TaskOffset.cpp | 17 +- src/Mod/Part/Gui/TaskThickness.cpp | 21 +- src/Mod/Part/Gui/ViewProvider.cpp | 4 +- src/Mod/Part/Gui/ViewProvider2DObject.cpp | 12 +- src/Mod/Part/Gui/ViewProviderBoolean.cpp | 53 +- src/Mod/Part/Gui/ViewProviderBoolean.h | 5 - src/Mod/Part/Gui/ViewProviderCompound.cpp | 9 +- src/Mod/Part/Gui/ViewProviderCompound.h | 2 - src/Mod/Part/Gui/ViewProviderExt.cpp | 85 ++- src/Mod/Part/Gui/ViewProviderExt.h | 10 +- src/Mod/Part/Gui/ViewProviderMirror.cpp | 12 +- .../Part/Gui/ViewProviderPlaneParametric.cpp | 9 +- src/Mod/Part/Gui/Workbench.cpp | 12 +- 58 files changed, 3246 insertions(+), 723 deletions(-) create mode 100644 src/Mod/Part/Gui/Resources/icons/Part_Element_Copy.svg create mode 100644 src/Mod/Part/Gui/Resources/icons/Part_Measure_Refresh.svg create mode 100644 src/Mod/Part/Gui/Resources/icons/Part_Transformed_Copy.svg diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index 35d3439a3daa..62ab8775edf6 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -515,6 +515,8 @@ class AppExport DocumentObject: public App::TransactionalObject */ virtual int canLoadPartial() const {return 0;} + virtual void onUpdateElementReference(const Property *) {} + /** Allow object to redirect a subname path * * @param ss: input as the current subname path from \a topParent leading diff --git a/src/App/PropertyContainerPyImp.cpp b/src/App/PropertyContainerPyImp.cpp index e2c7b015cf13..a9c2b8ad9b5d 100644 --- a/src/App/PropertyContainerPyImp.cpp +++ b/src/App/PropertyContainerPyImp.cpp @@ -506,6 +506,27 @@ PyObject *PropertyContainerPy::getCustomAttributes(const char* attr) const } } return dict; + } else if(Base::streq(attr,"Shape") + && getPropertyContainerPtr()->isDerivedFrom(App::DocumentObject::getClassTypeId())) + { + // Special treatment of Shape property + static PyObject *_getShape = 0; + if(!_getShape) { + _getShape = Py_None; + PyObject *mod = PyImport_ImportModule("Part"); + if(!mod) { + PyErr_Clear(); + } else { + Py::Object pyMod = Py::asObject(mod); + if(pyMod.hasAttr("getShape")) + _getShape = Py::new_reference_to(pyMod.getAttr("getShape")); + } + } + if(_getShape != Py_None) { + Py::Tuple args(1); + args.setItem(0,Py::Object(const_cast(this))); + return PyObject_CallObject(_getShape, args.ptr()); + } } return 0; diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 1f9cc5bbd66d..9a107dd19414 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -452,6 +452,7 @@ PyMOD_INIT_FUNC(Part) Part::Fillet ::init(); Part::Chamfer ::init(); Part::Compound ::init(); + Part::Compound2 ::init(); Part::Extrusion ::init(); Part::Revolution ::init(); Part::Mirroring ::init(); diff --git a/src/Mod/Part/App/AppPartPy.cpp b/src/Mod/Part/App/AppPartPy.cpp index a7991c41624f..cbaf75f5998e 100644 --- a/src/Mod/Part/App/AppPartPy.cpp +++ b/src/Mod/Part/App/AppPartPy.cpp @@ -126,7 +126,9 @@ #include "ImportStep.h" #include "edgecluster.h" #include "FaceMaker.h" +#include "PartFeature.h" #include "PartPyCXX.h" +#include "modelRefine.h" #ifdef FCUseFreeType # include "FT2FC.h" @@ -134,10 +136,6 @@ extern const char* BRepBuilderAPI_FaceErrorText(BRepBuilderAPI_FaceError fe); -namespace Part { -extern Py::Object shape2pyshape(const TopoDS_Shape &shape); -} - #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif @@ -147,6 +145,35 @@ extern Py::Object shape2pyshape(const TopoDS_Shape &shape); #endif namespace Part { + +PartExport void getPyShapes(PyObject *obj, std::vector &shapes) { + if(!obj) + return; + if(PyObject_TypeCheck(obj,&Part::TopoShapePy::Type)) + shapes.push_back(*static_cast(obj)->getTopoShapePtr()); + else if (PyObject_TypeCheck(obj, &GeometryPy::Type)) + shapes.push_back(TopoShape(static_cast(obj)->getGeometryPtr()->toShape())); + else if(PySequence_Check(obj)) { + Py::Sequence list(obj); + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { + if (PyObject_TypeCheck((*it).ptr(), &(Part::TopoShapePy::Type))) + shapes.push_back(*static_cast((*it).ptr())->getTopoShapePtr()); + else if (PyObject_TypeCheck((*it).ptr(), &GeometryPy::Type)) + shapes.push_back(TopoShape(static_cast( + (*it).ptr())->getGeometryPtr()->toShape())); + else + throw Py::TypeError("expect shape in sequence"); + } + }else + throw Py::TypeError("expect shape or sequence of shapes"); +} + +PartExport std::vector getPyShapes(PyObject *obj) { + std::vector ret; + getPyShapes(obj,ret); + return ret; +} + struct EdgePoints { gp_Pnt v1, v2; std::list::iterator it; @@ -425,12 +452,74 @@ class Module : public Py::ExtensionModule add_varargs_method("__fromPythonOCC__",&Module::fromPythonOCC, "__fromPythonOCC__(occ) -- Helper method to convert a pythonocc shape to an internal shape" ); + add_varargs_method("clearShapeCache",&Module::clearShapeCache, + "clearShapeCache() -- Clears internal shape cache" + ); + add_keyword_method("getShape",&Module::getShape, + "getShape(obj,subname=None,mat=None,needSubElement=False,transform=True,retType=0):\n" + "Obtain the the TopoShape of a given object with SubName reference\n\n" + "* obj: the input object\n" + "* subname: dot separated sub-object reference\n" + "* mat: the current transformation matrix\n" + "* needSubElement: if False, ignore the sub-element (e.g. Face1, Edge1) reference in 'subname'\n" + "* transform: if False, then skip obj's transformation. Use this if mat already include obj's\n" + " transformation matrix\n" + "* retType: 0: return TopoShape,\n" + " 1: return (shape,subObj,mat), where subObj is the object referenced in 'subname',\n" + " and 'mat' is the accumulated transformation matrix of that sub-object.\n" + " 2: same as 1, but make sure 'subObj' is resolved if it is a link.\n" + "* refine: refine the returned shape" + ); + add_varargs_method("splitSubname",&Module::splitSubname, + "splitSubname(subname) -> list(sub,mapped,subElement)\n" + "Split the given subname into a list\n\n" + "sub: subname without any sub-element reference\n" + "mapped: mapped element name, or '' if none\n" + "subElement: old style element name, or '' if none" + ); + add_varargs_method("joinSubname",&Module::joinSubname, + "joinSubname(sub,mapped,subElement) -> subname\n" + ); initialize("This is a module working with shapes."); // register with Python } virtual ~Module() {} private: + virtual Py::Object invoke_method_keyword( void *method_def, + const Py::Tuple &args, const Py::Dict &keywords ) override + { + try { + return Py::ExtensionModule::invoke_method_keyword(method_def, args, keywords); + } + catch (const Standard_Failure &e) { + std::string str; + Standard_CString msg = e.GetMessageString(); + str += typeid(e).name(); + str += " "; + if (msg) {str += msg;} + else {str += "No OCCT Exception Message";} + Base::Console().Error("%s\n", str.c_str()); + throw Py::Exception(Part::PartExceptionOCCError, str); + } + catch (const Base::Exception &e) { + std::string str; + str += "FreeCAD exception thrown ("; + str += e.what(); + str += ")"; + e.ReportException(); + throw Py::RuntimeError(str); + } + catch (const std::exception &e) { + std::string str; + str += "C++ exception thrown ("; + str += e.what(); + str += ")"; + Base::Console().Error("%s\n", str.c_str()); + throw Py::RuntimeError(str); + } + } + virtual Py::Object invoke_method_varargs(void *method_def, const Py::Tuple &args) { try { @@ -638,21 +727,13 @@ class Module : public Py::ExtensionModule TopoDS_Compound Comp; builder.MakeCompound(Comp); - try { - Py::Sequence list(pcObj); - for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { - if (PyObject_TypeCheck((*it).ptr(), &(Part::TopoShapePy::Type))) { - const TopoDS_Shape& sh = static_cast((*it).ptr())-> - getTopoShapePtr()->getShape(); - if (!sh.IsNull()) - builder.Add(Comp, sh); - } + PY_TRY { + for(auto &s : getPyShapes(pcObj)) { + const auto &sh = s.getShape(); + if (!sh.IsNull()) + builder.Add(Comp, sh); } - } - catch (Standard_Failure& e) { - throw Py::Exception(PartExceptionOCCError, e.GetMessageString()); - } - + } _PY_CATCH_OCC(throw Py::Exception()) return Py::asObject(new TopoShapeCompoundPy(new TopoShape(Comp))); } Py::Object makeShell(const Py::Tuple& args) @@ -1980,6 +2061,95 @@ class Module : public Py::ExtensionModule throw Py::Exception(PartExceptionOCCError, e.what()); } } + + Py::Object getShape(const Py::Tuple& args, const Py::Dict &kwds) { + PyObject *pObj; + const char *subname = 0; + PyObject *pyMat = 0; + PyObject *needSubElement = Py_False; + PyObject *transform = Py_True; + PyObject *noElementMap = Py_False; + PyObject *refine = Py_False; + short retType = 0; + static char* kwd_list[] = {"obj", "subname", "mat", + "needSubElement","transform","retType","noElementMap","refine",0}; + if(!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!|sO!OOhOO", kwd_list, + &App::DocumentObjectPy::Type, &pObj, &subname, &Base::MatrixPy::Type, &pyMat, + &needSubElement,&transform,&retType,&noElementMap,&refine)) + throw Py::Exception(); + + App::DocumentObject *obj = + static_cast(pObj)->getDocumentObjectPtr(); + App::DocumentObject *subObj = 0; + Base::Matrix4D mat; + if(pyMat) + mat = *static_cast(pyMat)->getMatrixPtr(); + auto shape = Feature::getTopoShape(obj,subname,PyObject_IsTrue(needSubElement), + &mat,&subObj,retType==2,PyObject_IsTrue(transform),PyObject_IsTrue(noElementMap)); + if(PyObject_IsTrue(refine)) { + // shape = TopoShape(0,shape.Hasher).makERefine(shape); + BRepBuilderAPI_RefineModel mkRefine(shape.getShape()); + shape.setShape(mkRefine.Shape()); + } + Py::Object sret(shape2pyshape(shape)); + if(retType==0) + return sret; + + return Py::TupleN(sret,Py::Object(new Base::MatrixPy(new Base::Matrix4D(mat))), + subObj?Py::Object(subObj->getPyObject(),true):Py::Object()); + } + + Py::Object clearShapeCache(const Py::Tuple &args) { + if (!PyArg_ParseTuple(args.ptr(),"")) + throw Py::Exception(); + Part::Feature::clearShapeCache(); + return Py::Object(); + } + + Py::Object splitSubname(const Py::Tuple& args) { + const char *subname; + if (!PyArg_ParseTuple(args.ptr(), "s",&subname)) + throw Py::Exception(); + auto element = Data::ComplexGeoData::findElementName(subname); + std::string sub(subname,element-subname); + Py::List list; + list.append(Py::String(sub)); + const char *dot = strchr(element,'.'); + if(!dot) + dot = element+strlen(element); + const char *mapped = Data::ComplexGeoData::isMappedElement(element); + if(mapped) + list.append(Py::String(std::string(mapped,dot-mapped))); + else + list.append(Py::String()); + if(*dot=='.') + list.append(Py::String(dot+1)); + else if(!mapped) + list.append(Py::String(element)); + else + list.append(Py::String()); + return list; + } + + Py::Object joinSubname(const Py::Tuple& args) { + const char *sub; + const char *mapped; + const char *element; + if (!PyArg_ParseTuple(args.ptr(), "sss",&sub,&mapped,&element)) + throw Py::Exception(); + std::string subname(sub); + if(subname.size() && subname[subname.size()-1]!='.') + subname += '.'; + if(mapped && mapped[0]) { + if(!Data::ComplexGeoData::isMappedElement(mapped)) + subname += Data::ComplexGeoData::elementMapPrefix(); + subname += mapped; + if(element && element[0] && subname[subname.size()-1]!='.') + subname += '.'; + } + subname += element; + return Py::String(subname); + } }; PyObject* initModule() diff --git a/src/Mod/Part/App/FeatureChamfer.cpp b/src/Mod/Part/App/FeatureChamfer.cpp index d7b76c6751a1..39310bff674b 100644 --- a/src/Mod/Part/App/FeatureChamfer.cpp +++ b/src/Mod/Part/App/FeatureChamfer.cpp @@ -51,16 +51,14 @@ App::DocumentObjectExecReturn *Chamfer::execute(void) App::DocumentObject* link = Base.getValue(); if (!link) return new App::DocumentObjectExecReturn("No object linked"); - if (!link->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) - return new App::DocumentObjectExecReturn("Linked object is not a Part object"); - Part::Feature *base = static_cast(Base.getValue()); try { - BRepFilletAPI_MakeChamfer mkChamfer(base->Shape.getValue()); + auto baseShape = Feature::getShape(link); + BRepFilletAPI_MakeChamfer mkChamfer(baseShape); TopTools_IndexedMapOfShape mapOfEdges; TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace; - TopExp::MapShapesAndAncestors(base->Shape.getValue(), TopAbs_EDGE, TopAbs_FACE, mapEdgeFace); - TopExp::MapShapes(base->Shape.getValue(), TopAbs_EDGE, mapOfEdges); + TopExp::MapShapesAndAncestors(baseShape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace); + TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfEdges); std::vector values = Edges.getValues(); for (std::vector::iterator it = values.begin(); it != values.end(); ++it) { @@ -75,7 +73,7 @@ App::DocumentObjectExecReturn *Chamfer::execute(void) TopoDS_Shape shape = mkChamfer.Shape(); if (shape.IsNull()) return new App::DocumentObjectExecReturn("Resulting shape is null"); - ShapeHistory history = buildHistory(mkChamfer, TopAbs_FACE, shape, base->Shape.getValue()); + ShapeHistory history = buildHistory(mkChamfer, TopAbs_FACE, shape, baseShape); this->Shape.setValue(shape); // make sure the 'PropertyShapeHistory' is not safed in undo/redo (#0001889) diff --git a/src/Mod/Part/App/FeatureCompound.cpp b/src/Mod/Part/App/FeatureCompound.cpp index 9a87e96c78e1..b9baa900f05e 100644 --- a/src/Mod/Part/App/FeatureCompound.cpp +++ b/src/Mod/Part/App/FeatureCompound.cpp @@ -72,12 +72,10 @@ App::DocumentObjectExecReturn *Compound::execute(void) const std::vector& links = Links.getValues(); for (std::vector::const_iterator it = links.begin(); it != links.end(); ++it) { - if (*it && (*it)->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - Part::Feature* fea = static_cast(*it); - - auto pos = tempLinks.insert(fea); + if (*it) { + auto pos = tempLinks.insert(*it); if (pos.second) { - const TopoDS_Shape& sh = fea->Shape.getValue(); + const TopoDS_Shape& sh = Feature::getShape(*it); if (!sh.IsNull()) { builder.Add(comp, sh); TopTools_IndexedMapOfShape faceMap; @@ -108,3 +106,15 @@ App::DocumentObjectExecReturn *Compound::execute(void) } } +//////////////////////////////////////////////////////////////////////// + +PROPERTY_SOURCE(Part::Compound2, Part::Compound) + +Compound2::Compound2() { + Shape.setStatus(App::Property::Transient,true); +} + +void Compound2::onDocumentRestored() { + auto res = execute(); + delete res; +} diff --git a/src/Mod/Part/App/FeatureCompound.h b/src/Mod/Part/App/FeatureCompound.h index 1caaeabc99f4..f55c474c06a8 100644 --- a/src/Mod/Part/App/FeatureCompound.h +++ b/src/Mod/Part/App/FeatureCompound.h @@ -52,6 +52,14 @@ class Compound : public Part::Feature //@} }; +/// Same as Part::Compound, except it marks the Shape as transient, and rebuild it during restore +class Compound2 : public Compound { + PROPERTY_HEADER(Part::Compound2); +public: + Compound2(); + virtual void onDocumentRestored() override; +}; + } //namespace Part diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index 801fdad50301..abb1df596596 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -102,16 +102,13 @@ bool Extrusion::fetchAxisLink(const App::PropertyLinkSub& axisLink, Base::Vector if (!axisLink.getValue()) return false; - if (!axisLink.getValue()->isDerivedFrom(Part::Feature::getClassTypeId())) - throw Base::TypeError("AxisLink has no OCC shape"); - - Part::Feature* linked = static_cast(axisLink.getValue()); + auto linked = axisLink.getValue(); TopoDS_Shape axEdge; if (axisLink.getSubValues().size() > 0 && axisLink.getSubValues()[0].length() > 0){ - axEdge = linked->Shape.getShape().getSubShape(axisLink.getSubValues()[0].c_str()); + axEdge = Feature::getTopoShape(linked).getSubShape(axisLink.getSubValues()[0].c_str()); } else { - axEdge = linked->Shape.getValue(); + axEdge = Feature::getShape(linked); } if (axEdge.IsNull()) @@ -196,25 +193,21 @@ Extrusion::ExtrusionParameters Extrusion::computeFinalParameters() Base::Vector3d Extrusion::calculateShapeNormal(const App::PropertyLink& shapeLink) { - if (!shapeLink.getValue()) + App::DocumentObject* docobj = 0; + Base::Matrix4D mat; + TopoDS_Shape sh = Feature::getShape(shapeLink.getValue(),0,false, &mat,&docobj); + + if (!docobj) throw Base::ValueError("calculateShapeNormal: link is empty"); - const App::DocumentObject* docobj = shapeLink.getValue(); //special case for sketches and the like: no matter what shape they have, use their local Z axis. if (docobj->isDerivedFrom(Part::Part2DObject::getClassTypeId())){ - const Part::Part2DObject* p2do = static_cast(docobj); Base::Vector3d OZ (0.0, 0.0, 1.0); Base::Vector3d result; - p2do->Placement.getValue().getRotation().multVec(OZ, result); + Base::Rotation(mat).multVec(OZ, result); return result; } - //extract the shape - if (! docobj->isDerivedFrom(Part::Feature::getClassTypeId())) - throw Base::TypeError("Linked object doesn't have shape."); - - const TopoShape &tsh = static_cast(docobj)->Shape.getShape(); - TopoDS_Shape sh = tsh.getShape(); if (sh.IsNull()) throw NullShapeException("calculateShapeNormal: link points to a valid object, but its shape is null."); @@ -327,13 +320,10 @@ App::DocumentObjectExecReturn *Extrusion::execute(void) App::DocumentObject* link = Base.getValue(); if (!link) return new App::DocumentObjectExecReturn("No object linked"); - if (!link->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) - return new App::DocumentObjectExecReturn("Linked object is not a Part object"); - Part::Feature *base = static_cast(Base.getValue()); try { Extrusion::ExtrusionParameters params = computeFinalParameters(); - TopoShape result = extrudeShape(base->Shape.getShape(),params); + TopoShape result = extrudeShape(Feature::getShape(link),params); this->Shape.setValue(result); return App::DocumentObject::StdReturn; } diff --git a/src/Mod/Part/App/FeatureFace.cpp b/src/Mod/Part/App/FeatureFace.cpp index e98670f5b7c9..68874db00f6c 100644 --- a/src/Mod/Part/App/FeatureFace.cpp +++ b/src/Mod/Part/App/FeatureFace.cpp @@ -84,9 +84,9 @@ App::DocumentObjectExecReturn *Face::execute(void) std::unique_ptr facemaker = FaceMaker::ConstructFromType(this->FaceMakerClass.getValue()); for (std::vector::iterator it = links.begin(); it != links.end(); ++it) { - if (!(*it && (*it)->isDerivedFrom(Part::Feature::getClassTypeId()))) + if (!(*it)) return new App::DocumentObjectExecReturn("Linked object is not a Part object (has no Shape)."); - TopoDS_Shape shape = static_cast(*it)->Shape.getShape().getShape(); + TopoDS_Shape shape = Feature::getShape(*it); if (shape.IsNull()) return new App::DocumentObjectExecReturn("Linked shape object is empty"); diff --git a/src/Mod/Part/App/FeatureFillet.cpp b/src/Mod/Part/App/FeatureFillet.cpp index 19fa4e6e7245..07ebb8aa84c1 100644 --- a/src/Mod/Part/App/FeatureFillet.cpp +++ b/src/Mod/Part/App/FeatureFillet.cpp @@ -50,17 +50,16 @@ App::DocumentObjectExecReturn *Fillet::execute(void) App::DocumentObject* link = Base.getValue(); if (!link) return new App::DocumentObjectExecReturn("No object linked"); - if (!link->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) - return new App::DocumentObjectExecReturn("Linked object is not a Part object"); - Part::Feature *base = static_cast(Base.getValue()); + + auto baseShape = Feature::getShape(link); try { #if defined(__GNUC__) && defined (FC_OS_LINUX) Base::SignalException se; #endif - BRepFilletAPI_MakeFillet mkFillet(base->Shape.getValue()); + BRepFilletAPI_MakeFillet mkFillet(baseShape); TopTools_IndexedMapOfShape mapOfShape; - TopExp::MapShapes(base->Shape.getValue(), TopAbs_EDGE, mapOfShape); + TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfShape); std::vector values = Edges.getValues(); for (std::vector::iterator it = values.begin(); it != values.end(); ++it) { @@ -74,7 +73,7 @@ App::DocumentObjectExecReturn *Fillet::execute(void) TopoDS_Shape shape = mkFillet.Shape(); if (shape.IsNull()) return new App::DocumentObjectExecReturn("Resulting shape is null"); - ShapeHistory history = buildHistory(mkFillet, TopAbs_FACE, shape, base->Shape.getValue()); + ShapeHistory history = buildHistory(mkFillet, TopAbs_FACE, shape, baseShape); this->Shape.setValue(shape); // make sure the 'PropertyShapeHistory' is not safed in undo/redo (#0001889) diff --git a/src/Mod/Part/App/FeatureMirroring.cpp b/src/Mod/Part/App/FeatureMirroring.cpp index 5d6a735de3dd..7427422b14a6 100644 --- a/src/Mod/Part/App/FeatureMirroring.cpp +++ b/src/Mod/Part/App/FeatureMirroring.cpp @@ -95,14 +95,11 @@ App::DocumentObjectExecReturn *Mirroring::execute(void) App::DocumentObject* link = Source.getValue(); if (!link) return new App::DocumentObjectExecReturn("No object linked"); - if (!link->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) - return new App::DocumentObjectExecReturn("Linked object is not a Part object"); - Part::Feature *source = static_cast(link); Base::Vector3d base = Base.getValue(); Base::Vector3d norm = Normal.getValue(); try { - const TopoDS_Shape& shape = source->Shape.getValue(); + const TopoDS_Shape& shape = Feature::getShape(link); if (shape.IsNull()) Standard_Failure::Raise("Cannot mirroR empty shape"); gp_Ax2 ax2(gp_Pnt(base.x,base.y,base.z), gp_Dir(norm.x,norm.y,norm.z)); diff --git a/src/Mod/Part/App/FeatureOffset.cpp b/src/Mod/Part/App/FeatureOffset.cpp index 044716fd11c0..a24a1bca8820 100644 --- a/src/Mod/Part/App/FeatureOffset.cpp +++ b/src/Mod/Part/App/FeatureOffset.cpp @@ -77,7 +77,7 @@ short Offset::mustExecute() const App::DocumentObjectExecReturn *Offset::execute(void) { App::DocumentObject* source = Source.getValue(); - if (!(source && source->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()))) + if (!source) return new App::DocumentObjectExecReturn("No source shape linked."); double offset = Value.getValue(); double tol = Precision::Confusion(); @@ -86,7 +86,7 @@ App::DocumentObjectExecReturn *Offset::execute(void) short mode = (short)Mode.getValue(); short join = (short)Join.getValue(); bool fill = Fill.getValue(); - const TopoShape& shape = static_cast(source)->Shape.getShape(); + const TopoShape& shape = Feature::getShape(source); if (fabs(offset) > 2*tol) this->Shape.setValue(shape.makeOffsetShape(offset, tol, inter, self, mode, join, fill)); else diff --git a/src/Mod/Part/App/FeaturePartBoolean.cpp b/src/Mod/Part/App/FeaturePartBoolean.cpp index 6253649f5a6b..eb6ec947150f 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.cpp +++ b/src/Mod/Part/App/FeaturePartBoolean.cpp @@ -73,17 +73,17 @@ App::DocumentObjectExecReturn *Boolean::execute(void) #if defined(__GNUC__) && defined (FC_OS_LINUX) Base::SignalException se; #endif - Part::Feature *base = dynamic_cast(Base.getValue()); - Part::Feature *tool = dynamic_cast(Tool.getValue()); + auto base = Base.getValue(); + auto tool = Tool.getValue(); if (!base || !tool) return new App::DocumentObjectExecReturn("Linked object is not a Part object"); // Now, let's get the TopoDS_Shape - TopoDS_Shape BaseShape = base->Shape.getValue(); + TopoDS_Shape BaseShape = Feature::getShape(base); if (BaseShape.IsNull()) throw NullShapeException("Base shape is null"); - TopoDS_Shape ToolShape = tool->Shape.getValue(); + TopoDS_Shape ToolShape = Feature::getShape(tool); if (ToolShape.IsNull()) throw NullShapeException("Tool shape is null"); diff --git a/src/Mod/Part/App/FeaturePartBox.cpp b/src/Mod/Part/App/FeaturePartBox.cpp index 2d728241934f..eda949404888 100644 --- a/src/Mod/Part/App/FeaturePartBox.cpp +++ b/src/Mod/Part/App/FeaturePartBox.cpp @@ -91,6 +91,16 @@ void Box::Restore(Base::XMLReader &reader) { reader.readElement("Properties"); int Cnt = reader.getAttributeAsInteger("Count"); + int transientCount = 0; + if(reader.hasAttribute("TransientCount")) + transientCount = reader.getAttributeAsUnsigned("TransientCount"); + + for (int i=0;isetStatusValue(reader.getAttributeAsUnsigned("status")); + } bool location_xyz = false; bool location_axis = false; @@ -104,7 +114,27 @@ void Box::Restore(Base::XMLReader &reader) reader.readElement("Property"); const char* PropName = reader.getAttribute("name"); const char* TypeName = reader.getAttribute("type"); - App::Property* prop = getPropertyByName(PropName); + auto prop = dynamicProps.restore(*this,PropName,TypeName,reader); + if(!prop) + prop = getPropertyByName(PropName); + + std::bitset<32> status; + if(reader.hasAttribute("status")) { + status = reader.getAttributeAsUnsigned("status"); + if(prop) + prop->setStatusValue(status.to_ulong()); + } + if (prop && strcmp(prop->getTypeId().getName(), TypeName) == 0) { + if (!prop->testStatus(App::Property::Transient) + && !status.test(App::Property::Transient) + && !status.test(App::Property::PropTransient) + && !(getPropertyType(prop) & App::Prop_Transient)) + { + prop->Restore(reader); + } + reader.readEndElement("Property"); + continue; + } if (!prop) { // in case this comes from an old document we must use the new properties if (strcmp(PropName, "l") == 0) { diff --git a/src/Mod/Part/App/FeaturePartCommon.cpp b/src/Mod/Part/App/FeaturePartCommon.cpp index 2c8471732a13..e5f8388a53b1 100644 --- a/src/Mod/Part/App/FeaturePartCommon.cpp +++ b/src/Mod/Part/App/FeaturePartCommon.cpp @@ -89,9 +89,7 @@ App::DocumentObjectExecReturn *MultiCommon::execute(void) std::vector::iterator it; for (it = obj.begin(); it != obj.end(); ++it) { - if ((*it)->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - s.push_back(static_cast(*it)->Shape.getValue()); - } + s.push_back(Feature::getShape(*it)); } bool argumentsAreInCompound = false; diff --git a/src/Mod/Part/App/FeaturePartFuse.cpp b/src/Mod/Part/App/FeaturePartFuse.cpp index 3979cbeb3d0a..bd3296549eb6 100644 --- a/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/src/Mod/Part/App/FeaturePartFuse.cpp @@ -89,9 +89,7 @@ App::DocumentObjectExecReturn *MultiFuse::execute(void) std::vector::iterator it; for (it = obj.begin(); it != obj.end(); ++it) { - if ((*it)->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - s.push_back(static_cast(*it)->Shape.getValue()); - } + s.push_back(Feature::getShape(*it)); } bool argumentsAreInCompound = false; diff --git a/src/Mod/Part/App/FeatureRevolution.cpp b/src/Mod/Part/App/FeatureRevolution.cpp index 444d0f6a5f23..0b53c14a8381 100644 --- a/src/Mod/Part/App/FeatureRevolution.cpp +++ b/src/Mod/Part/App/FeatureRevolution.cpp @@ -90,16 +90,13 @@ bool Revolution::fetchAxisLink(const App::PropertyLinkSub &axisLink, if (!axisLink.getValue()) return false; - if (!axisLink.getValue()->isDerivedFrom(Part::Feature::getClassTypeId())) - throw Base::TypeError("AxisLink has no OCC shape"); - - Part::Feature* linked = static_cast(axisLink.getValue()); + auto linked = axisLink.getValue(); TopoDS_Shape axEdge; if (axisLink.getSubValues().size() > 0 && axisLink.getSubValues()[0].length() > 0){ - axEdge = linked->Shape.getShape().getSubShape(axisLink.getSubValues()[0].c_str()); + axEdge = Feature::getTopoShape(linked).getSubShape(axisLink.getSubValues()[0].c_str()); } else { - axEdge = linked->Shape.getValue(); + axEdge = Feature::getShape(linked); } if (axEdge.IsNull()) @@ -133,9 +130,6 @@ App::DocumentObjectExecReturn *Revolution::execute(void) App::DocumentObject* link = Source.getValue(); if (!link) return new App::DocumentObjectExecReturn("No object linked"); - if (!link->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) - return new App::DocumentObjectExecReturn("Linked object is not a Part object"); - Part::Feature *base = static_cast(Source.getValue()); try { //read out axis link @@ -158,7 +152,7 @@ App::DocumentObjectExecReturn *Revolution::execute(void) angle = angle_edge; //apply "midplane" symmetry - TopoShape sourceShape = base->Shape.getShape(); + TopoShape sourceShape = Feature::getShape(link); if (Symmetric.getValue()) { //rotate source shape backwards by half angle, to make resulting revolution symmetric to the profile gp_Trsf mov; diff --git a/src/Mod/Part/App/GeometryCurvePy.xml b/src/Mod/Part/App/GeometryCurvePy.xml index 0c32d7a758c4..bc2d62454849 100644 --- a/src/Mod/Part/App/GeometryCurvePy.xml +++ b/src/Mod/Part/App/GeometryCurvePy.xml @@ -217,5 +217,11 @@ or raises an exception if it is not periodic. + + + Returns a rotation object to describe the orientation for curve that supports it + + + diff --git a/src/Mod/Part/App/GeometryCurvePyImp.cpp b/src/Mod/Part/App/GeometryCurvePyImp.cpp index f104bad405b3..330c8b7fe808 100644 --- a/src/Mod/Part/App/GeometryCurvePyImp.cpp +++ b/src/Mod/Part/App/GeometryCurvePyImp.cpp @@ -28,6 +28,7 @@ # include # include # include +# include # include # include # include @@ -851,6 +852,17 @@ PyObject* GeometryCurvePy::intersect(PyObject *args) return 0; } +Py::Object GeometryCurvePy::getRotation(void) const +{ + Handle(Geom_Conic) s = Handle(Geom_Conic)::DownCast(getGeometryPtr()->handle()); + if(!s) + return Py::Object(); + gp_Trsf trsf; + trsf.SetTransformation(s->Position(),gp_Ax3()); + auto q = trsf.GetRotation(); + return Py::Rotation(Base::Rotation(q.X(),q.Y(),q.Z(),q.W())); +} + PyObject* GeometryCurvePy::reverse(PyObject *args) { if (!PyArg_ParseTuple(args, "")) diff --git a/src/Mod/Part/App/GeometryPy.xml b/src/Mod/Part/App/GeometryPy.xml index fa6bb72f6173..87d69de4bb16 100644 --- a/src/Mod/Part/App/GeometryPy.xml +++ b/src/Mod/Part/App/GeometryPy.xml @@ -1,13 +1,13 @@ diff --git a/src/Mod/Part/App/GeometrySurfacePy.xml b/src/Mod/Part/App/GeometrySurfacePy.xml index cc2f62da8d2f..6c6937ab750d 100644 --- a/src/Mod/Part/App/GeometrySurfacePy.xml +++ b/src/Mod/Part/App/GeometrySurfacePy.xml @@ -87,6 +87,12 @@ Checks if the surface is planar within a certain tolerance. + + + Returns a rotation object to describe the orientation for surface that supports it + + + Builds the U isoparametric curve diff --git a/src/Mod/Part/App/GeometrySurfacePyImp.cpp b/src/Mod/Part/App/GeometrySurfacePyImp.cpp index 95445f40c5df..60dc42b71e89 100644 --- a/src/Mod/Part/App/GeometrySurfacePyImp.cpp +++ b/src/Mod/Part/App/GeometrySurfacePyImp.cpp @@ -31,6 +31,7 @@ # include # include # include +# include # include # include # include @@ -855,3 +856,16 @@ PyObject* GeometrySurfacePy::intersect(PyObject *args) PyErr_SetString(PyExc_TypeError, "intersect(): Geometry is not a surface"); return 0; } + +Py::Object GeometrySurfacePy::getRotation(void) const +{ + Handle(Geom_ElementarySurface) s = Handle(Geom_ElementarySurface)::DownCast + (getGeometryPtr()->handle()); + if(!s) + return Py::Object(); + gp_Trsf trsf; + trsf.SetTransformation(s->Position().Ax2(),gp_Ax3()); + auto q = trsf.GetRotation(); + return Py::Rotation(Base::Rotation(q.X(),q.Y(),q.Z(),q.W())); +} + diff --git a/src/Mod/Part/App/OCCError.h b/src/Mod/Part/App/OCCError.h index 183b225b8011..59c9cc22c8ff 100644 --- a/src/Mod/Part/App/OCCError.h +++ b/src/Mod/Part/App/OCCError.h @@ -70,9 +70,9 @@ PartExport extern PyObject* PartExceptionOCCDimensionError; #define PY_TRY try -#ifndef DONT_CATCH_CXX_EXCEPTIONS /// see docu of PY_TRY -# define PY_CATCH_OCC catch (Standard_Failure &e) \ +# define _PY_CATCH_OCC(R) \ + catch (Standard_Failure &e) \ { \ std::string str; \ Standard_CString msg = e.GetMessageString(); \ @@ -81,80 +81,11 @@ PartExport extern PyObject* PartExceptionOCCDimensionError; if (msg) {str += msg;} \ else {str += "No OCCT Exception Message";} \ Base::Console().Error(str.c_str()); \ - Py_Error(Part::PartExceptionOCCError,str.c_str()); \ + _Py_Error(R,Part::PartExceptionOCCError,str.c_str()); \ } \ - catch(Base::Exception &e) \ - { \ - std::string str; \ - str += "FreeCAD exception thrown ("; \ - str += e.what(); \ - str += ")"; \ - e.ReportException(); \ - Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \ - } \ - catch(std::exception &e) \ - { \ - std::string str; \ - str += "STL exception thrown ("; \ - str += e.what(); \ - str += ")"; \ - Base::Console().Error(str.c_str()); \ - Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \ - } \ - catch(const Py::Exception&) \ - { \ - return NULL; \ - } \ - catch(const char *e) \ - { \ - Py_Error(Base::BaseExceptionFreeCADError,e); \ - } \ - catch(...) \ - { \ - Py_Error(Base::BaseExceptionFreeCADError,"Unknown C++ exception"); \ - } - -#else -/// see docu of PY_TRY -# define PY_CATCH_OCC catch (Standard_Failure &e) \ - { \ - std::string str; \ - Standard_CString msg = e.GetMessageString(); \ - str += typeid(e).name(); \ - str += " "; \ - if (msg) {str += msg;} \ - else {str += "No OCCT Exception Message";} \ - Base::Console().Error(str.c_str()); \ - Py_Error(Part::PartExceptionOCCError,str.c_str()); \ - } \ - catch(Base::Exception &e) \ - { \ - std::string str; \ - str += "FreeCAD exception thrown ("; \ - str += e.what(); \ - str += ")"; \ - e.ReportException(); \ - Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \ - } \ - catch(std::exception &e) \ - { \ - std::string str; \ - str += "STL exception thrown ("; \ - str += e.what(); \ - str += ")"; \ - Base::Console().Error(str.c_str()); \ - Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \ - } \ - catch(const Py::Exception&) \ - { \ - return NULL; \ - } \ - catch(const char *e) \ - { \ - Py_Error(Base::BaseExceptionFreeCADError,e); \ - } - -#endif // DONT_CATCH_CXX_EXCEPTIONS + _PY_CATCH(R) } //namespace Part + +#define PY_CATCH_OCC _PY_CATCH_OCC(return(NULL)) #endif // _OCCError_h_ diff --git a/src/Mod/Part/App/OpenCascadeAll.h b/src/Mod/Part/App/OpenCascadeAll.h index ed007e285116..ef7de5a2402b 100644 --- a/src/Mod/Part/App/OpenCascadeAll.h +++ b/src/Mod/Part/App/OpenCascadeAll.h @@ -234,6 +234,7 @@ #include #endif #include +#include #include #include #include @@ -252,6 +253,7 @@ #include #include #include +#include #include #include #include @@ -419,6 +421,7 @@ #include #include #include +#include // Adaptors #include diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index 3650dea01537..f687eeed63f1 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -52,6 +52,8 @@ # include #endif +#include +#include #include #include #include @@ -63,6 +65,8 @@ #include #include #include +#include +#include #include "PartPyCXX.h" #include "PartFeature.h" @@ -126,8 +130,10 @@ App::DocumentObject *Feature::getSubObject(const char *subname, if(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.')) return App::DocumentObject::getSubObject(subname,pyObj,pmat,transform,depth); - if(pmat && transform) - *pmat *= Placement.getValue().toMatrix(); + Base::Matrix4D _mat; + auto &mat = pmat?*pmat:_mat; + if(transform) + mat *= Placement.getValue().toMatrix(); if(!pyObj) { #if 0 @@ -142,13 +148,11 @@ App::DocumentObject *Feature::getSubObject(const char *subname, try { TopoShape ts(Shape.getShape()); - bool doTransform = pmat && *pmat!=ts.getTransform(); - if(doTransform) { - ts.setShape(ts.getShape().Located(TopLoc_Location()),false); - ts.initCache(1); - } + bool doTransform = mat!=ts.getTransform(); + if(doTransform) + ts.setShape(ts.getShape().Located(TopLoc_Location())); if(subname && *subname && !ts.isNull()) - ts = ts.getSubTopoShape(subname,true); + ts = ts.getSubShape(subname); if(doTransform && !ts.isNull()) { static int sCopy = -1; if(sCopy<0) { @@ -171,7 +175,7 @@ App::DocumentObject *Feature::getSubObject(const char *subname, } } } - ts.transformShape(*pmat,copy,true); + ts.transformShape(mat,copy,true); } *pyObj = Py::new_reference_to(shape2pyshape(ts)); return const_cast(this); @@ -189,6 +193,330 @@ 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) +{ + return getTopoShape(obj,subname,needSubElement,pmat,powner,resolveLink,transform,true).getShape(); +} + +struct ShapeCache { + + std::unordered_map ,TopoShape> > cache; + + bool inited = false; + void init() { + if(inited) + return; + inited = true; + App::GetApplication().signalDeleteDocument.connect( + boost::bind(&ShapeCache::slotDeleteDocument, this, _1)); + App::GetApplication().signalDeletedObject.connect( + boost::bind(&ShapeCache::slotClear, this, _1)); + App::GetApplication().signalChangedObject.connect( + boost::bind(&ShapeCache::slotChanged, this, _1,_2)); + } + + void slotDeleteDocument(const App::Document &doc) { + cache.erase(&doc); + } + + void slotChanged(const App::DocumentObject &obj, const App::Property &prop) { + const char *propName = prop.getName(); + if(!propName) + return; + if(strcmp(propName,"Shape")==0 + || strcmp(propName,"Group")==0 + || strstr(propName,"Touched")!=0) + slotClear(obj); + } + + void slotClear(const App::DocumentObject &obj) { + auto it = cache.find(obj.getDocument()); + if(it==cache.end()) + return; + auto &map = it->second; + for(auto it2=map.lower_bound(std::make_pair(&obj,std::string())); + it2!=map.end() && it2->first.first==&obj;) + { + it2 = map.erase(it2); + } + } + + bool getShape(const App::DocumentObject *obj, TopoShape &shape, const char *subname=0) { + init(); + auto &entry = cache[obj->getDocument()]; + if(!subname) subname = ""; + auto it = entry.find(std::make_pair(obj,std::string(subname))); + if(it!=entry.end()) { + shape = it->second; + return !shape.isNull(); + } + return false; + } + + void setShape(const App::DocumentObject *obj, const TopoShape &shape, const char *subname=0) { + init(); + if(!subname) subname = ""; + cache[obj->getDocument()][std::make_pair(obj,std::string(subname))] = shape; + } +}; +static ShapeCache _ShapeCache; + +void Feature::clearShapeCache() { + _ShapeCache.cache.clear(); +} + +static TopoShape _getTopoShape(const App::DocumentObject *obj, const char *subname, + bool needSubElement, Base::Matrix4D *pmat, App::DocumentObject **powner, + bool resolveLink, bool noElementMap, std::vector &linkStack) + +{ + TopoShape shape; + + if(!obj) return shape; + + PyObject *pyobj = 0; + Base::Matrix4D mat; + if(powner) *powner = 0; + + std::string _subname; + auto subelement = Data::ComplexGeoData::findElementName(subname); + if(!needSubElement && subname) { + // strip out element name if not needed + if(subelement && *subelement) { + _subname = std::string(subname,subelement); + subname = _subname.c_str(); + } + } + + if(_ShapeCache.getShape(obj,shape,subname)) { + if(noElementMap) { + // shape.resetElementMap(); + // shape.Tag = 0; + // shape.Hasher.reset(); + } + } + + App::DocumentObject *linked = 0; + App::DocumentObject *owner = 0; + Base::Matrix4D linkMat; + // App::StringHasherRef hasher; + // long tag; + { + Base::PyGILStateLocker lock; + owner = obj->getSubObject(subname,shape.isNull()?&pyobj:0,&mat,false); + if(!owner) + return shape; + // tag = owner->getID(); + // hasher = owner->getDocument()->getStringHasher(); + linked = owner->getLinkedObject(true,&linkMat,false); + if(pmat) { + if(resolveLink && obj!=owner) + *pmat = mat * linkMat; + else + *pmat = mat; + } + if(!linked) + linked = owner; + if(powner) + *powner = resolveLink?linked:owner; + + if(!shape.isNull()) + return shape; + + if(pyobj && PyObject_TypeCheck(pyobj,&TopoShapePy::Type)) { + shape = *static_cast(pyobj)->getTopoShapePtr(); + if(!shape.isNull()) { + if(obj->getDocument() != linked->getDocument()) + _ShapeCache.setShape(obj,shape,subname); + if(noElementMap) { + // shape.resetElementMap(); + // shape.Tag = 0; + // shape.Hasher.reset(); + } + Py_DECREF(pyobj); + return shape; + } + } + + Py_XDECREF(pyobj); + } + + // nothing can be done if there is sub-element references + if(needSubElement && subelement && *subelement) + return shape; + + bool scaled = false; + if(obj!=owner) { + if(_ShapeCache.getShape(owner,shape)) { + auto scaled = shape.transformShape(mat,false,true); + if(owner->getDocument()!=obj->getDocument()) { + // shape.reTagElementMap(obj->getID(),obj->getDocument()->getStringHasher()); + _ShapeCache.setShape(obj,shape,subname); + } else if(scaled) + _ShapeCache.setShape(obj,shape,subname); + } + if(!shape.isNull()) { + if(noElementMap) { + // shape.resetElementMap(); + // shape.Tag = 0; + // shape.Hasher.reset(); + } + return shape; + } + } + + auto link = owner->getExtensionByType(true); + if(owner!=linked && (!link || !link->_ChildCache.getSize())) { + // if there is a linked object, and there is no child cache (which is used + // for special handling of plain group), obtain shape from the linked object + shape = Feature::getTopoShape(linked,0,false,0,0,false,false); + if(shape.isNull()) + return shape; + if(owner==obj) + shape.transformShape(mat*linkMat,false,true); + else + shape.transformShape(linkMat,false,true); + // shape.reTagElementMap(tag,hasher); + + } else { + + if(link || owner->getExtensionByType(true)) + linkStack.push_back(owner); + + // Construct a compound of sub objects + std::vector shapes; + + // Acceleration for link array. Unlike non-array link, a link array does + // not return the linked object when calling getLinkedObject(). + // Therefore, it should be handled here. + TopoShape baseShape; + std::string op; + if(link && link->getElementCountValue()) { + linked = link->getTrueLinkedObject(false); + if(linked && linked!=owner) { + baseShape = Feature::getTopoShape(linked,0,false,0,0,false,false); + // if(!link->getShowElementValue()) + // baseShape.reTagElementMap(owner->getID(),owner->getDocument()->getStringHasher()); + } + } + for(auto &sub : owner->getSubObjects()) { + if(sub.empty()) continue; + int visible; + if(sub[sub.size()-1] != '.') + sub += '.'; + std::string childName; + App::DocumentObject *parent=0; + Base::Matrix4D mat; + auto subObj = owner->resolve(sub.c_str(), &parent, &childName,0,0,&mat,false); + if(!parent || !subObj) + continue; + if(linkStack.size() + && parent->getExtensionByType(true,false)) + { + visible = linkStack.back()->isElementVisible(childName.c_str()); + }else + visible = parent->isElementVisible(childName.c_str()); + if(visible==0) + continue; + TopoShape shape; + if(baseShape.isNull()) { + shape = _getTopoShape(owner,sub.c_str(),false,0,&subObj,false,false,linkStack); + if(shape.isNull()) + continue; + if(visible<0 && subObj && !subObj->Visibility.getValue()) + continue; + }else{ + if(link && !link->getShowElementValue()) + shape = baseShape.makETransform(mat,(TopoShape::indexPostfix()+childName).c_str()); + else { + shape = baseShape.makETransform(mat); + // shape.reTagElementMap(subObj->getID(),subObj->getDocument()->getStringHasher()); + } + } + shapes.push_back(shape); + } + + if(linkStack.size() && linkStack.back()==owner) + linkStack.pop_back(); + + if(shapes.empty()) + return shape; + + // shape.Tag = tag; + // shape.Hasher = hasher; + shape.makECompound(shapes); + } + + _ShapeCache.setShape(owner,shape); + + if(owner!=obj) { + scaled = shape.transformShape(mat,false,true); + if(owner->getDocument()!=obj->getDocument()) { + // shape.reTagElementMap(obj->getID(),obj->getDocument()->getStringHasher()); + _ShapeCache.setShape(obj,shape,subname); + }else if(scaled) + _ShapeCache.setShape(obj,shape,subname); + } + if(noElementMap) { + // shape.resetElementMap(); + // shape.Tag = 0; + // shape.Hasher.reset(); + } + return shape; +} + +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 || !obj->getNameInDocument()) + return TopoShape(); + + std::vector linkStack; + + // NOTE! _getTopoShape() always return shape without top level + // transformation for easy shape caching, i.e. with `transform` set + // to false. So we manually apply the top level transform if asked. + + Base::Matrix4D mat; + auto shape = _getTopoShape(obj, subname, needSubElement, &mat, + powner, resolveLink, noElementMap, linkStack); + + Base::Matrix4D topMat; + if(pmat || transform) { + // Obtain top level transformation + if(pmat) + topMat = *pmat; + if(transform) + obj->getSubObject(0,0,&topMat); + + // Apply the top level transformation + if(!shape.isNull()) + shape.transformShape(topMat,false,true); + + if(pmat) + *pmat = topMat * mat; + } + + return shape; + +} + +App::DocumentObject *Feature::getShapeOwner(const App::DocumentObject *obj, const char *subname) +{ + if(!obj) return 0; + auto owner = obj->getSubObject(subname); + if(owner) { + auto linked = owner->getLinkedObject(true); + if(linked) + owner = linked; + } + return owner; +} + 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 499e9b3c7b4c..599bf7f64dfd 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -71,6 +71,44 @@ class PartExport Feature : public App::GeoFeature virtual DocumentObject *getSubObject(const char *subname, PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const override; + /** Convenience function to extract shape from fully qualified subname + * + * @param obj: the parent object + * + * @param subname: dot separated full qualified subname + * + * @param needSubElement: whether to ignore the non-object subelement + * reference inside \c subname + * + * @param pmat: used as current transformation on input, and return the + * accumulated transformation on output + * + * @param owner: return the owner of the shape returned + * + * @param resolveLink: if true, resolve link(s) of the returned 'owner' + * by calling its getLinkedObject(true) function + * + * @param transform: if true, apply obj's transformation. Set to false + * if pmat already include obj's transformation matrix. + */ + static TopoDS_Shape getShape(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); + + 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 void clearShapeCache(); + + static App::DocumentObject *getShapeOwner(const App::DocumentObject *obj, const char *subname=0); + + static bool hasShapeOwner(const App::DocumentObject *obj, const char *subname=0) { + auto owner = getShapeOwner(obj,subname); + return owner && owner->isDerivedFrom(getClassTypeId()); + } + protected: /// recompute only this object virtual App::DocumentObjectExecReturn *recompute(void); diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index 33d4b40b832a..0d20733c486b 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -84,20 +84,20 @@ App::DocumentObjectExecReturn* RuledSurface::getShape(const App::PropertyLinkSub TopoDS_Shape& shape) const { App::DocumentObject* obj = link.getValue(); - if (!(obj && obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()))) + if(!obj) return new App::DocumentObjectExecReturn("No shape linked."); // if no explicit sub-shape is selected use the whole part const std::vector& element = link.getSubValues(); if (element.empty()) { - shape = static_cast(obj)->Shape.getValue(); + shape = Feature::getShape(obj); return nullptr; } else if (element.size() != 1) { return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked."); } - const Part::TopoShape& part = static_cast(obj)->Shape.getValue(); + const Part::TopoShape& part = Feature::getTopoShape(obj); if (!part.getShape().IsNull()) { if (!element[0].empty()) { shape = part.getSubShape(element[0].c_str()); @@ -311,9 +311,7 @@ App::DocumentObjectExecReturn *Loft::execute(void) const std::vector& shapes = Sections.getValues(); std::vector::const_iterator it; for (it = shapes.begin(); it != shapes.end(); ++it) { - if (!(*it)->isDerivedFrom(Part::Feature::getClassTypeId())) - return new App::DocumentObjectExecReturn("Linked object is not a shape."); - TopoDS_Shape shape = static_cast(*it)->Shape.getValue(); + TopoDS_Shape shape = Feature::getShape(*it); if (shape.IsNull()) return new App::DocumentObjectExecReturn("Linked shape is invalid."); @@ -424,12 +422,12 @@ App::DocumentObjectExecReturn *Sweep::execute(void) if (Sections.getSize() == 0) return new App::DocumentObjectExecReturn("No sections linked."); App::DocumentObject* spine = Spine.getValue(); - if (!(spine && spine->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()))) + if (!spine) return new App::DocumentObjectExecReturn("No spine linked."); const std::vector& subedge = Spine.getSubValues(); TopoDS_Shape path; - const Part::TopoShape& shape = static_cast(spine)->Shape.getValue(); + const Part::TopoShape& shape = Feature::getTopoShape(spine); if (!shape.getShape().IsNull()) { try { if (!subedge.empty()) { @@ -483,9 +481,7 @@ App::DocumentObjectExecReturn *Sweep::execute(void) const std::vector& shapes = Sections.getValues(); std::vector::const_iterator it; for (it = shapes.begin(); it != shapes.end(); ++it) { - if (!(*it)->isDerivedFrom(Part::Feature::getClassTypeId())) - return new App::DocumentObjectExecReturn("Linked object is not a shape."); - TopoDS_Shape shape = static_cast(*it)->Shape.getValue(); + TopoDS_Shape shape = Feature::getShape(*it); if (shape.IsNull()) return new App::DocumentObjectExecReturn("Linked shape is invalid."); @@ -638,9 +634,9 @@ void Thickness::handleChangedPropertyType(Base::XMLReader &reader, const char *T App::DocumentObjectExecReturn *Thickness::execute(void) { App::DocumentObject* source = Faces.getValue(); - if (!(source && source->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()))) + if (!source) return new App::DocumentObjectExecReturn("No source shape linked."); - const TopoShape& shape = static_cast(source)->Shape.getShape(); + const TopoShape& shape = Feature::getTopoShape(source); if (shape.isNull()) return new App::DocumentObjectExecReturn("Source shape is empty."); diff --git a/src/Mod/Part/App/PartPyCXX.cpp b/src/Mod/Part/App/PartPyCXX.cpp index 15b3c89fd5c9..44eda6bf96a2 100644 --- a/src/Mod/Part/App/PartPyCXX.cpp +++ b/src/Mod/Part/App/PartPyCXX.cpp @@ -33,11 +33,11 @@ #include namespace Part { -PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape) +PartExport Py::Object shape2pyshape(const TopoShape &shape) { PyObject* ret = 0; - if (!shape.IsNull()) { - TopAbs_ShapeEnum type = shape.ShapeType(); + if (!shape.isNull()) { + TopAbs_ShapeEnum type = shape.getShape().ShapeType(); switch (type) { case TopAbs_COMPOUND: @@ -79,6 +79,11 @@ PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape) return Py::asObject(ret); } + +PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape) { + return shape2pyshape(TopoShape(shape)); +} + } //namespace Part diff --git a/src/Mod/Part/App/PartPyCXX.h b/src/Mod/Part/App/PartPyCXX.h index e6ccaa216142..f306e44a64e7 100644 --- a/src/Mod/Part/App/PartPyCXX.h +++ b/src/Mod/Part/App/PartPyCXX.h @@ -32,4 +32,11 @@ namespace Py { bool TopoShape::accepts (PyObject *pyob) const; } +namespace Part { + PartExport Py::Object shape2pyshape(const TopoShape &shape); + PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape); + PartExport void getPyShapes(PyObject *obj, std::vector &shapes); + PartExport std::vector getPyShapes(PyObject *obj); +} + #endif //PART_PYCXX_H diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 91ef2d01c393..e042295be349 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -181,6 +181,8 @@ #endif #endif // _PreComp_ +#include + #include #include #include @@ -188,6 +190,7 @@ #include #include +#include "PartPyCXX.h" #include "TopoShape.h" #include "CrossSection.h" #include "TopoShapeFacePy.h" @@ -200,6 +203,8 @@ #include "FaceMakerBullseye.h" #include "BRepOffsetAPI_MakeOffsetFix.h" +FC_LOG_LEVEL_INIT("TopoShape",true,true); + using namespace Part; const char* BRepBuilderAPI_FaceErrorText(BRepBuilderAPI_FaceError et) @@ -298,6 +303,7 @@ TopoShape::TopoShape(const TopoDS_Shape& shape) TopoShape::TopoShape(const TopoShape& shape) : _Shape(shape._Shape) { + Tag = shape.Tag; } std::vector TopoShape::getElementTypes(void) const @@ -323,88 +329,188 @@ Data::Segment* TopoShape::getSubElement(const char* Type, unsigned long n) const return new ShapeSegment(getSubShape(temp.c_str())); } -TopoDS_Shape TopoShape::getSubShape(const char* Type) const +TopoDS_Shape TopoShape::getSubShape(const char* Type, bool silent) const { - if (!Type) - Standard_Failure::Raise("No sub-shape type given"); - if (this->_Shape.IsNull()) + auto res = shapeTypeAndIndex(Type); + return getSubShape(res.first,res.second,silent); +} + +TopoDS_Shape TopoShape::getSubShape(TopAbs_ShapeEnum type, int index, bool silent) const +{ + if(index <= 0) { + if(silent) + return TopoDS_Shape(); + Standard_Failure::Raise("Unsupported sub-shape type"); + } + + if (this->_Shape.IsNull()) { + if(silent) + return TopoDS_Shape(); Standard_Failure::Raise("Cannot get sub-shape from empty shape"); + } - std::string shapetype(Type); - if (shapetype.size() > 4 && shapetype.substr(0,4) == "Face") { - int index=std::atoi(&shapetype[4]); - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(this->_Shape, TopAbs_FACE, anIndices); - // To avoid a segmentation fault we have to check if container is empty - if (anIndices.IsEmpty()) - Standard_Failure::Raise("Shape has no faces"); - return anIndices.FindKey(index); - } - else if (shapetype.size() > 4 && shapetype.substr(0,4) == "Edge") { - int index=std::atoi(&shapetype[4]); - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(this->_Shape, TopAbs_EDGE, anIndices); - // To avoid a segmentation fault we have to check if container is empty - if (anIndices.IsEmpty()) - Standard_Failure::Raise("Shape has no edges"); - return anIndices.FindKey(index); - } - else if (shapetype.size() > 6 && shapetype.substr(0,6) == "Vertex") { - int index=std::atoi(&shapetype[6]); - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(this->_Shape, TopAbs_VERTEX, anIndices); - // To avoid a segmentation fault we have to check if container is empty - if (anIndices.IsEmpty()) - Standard_Failure::Raise("Shape has no vertexes"); - return anIndices.FindKey(index); - } - - Standard_Failure::Raise("Unsupported sub-shape type"); - return TopoDS_Shape(); // avoid compiler warning + try { + if(type == TopAbs_SHAPE) { + int i=1; + for(TopoDS_Iterator it(_Shape);it.More();it.Next(),++i) { + if(i == index) + return it.Value(); + } + } else { + TopTools_IndexedMapOfShape anIndices; + TopExp::MapShapes(this->_Shape, type, anIndices); + if(index <= anIndices.Extent()) + return anIndices.FindKey(index); + } + } catch(Standard_Failure &) { + if(silent) + return TopoDS_Shape(); + throw; + } + if(!silent) + Standard_Failure::Raise("Index out of bound"); + return TopoDS_Shape(); } unsigned long TopoShape::countSubShapes(const char* Type) const { - std::string shapetype(Type); - if (shapetype == "Face") { - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(this->_Shape, TopAbs_FACE, anIndices); - return anIndices.Extent(); + if(!Type) return 0; + if(strcmp(Type,"SubShape")==0) + return countSubShapes(TopAbs_SHAPE); + auto type = shapeType(Type,true); + if(type == TopAbs_SHAPE) + return 0; + return countSubShapes(type); +} + +unsigned long TopoShape::countSubShapes(TopAbs_ShapeEnum Type) const +{ + if(Type == TopAbs_SHAPE) { + int count = 0; + for(TopoDS_Iterator it(_Shape);it.More();it.Next()) + ++count; + return count; } - else if (shapetype == "Edge") { - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(this->_Shape, TopAbs_EDGE, anIndices); - return anIndices.Extent(); + TopTools_IndexedMapOfShape anIndices; + TopExp::MapShapes(this->_Shape, Type, anIndices); + return anIndices.Extent(); +} + +bool TopoShape::hasSubShape(TopAbs_ShapeEnum type) const { + if(type == TopAbs_SHAPE) { + TopoDS_Iterator it(_Shape); + return !!it.More(); } - else if (shapetype == "Vertex") { - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(this->_Shape, TopAbs_VERTEX, anIndices); - return anIndices.Extent(); + TopExp_Explorer exp(_Shape,type); + return !!exp.More(); +} + +bool TopoShape::hasSubShape(const char *Type) const { + auto idx = shapeTypeAndIndex(Type); + return idx.second>0 && idx.second<=(int)countSubShapes(idx.first); +} + +static std::array _ShapeNames; + +static void initShapeNameMap() { + if(_ShapeNames[TopAbs_VERTEX].empty()) { + _ShapeNames[TopAbs_VERTEX] = "Vertex"; + _ShapeNames[TopAbs_EDGE] = "Edge"; + _ShapeNames[TopAbs_FACE] = "Face"; + _ShapeNames[TopAbs_WIRE] = "Wire"; + _ShapeNames[TopAbs_SHELL] = "Shell"; + _ShapeNames[TopAbs_SOLID] = "Solid"; + _ShapeNames[TopAbs_COMPOUND] = "Compound"; + _ShapeNames[TopAbs_COMPSOLID] = "CompSolid"; } +} - return 0; +std::pair TopoShape::shapeTypeAndIndex(const char *name) { + int idx = 0; + TopAbs_ShapeEnum type = TopAbs_SHAPE; + static const std::string _subshape("SubShape"); + if(boost::starts_with(name,_subshape)) { + std::istringstream iss(name+_subshape.size()); + iss >> idx; + if(!iss.eof()) + idx = 0; + } else { + type = shapeType(name,true); + if(type != TopAbs_SHAPE) { + std::istringstream iss(name+shapeName(type).size()); + iss >> idx; + if(!iss.eof()) { + idx = 0; + type = TopAbs_SHAPE; + } + } + } + return std::make_pair(type,idx); } -PyObject * TopoShape::getPySubShape(const char* Type) const -{ - // get the shape - TopoDS_Shape Shape = getSubShape(Type); - // destinquish the return type - std::string shapetype(Type); - if (shapetype.size() > 4 && shapetype.substr(0,4) == "Face") - return new TopoShapeFacePy(new TopoShape(Shape)); - else if (shapetype.size() > 4 && shapetype.substr(0,4) == "Edge") - return new TopoShapeEdgePy(new TopoShape(Shape)); - else if (shapetype.size() > 6 && shapetype.substr(0,6) == "Vertex") - return new TopoShapeVertexPy(new TopoShape(Shape)); - else - return 0; +TopAbs_ShapeEnum TopoShape::shapeType(const char *type, bool silent) { + if(type) { + initShapeNameMap(); + for(size_t idx=0;idx<_ShapeNames.size();++idx) { + if(_ShapeNames[idx].size() && boost::starts_with(type,_ShapeNames[idx])) + return (TopAbs_ShapeEnum)idx; + } + } + if(!silent) { + if(Data::ComplexGeoData::hasMissingElement(type)) + FC_THROWM(Base::CADKernelError,"missing shape element: " << (type?type:"?")); + FC_THROWM(Base::CADKernelError,"invalid shape type: " << (type?type:"?")); + } + return TopAbs_SHAPE; +} + +TopAbs_ShapeEnum TopoShape::shapeType(char type, bool silent) { + switch(type) { + case 'E': + return TopAbs_EDGE; + case 'V': + return TopAbs_VERTEX; + case 'F': + return TopAbs_FACE; + default: + if(!silent) + FC_THROWM(Base::CADKernelError, "invalid shape type '" << type << "'"); + return TopAbs_SHAPE; + } +} + +TopAbs_ShapeEnum TopoShape::shapeType(bool silent) const { + if(isNull()) { + if(!silent) + FC_THROWM(NullShapeException, "Input shape is null"); + return TopAbs_SHAPE; + } + return getShape().ShapeType(); +} + +const std::string &TopoShape::shapeName(TopAbs_ShapeEnum type, bool silent) { + initShapeNameMap(); + if(type>=0 && type<_ShapeNames.size() && _ShapeNames[type].size()) + return _ShapeNames[type]; + if(!silent) + FC_THROWM(Base::CADKernelError, "invalid shape type '" << type << "'"); + static std::string ret(""); + return ret; +} + +const std::string &TopoShape::shapeName(bool silent) const { + return shapeName(shapeType(silent),silent); +} +PyObject * TopoShape::getPySubShape(const char* Type, bool silent) const +{ + return Py::new_reference_to(shape2pyshape(getSubShape(Type,silent))); } void TopoShape::operator = (const TopoShape& sh) { if (this != &sh) { + this->Tag = sh.Tag; this->_Shape = sh._Shape; } } @@ -470,6 +576,18 @@ void TopoShape::convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx) #endif } +Base::Matrix4D TopoShape::convert(const gp_Trsf& trsf) { + Base::Matrix4D mat; + convertToMatrix(trsf,mat); + return mat; +} + +gp_Trsf TopoShape::convert(const Base::Matrix4D& mtrx) { + gp_Trsf trsf; + convertTogpTrsf(mtrx,trsf); + return trsf; +} + void TopoShape::setTransform(const Base::Matrix4D& rclTrf) { gp_Trsf mov; @@ -2841,7 +2959,10 @@ TopoDS_Shape TopoShape::makeThickSolid(const TopTools_ListOfShape& remFace, void TopoShape::transformGeometry(const Base::Matrix4D &rclMat) { - this->_Shape = transformGShape(rclMat); + if (this->_Shape.IsNull()) + Standard_Failure::Raise("Cannot transform null shape"); + + *this = makEGTransform(rclMat); } TopoDS_Shape TopoShape::transformGShape(const Base::Matrix4D& rclTrf) const @@ -2868,23 +2989,12 @@ TopoDS_Shape TopoShape::transformGShape(const Base::Matrix4D& rclTrf) const return mkTrf.Shape(); } -void TopoShape::transformShape(const Base::Matrix4D& rclTrf, bool copy) +bool TopoShape::transformShape(const Base::Matrix4D& rclTrf, bool copy, bool checkScale) { if (this->_Shape.IsNull()) Standard_Failure::Raise("Cannot transform null shape"); - gp_Trsf mat; - mat.SetValues(rclTrf[0][0],rclTrf[0][1],rclTrf[0][2],rclTrf[0][3], - rclTrf[1][0],rclTrf[1][1],rclTrf[1][2],rclTrf[1][3], - rclTrf[2][0],rclTrf[2][1],rclTrf[2][2],rclTrf[2][3] -#if OCC_VERSION_HEX < 0x060800 - , 0.00001,0.00001 -#endif - ); //precision was removed in OCCT CR0025194 - - // location transformation - BRepBuilderAPI_Transform mkTrf(this->_Shape, mat, copy ? Standard_True : Standard_False); - this->_Shape = mkTrf.Shape(); + return _makETransform(TopoShape(*this),rclTrf,0,checkScale,copy); } TopoDS_Shape TopoShape::mirror(const gp_Ax2& ax2) const @@ -3428,6 +3538,10 @@ void TopoShape::getLinesFromSubelement(const Data::Segment* element, return; } + // build up map edge->face + TopTools_IndexedDataMapOfShapeListOfShape edge2Face; + TopExp::MapShapesAndAncestors(this->_Shape, TopAbs_EDGE, TopAbs_FACE, edge2Face); + for(TopExp_Explorer exp(shape,TopAbs_EDGE);exp.More();exp.Next()) { TopoDS_Edge aEdge = TopoDS::Edge(exp.Current()); @@ -3460,12 +3574,16 @@ void TopoShape::getLinesFromSubelement(const Data::Segment* element, // must provide this triangulation // Look for one face in our map (it doesn't care which one we take) - auto aFace = findAncestorShape(aEdge, TopAbs_FACE); - if(aFace.IsNull()) + int index = edge2Face.FindIndex(aEdge); + if(!index) + continue; + const auto &faces = edge2Face.FindFromIndex(index); + if(!faces.Extent()) continue; + const TopoDS_Face& aFace = TopoDS::Face(faces.First()); // take the face's triangulation instead - Handle(Poly_Triangulation) aPolyTria = BRep_Tool::Triangulation(TopoDS::Face(aFace),aLoc); + Handle(Poly_Triangulation) aPolyTria = BRep_Tool::Triangulation(aFace,aLoc); if (!aLoc.IsIdentity()) { myTransf = aLoc.Transformation(); } @@ -3691,3 +3809,286 @@ TopoDS_Shape TopoShape::makeShell(const TopoDS_Shape& input) const return input; } } + +#define _HANDLE_NULL_SHAPE(_msg,_throw) do {\ + if(_throw) {\ + FC_THROWM(NullShapeException,_msg);\ + }\ + FC_WARN(_msg);\ +}while(0) + +#define HANDLE_NULL_SHAPE _HANDLE_NULL_SHAPE("Null shape",true) +#define HANDLE_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",true) +#define WARN_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",false) + +TopoShape &TopoShape::makEWires(const TopoShape &shape, const char *op, bool fix, double tol) +{ + _Shape.Nullify(); + + if(shape.isNull()) + HANDLE_NULL_INPUT; + + if(tol edges; + std::list edge_list; + std::vector wires; + + TopTools_IndexedMapOfShape anIndices; + TopExp::MapShapes(shape.getShape(), TopAbs_EDGE, anIndices); + for(int i=1;i<=anIndices.Extent();++i) + edge_list.push_back(anIndices.FindKey(i)); + + edges.reserve(edge_list.size()); + wires.reserve(edge_list.size()); + + // sort them together to wires + while (edge_list.size() > 0) { + BRepBuilderAPI_MakeWire mkWire; + // add and erase first edge + edges.push_back(edge_list.front()); + edge_list.pop_front(); + mkWire.Add(TopoDS::Edge(edges.back().getShape())); + edges.back().setShape(mkWire.Edge()); + + TopoDS_Wire new_wire = mkWire.Wire(); // current new wire + + // try to connect each edge to the wire, the wire is complete if no more edges are connectible + bool found = false; + do { + found = false; + for (auto it=edge_list.begin();it!=edge_list.end();++it) { + mkWire.Add(TopoDS::Edge(it->getShape())); + if (mkWire.Error() != BRepBuilderAPI_DisconnectedWire) { + // edge added ==> remove it from list + found = true; + edges.push_back(*it); + edges.back().setShape(mkWire.Edge()); + edge_list.erase(it); + new_wire = mkWire.Wire(); + break; + } + } + } while (found); + + // Fix any topological issues of the wire + ShapeFix_Wire aFix; + aFix.SetPrecision(tol); + aFix.Load(new_wire); + + aFix.FixReorder(); + // Assuming FixReorder() just reorder and don't change the underlying + // edges, we get the wire and do a name mapping now, as the following + // two operations (FixConnected and FixClosed) may change the edges. + wires.push_back(aFix.Wire()); + + aFix.FixConnected(); + aFix.FixClosed(); + // Now retrieve the shape and set it without touching element map + wires.back().setShape(aFix.Wire()); + } + return makECompound(wires,0,false); +} + +TopoShape &TopoShape::makECompound(const std::vector &shapes, const char *op, bool force) +{ + (void)op; + _Shape.Nullify(); + + if(shapes.empty()) + HANDLE_NULL_INPUT; + + if(!force && shapes.size()==1) { + *this = shapes[0]; + return *this; + } + + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + int count = 0; + for(auto &s : shapes) { + if(s.isNull()) { + WARN_NULL_INPUT; + continue; + } + builder.Add(comp,s.getShape()); + ++count; + } + if(!count) + HANDLE_NULL_SHAPE; + _Shape = comp; + return *this; +} + +TopoShape &TopoShape::makEFace(const TopoShape &shape, const char *op, const char *maker) +{ + std::vector shapes; + if(shape.getShape().ShapeType() == TopAbs_COMPOUND) { + for(TopoDS_Iterator it(_Shape);it.More();it.Next()) + shapes.push_back(it.Value()); + } else + shapes.push_back(shape); + return makEFace(shapes,op,maker); +} + +TopoShape &TopoShape::makEFace(const std::vector &shapes, const char *op, const char *maker) +{ + (void)op; + _Shape.Nullify(); + + if(!maker || !maker[0]) maker = "Part::FaceMakerBullseye"; + std::unique_ptr mkFace = FaceMaker::ConstructFromType(maker); + for(auto &s : shapes) { + if (s.getShape().ShapeType() == TopAbs_COMPOUND) + mkFace->useCompound(TopoDS::Compound(s.getShape())); + else + mkFace->addShape(s.getShape()); + } + mkFace->Build(); + _Shape = mkFace->Shape(); + return *this; +} + +TopoShape &TopoShape::makERefine(const TopoShape &shape, const char *op, bool no_fail) { + (void)op; + _Shape.Nullify(); + if(shape.isNull()) { + if(!no_fail) + HANDLE_NULL_SHAPE; + return *this; + } + try { + BRepBuilderAPI_RefineModel mkRefine(shape.getShape()); + _Shape = mkRefine.Shape(); + return *this; + }catch (Standard_Failure &) { + if(!no_fail) throw; + } + *this = shape; + return *this; +} + +bool TopoShape::findPlane(gp_Pln &pln, double tol) const { + if(_Shape.IsNull()) + return false; + TopoDS_Shape shape = _Shape; + TopExp_Explorer exp(_Shape,TopAbs_FACE); + if(exp.More()) { + auto face = exp.Current(); + exp.Next(); + if(!exp.More()) { + BRepAdaptor_Surface adapt(TopoDS::Face(face)); + if(adapt.GetType() != GeomAbs_Plane) + return false; + pln = adapt.Plane(); + return true; + } + }else{ + TopExp_Explorer exp(_Shape,TopAbs_EDGE); + if(exp.More()) { + TopoDS_Shape edge = exp.Current(); + exp.Next(); + if(!exp.More()) { + // To deal with OCCT bug of wrong edge transformation + shape = BRepBuilderAPI_Copy(edge).Shape(); + } + } + } + try { + BRepLib_FindSurface finder(shape,tol,Standard_True); + if (!finder.Found()) + return false; + pln = GeomAdaptor_Surface(finder.Surface()).Plane(); + return true; + }catch (Standard_Failure &e) { + // For some reason the above BRepBuilderAPI_Copy failed to copy + // the geometry of some edge, causing exception with message + // BRepAdaptor_Curve::No geometry. However, without the above + // copy, circular edges often have the wrong transformation! + FC_LOG("failed to find surface: " << e.GetMessageString()); + return false; + } +} + +bool TopoShape::isCoplanar(const TopoShape &other, double tol) const { + if(isNull() || other.isNull()) + return false; + if(_Shape.IsEqual(other._Shape)) + return true; + gp_Pln pln1,pln2; + if(!findPlane(pln1,tol) || !other.findPlane(pln2,tol)) + return false; + if(tol<0.0) + tol = Precision::Confusion(); + return pln1.Position().IsCoplanar(pln2.Position(),tol,tol); +} + +bool TopoShape::_makETransform(const TopoShape &shape, + const Base::Matrix4D &rclTrf, const char *op, bool checkScale, bool copy) +{ + if(checkScale) { + if(rclTrf.hasScale()<0) { + makEGTransform(shape,rclTrf,op,copy); + return true; + } + } + makETransform(shape,convert(rclTrf),op,copy); + return false; +} + +TopoShape &TopoShape::makETransform(const TopoShape &shape, const gp_Trsf &trsf, const char *op, bool copy) { + // resetElementMap(); + + if(!copy) { + // OCCT checks the ScaleFactor against gp::Resolution() which is DBL_MIN!!! + copy = trsf.ScaleFactor()*trsf.HVectorialPart().Determinant() < 0. || + Abs(Abs(trsf.ScaleFactor()) - 1) > Precision::Confusion(); + } + TopoShape tmp(shape); + if(copy) { + BRepBuilderAPI_Transform mkTrf(shape.getShape(), trsf, Standard_True); + // TODO: calling Moved() is to make sure the shape has some Location, + // which is necessary for STEP export to work. However, if we reach + // here, it porabably means BRepBuilderAPI_Transform has modified + // underlying shapes (because of scaling), it will break compound child + // parent relationship anyway. In short, STEP import/export will most + // likely break badly if there is any scaling involved + tmp._Shape = mkTrf.Shape().Moved(gp_Trsf()); + }else + tmp._Shape.Move(trsf); + if(op || (shape.Tag && shape.Tag!=Tag)) { + _Shape = tmp._Shape; + // tmp.initCache(1); + // mapSubElement(tmp,op); + } else + *this = tmp; + return *this; +} + +TopoShape &TopoShape::makEGTransform(const TopoShape &shape, + const Base::Matrix4D &rclTrf, const char *op, bool copy) +{ + (void)op; + gp_GTrsf mat; + mat.SetValue(1,1,rclTrf[0][0]); + mat.SetValue(2,1,rclTrf[1][0]); + mat.SetValue(3,1,rclTrf[2][0]); + mat.SetValue(1,2,rclTrf[0][1]); + mat.SetValue(2,2,rclTrf[1][1]); + mat.SetValue(3,2,rclTrf[2][1]); + mat.SetValue(1,3,rclTrf[0][2]); + mat.SetValue(2,3,rclTrf[1][2]); + mat.SetValue(3,3,rclTrf[2][2]); + mat.SetValue(1,4,rclTrf[0][3]); + mat.SetValue(2,4,rclTrf[1][3]); + mat.SetValue(3,4,rclTrf[2][3]); + + // geometric transformation + BRepBuilderAPI_GTransform mkTrf(shape.getShape(), mat, copy); + _Shape = mkTrf.Shape(); + return *this; +} + diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index abb72591a427..e0c906138a24 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -123,6 +123,8 @@ class PartExport TopoShape : public Data::ComplexGeoData virtual bool getCenterOfGravity(Base::Vector3d& center) const; static void convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf); static void convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx); + static Base::Matrix4D convert(const gp_Trsf& trsf); + static gp_Trsf convert(const Base::Matrix4D& mtrx); //@} /** @name Subelement management */ @@ -148,10 +150,14 @@ class PartExport TopoShape : public Data::ComplexGeoData std::vector &faces) const; //@} /// get the Topo"sub"Shape with the given name - TopoDS_Shape getSubShape(const char* Type) const; + TopoDS_Shape getSubShape(const char* Type, bool silent=false) const; + TopoDS_Shape getSubShape(TopAbs_ShapeEnum type, int idx, bool silent=false) const; unsigned long countSubShapes(const char* Type) const; + unsigned long countSubShapes(TopAbs_ShapeEnum type) const; + bool hasSubShape(const char *Type) const; + bool hasSubShape(TopAbs_ShapeEnum type) const; /// get the Topo"sub"Shape with the given name - PyObject * getPySubShape(const char* Type) const; + PyObject * getPySubShape(const char* Type, bool silent=false) const; /** @name Save/restore */ //@{ @@ -189,6 +195,8 @@ class PartExport TopoShape : public Data::ComplexGeoData bool isValid() const; bool analyze(bool runBopCheck, std::ostream&) const; bool isClosed() const; + bool isCoplanar(const TopoShape &other, double tol=-1) const; + bool findPlane(gp_Pln &pln, double tol=-1) const; //@} /** @name Boolean operation*/ @@ -263,7 +271,7 @@ class PartExport TopoShape : public Data::ComplexGeoData //@{ void transformGeometry(const Base::Matrix4D &rclMat); TopoDS_Shape transformGShape(const Base::Matrix4D&) const; - void transformShape(const Base::Matrix4D&, bool copy); + bool transformShape(const Base::Matrix4D&, bool copy, bool checkScale=false); TopoDS_Shape mirror(const gp_Ax2&) const; TopoDS_Shape toNurbs() const; TopoDS_Shape replaceShape(const std::vector< std::pair >& s) const; @@ -289,6 +297,68 @@ class PartExport TopoShape : public Data::ComplexGeoData void getDomains(std::vector&) const; //@} + /** @name Element name mapping aware shape maker + * + * To be complete in next batch of patches + */ + //@{ + TopoShape &makECompound(const std::vector &shapes, const char *op=0, bool force=true); + + TopoShape &makEWires(const TopoShape &shape, const char *op=0, bool fix=false, double tol=0.0); + TopoShape makEWires(const char *op=0, bool fix=false, double tol=0.0) const { + return TopoShape().makEWires(*this,op,fix,tol); + } + TopoShape &makEFace(const std::vector &shapes, const char *op=0, const char *maker=0); + TopoShape &makEFace(const TopoShape &shape, const char *op=0, const char *maker=0); + TopoShape makEFace(const char *op=0, const char *maker=0) const { + return TopoShape().makEFace(*this,op,maker); + } + bool _makETransform(const TopoShape &shape, const Base::Matrix4D &mat, + const char *op=0, bool checkScale=false, bool copy=false); + + TopoShape &makETransform(const TopoShape &shape, const Base::Matrix4D &mat, + const char *op=0, bool checkScale=false, bool copy=false) { + _makETransform(shape,mat,op,checkScale,copy); + return *this; + } + TopoShape makETransform(const Base::Matrix4D &mat, const char *op=0, + bool checkScale=false, bool copy=false) const { + return TopoShape().makETransform(*this,mat,op,checkScale,copy); + } + + TopoShape &makETransform(const TopoShape &shape, const gp_Trsf &trsf, + const char *op=0, bool copy=false); + TopoShape makETransform(const gp_Trsf &trsf, const char *op=0, bool copy=false) const { + return TopoShape().makETransform(*this,trsf,op,copy); + } + + void move(const TopLoc_Location &loc) { + _Shape.Move(loc); + } + TopoShape moved(const TopLoc_Location &loc) const { + TopoShape ret(*this); + ret._Shape.Move(loc); + return ret; + } + + TopoShape &makEGTransform(const TopoShape &shape, const Base::Matrix4D &mat, + const char *op=0, bool copy=false); + TopoShape makEGTransform(const Base::Matrix4D &mat, const char *op=0, bool copy=false) const { + return TopoShape().makEGTransform(*this,mat,op,copy); + } + + TopoShape &makERefine(const TopoShape &shape, const char *op=0, bool no_fail=true); + TopoShape makERefine(const char *op=0, bool no_fail=true) const { + return TopoShape().makERefine(*this,op,no_fail); + } + //@} + + static TopAbs_ShapeEnum shapeType(const char *type,bool silent=false); + static TopAbs_ShapeEnum shapeType(char type,bool silent=false); + TopAbs_ShapeEnum shapeType(bool silent=false) const; + static const std::string &shapeName(TopAbs_ShapeEnum type,bool silent=false); + const std::string &shapeName(bool silent=false) const; + static std::pair shapeTypeAndIndex(const char *name); private: TopoDS_Shape _Shape; }; diff --git a/src/Mod/Part/App/TopoShapePy.xml b/src/Mod/Part/App/TopoShapePy.xml index c370745758c5..29a9f49cbc68 100644 --- a/src/Mod/Part/App/TopoShapePy.xml +++ b/src/Mod/Part/App/TopoShapePy.xml @@ -335,7 +335,19 @@ transformGeometry(Matrix) -> Shape Apply transformation on a shape without changing the underlying geometry. -transformShape(Matrix,[boolean copy=False]) -> None +transformShape(Matrix,[boolean copy=False, checkScale=False]) -> None + +If checkScale is True, it will use transformGeometry if non-uniform +scaling is detected. + + + + + +transformed(Matrix,copy=False,checkScale=False,op=None) -> shape + +Create a new transformed shape + @@ -343,11 +355,29 @@ transformShape(Matrix,[boolean copy=False]) -> None Apply the translation to the current location of this shape. + + + +translated(vector) -> shape + +Create a new shape with translation + + + - Apply the rotation (degree) to the current location of this shape - Shp.rotate(Vector(0,0,0),Vector(0,0,1),180) - rotate the shape around the Z Axis 180 degrees. +Apply the rotation (base,dir,degree) to the current location of this shape +Shp.rotate(Vector(0,0,0),Vector(0,0,1),180) - rotate the shape around the Z Axis 180 degrees. + + + + + + +rotated(base,dir,degree) -> shape + +Create a new shape with rotation. @@ -356,6 +386,15 @@ transformShape(Matrix,[boolean copy=False]) -> None Apply scaling with point and factor to this shape. + + + +scaled(factor,base=Vector(0,0,0)) -> shape + +Create a new shape with scale. + + + Make fillet. @@ -428,6 +467,23 @@ Returns: result of offsetting (wire or face or compound of those). Compounding structure follows that of source shape. + + + +makeWires(op=None): make wire(s) using the edges of this shape + +The function will sort any edges inside the current 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. + + + Reverses the orientation of this shape. @@ -486,6 +542,16 @@ If the shape is an edge it returns True if its vertices are the same. Checks if the shape is valid, i.e. neither null, nor empty nor corrupted. + + + isCoplanar(shape,tol=None) -- Checks if this shape is coplanar with the given shape. + + + + + findPlane(tol=None) -- return a plane if the shape is planar + + Tries to fix a broken shape. True is returned if the operation succeeded, False otherwise. @@ -635,6 +701,11 @@ infos contains additional info on the solutions. It is a list of tuples: Returns a SubElement + + + Returns the count of a type of element + + @@ -796,6 +867,12 @@ infos contains additional info on the solutions. It is a list of tuples: + + + List of sub-shapes in this shape. + + + Total length of the edges of the shape. diff --git a/src/Mod/Part/App/TopoShapePyImp.cpp b/src/Mod/Part/App/TopoShapePyImp.cpp index f7c753587f80..d3900a8f86e5 100644 --- a/src/Mod/Part/App/TopoShapePyImp.cpp +++ b/src/Mod/Part/App/TopoShapePyImp.cpp @@ -47,6 +47,7 @@ # include # include # include +# include # include # include # include @@ -58,6 +59,7 @@ # include # include # include +# include # include # include # include @@ -91,6 +93,7 @@ #include #include #include +#include using namespace Part; @@ -102,10 +105,6 @@ using namespace Part; #define M_PI_2 1.57079632679489661923 /* pi/2 */ #endif -namespace Part { -extern Py::Object shape2pyshape(const TopoDS_Shape &shape); -} - // returns a string which represents the object e.g. when printed in python std::string TopoShapePy::representation(void) const { @@ -127,29 +126,31 @@ int TopoShapePy::PyInit(PyObject* args, PyObject*) if (!PyArg_ParseTuple(args, "|O", &pcObj)) return -1; + auto shapes = getPyShapes(pcObj); + if (pcObj) { TopoShape shape; - try { - Py::Sequence list(pcObj); - bool first = true; - for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { - if (PyObject_TypeCheck((*it).ptr(), &(Part::GeometryPy::Type))) { - TopoDS_Shape sh = static_cast((*it).ptr())-> - getGeometryPtr()->toShape(); - if (first) { - first = false; - shape.setShape(sh); - } - else { - shape.setShape(shape.fuse(sh)); + PY_TRY { + if(PyObject_TypeCheck(pcObj,&TopoShapePy::Type)) { + shape = *static_cast(pcObj)->getTopoShapePtr(); + }else{ + Py::Sequence list(pcObj); + bool first = true; + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { + if (PyObject_TypeCheck((*it).ptr(), &(Part::GeometryPy::Type))) { + TopoDS_Shape sh = static_cast((*it).ptr())-> + getGeometryPtr()->toShape(); + if (first) { + first = false; + shape.setShape(sh); + } + else { + shape.setShape(shape.fuse(sh)); + } } } } - } - catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); - return -1; - } + }_PY_CATCH_OCC(return(-1)) getTopoShapePtr()->setShape(shape.getShape()); } @@ -1399,19 +1400,35 @@ PyObject* TopoShapePy::transformShape(PyObject *args) { PyObject *obj; PyObject *copy = Py_False; - if (!PyArg_ParseTuple(args, "O!|O!", &(Base::MatrixPy::Type),&obj,&(PyBool_Type), ©)) + PyObject *checkScale = Py_False; + if (!PyArg_ParseTuple(args, "O!|O!O", &(Base::MatrixPy::Type),&obj,&(PyBool_Type), ©,&checkScale)) return NULL; Base::Matrix4D mat = static_cast(obj)->value(); - try { - this->getTopoShapePtr()->transformShape(mat, PyObject_IsTrue(copy) ? true : false); - Py_Return; - } - catch (Standard_Failure& e) { - - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); - return NULL; - } + PY_TRY { + this->getTopoShapePtr()->transformShape(mat, PyObject_IsTrue(copy) ? true : false, + PyObject_IsTrue(checkScale)); + return IncRef(); + } PY_CATCH_OCC +} + +PyObject* TopoShapePy::transformed(PyObject *args, PyObject *keywds) +{ + static char *kwlist[] = {"matrix", "copy", "checkScale", "op", NULL}; + PyObject* pymat; + PyObject* copy = Py_False; + PyObject* checkScale = Py_False; + const char *op = 0; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|OOs", kwlist, + &Base::MatrixPy::Type, &pymat,©,&checkScale,&op)) + return 0; + Base::Matrix4D mat = static_cast(pymat)->value(); + (void)op; + PY_TRY { + TopoShape s(*getTopoShapePtr()); + s.transformShape(mat,PyObject_IsTrue(copy),PyObject_IsTrue(checkScale)); + return Py::new_reference_to(shape2pyshape(s)); + }PY_CATCH_OCC } PyObject* TopoShapePy::translate(PyObject *args) @@ -1438,7 +1455,7 @@ PyObject* TopoShapePy::translate(PyObject *args) TopoDS_Shape shape = getTopoShapePtr()->getShape(); shape.Move(loc); getTopoShapePtr()->setShape(shape); - Py_Return; + return IncRef(); } PyObject* TopoShapePy::rotate(PyObject *args) @@ -1448,7 +1465,7 @@ PyObject* TopoShapePy::rotate(PyObject *args) if (!PyArg_ParseTuple(args, "OOd", &obj1, &obj2, &angle)) return NULL; - try { + PY_TRY { // Vector also supports sequence Py::Sequence p1(obj1), p2(obj2); // Convert into OCC representation @@ -1466,11 +1483,8 @@ PyObject* TopoShapePy::rotate(PyObject *args) TopoDS_Shape shape = getTopoShapePtr()->getShape(); shape.Move(loc); getTopoShapePtr()->setShape(shape); - Py_Return; - } - catch (const Py::Exception&) { - return NULL; - } + return IncRef(); + } PY_CATCH_OCC } PyObject* TopoShapePy::scale(PyObject *args) @@ -1492,20 +1506,30 @@ PyObject* TopoShapePy::scale(PyObject *args) return NULL; } - try { + PY_TRY { gp_Trsf scl; scl.SetScale(pos, factor); BRepBuilderAPI_Transform BRepScale(scl); bool bCopy = true; BRepScale.Perform(getTopoShapePtr()->getShape(),bCopy); getTopoShapePtr()->setShape(BRepScale.Shape()); - Py_Return; - } - catch (Standard_Failure& e) { + return IncRef(); + } PY_CATCH_OCC +} - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); - return NULL; - } +PyObject* TopoShapePy::translated(PyObject *args) { + Py::Object pyobj(shape2pyshape(*getTopoShapePtr())); + return static_cast(pyobj.ptr())->translate(args); +} + +PyObject* TopoShapePy::rotated(PyObject *args) { + Py::Object pyobj(shape2pyshape(*getTopoShapePtr())); + return static_cast(pyobj.ptr())->rotate(args); +} + +PyObject* TopoShapePy::scaled(PyObject *args) { + Py::Object pyobj(shape2pyshape(*getTopoShapePtr())); + return static_cast(pyobj.ptr())->scale(args); } PyObject* TopoShapePy::makeFillet(PyObject *args) @@ -1827,13 +1851,34 @@ PyObject* TopoShapePy::isValid(PyObject *args) { if (!PyArg_ParseTuple(args, "")) return NULL; - try { + PY_TRY { return Py_BuildValue("O", (getTopoShapePtr()->isValid() ? Py_True : Py_False)); - } - catch (...) { - PyErr_SetString(PyExc_RuntimeError, "check failed, shape may be empty"); + } PY_CATCH_OCC +} + +PyObject* TopoShapePy::isCoplanar(PyObject *args) +{ + PyObject *pyObj; + double tol = -1; + if (!PyArg_ParseTuple(args, "O!|d", &TopoShapePy::Type, &pyObj, &tol)) return NULL; - } + PY_TRY { + return Py::new_reference_to(Py::Boolean(getTopoShapePtr()->isCoplanar( + *static_cast(pyObj)->getTopoShapePtr(),tol))); + }PY_CATCH_OCC +} + +PyObject* TopoShapePy::findPlane(PyObject *args) +{ + double tol = -1; + if (!PyArg_ParseTuple(args, "|d", &tol)) + return NULL; + PY_TRY { + gp_Pln pln; + if(getTopoShapePtr()->findPlane(pln,tol)) + return new PlanePy(new GeomPlane(new Geom_Plane(pln))); + Py_Return; + }PY_CATCH_OCC } PyObject* TopoShapePy::fix(PyObject *args) @@ -2052,10 +2097,16 @@ PyObject* TopoShapePy::makeShapeFromMesh(PyObject *args) getTopoShapePtr()->setFaces(Points, Facets,tolerance); Py_Return; - } - catch (const Py::Exception&) { - return 0; - } + } PY_CATCH_OCC +} + +PyObject* TopoShapePy::makeWires(PyObject *args) { + const char *op = 0; + if (!PyArg_ParseTuple(args, "s", &op)) + return NULL; + PY_TRY { + return Py::new_reference_to(shape2pyshape(getTopoShapePtr()->makEWires(op))); + }PY_CATCH_OCC } PyObject* TopoShapePy::toNurbs(PyObject *args) @@ -2157,6 +2208,16 @@ PyObject* TopoShapePy::getElement(PyObject *args) return 0; } +PyObject* TopoShapePy::countElement(PyObject *args) +{ + char* input; + if (!PyArg_ParseTuple(args, "s", &input)) + return NULL; + PY_TRY { + return Py::new_reference_to(Py::Int((long)getTopoShapePtr()->countSubShapes(input))); + } PY_CATCH_OCC +} + PyObject* TopoShapePy::getTolerance(PyObject *args) { int mode; @@ -2900,6 +2961,14 @@ void TopoShapePy::setOrientation(Py::String arg) getTopoShapePtr()->setShape(sh); } +Py::List TopoShapePy::getSubShapes(void) const +{ + Py::List ret; + for(TopoDS_Iterator it(getTopoShapePtr()->getShape());it.More();it.Next()) + ret.append(shape2pyshape(it.Value())); + return ret; +} + Py::List TopoShapePy::getFaces(void) const { Py::List ret; @@ -3117,38 +3186,11 @@ Py::Float TopoShapePy::getVolume(void) const PyObject *TopoShapePy::getCustomAttributes(const char* attr) const { if (!attr) return 0; - std::string name(attr); - try { - if (name.size() > 4 && name.substr(0,4) == "Face" && name[4]>=48 && name[4]<=57) { - std::unique_ptr s(static_cast - (getTopoShapePtr()->getSubElementByName(attr))); - TopoDS_Shape Shape = s->Shape; - TopoShapeFacePy* face = new TopoShapeFacePy(new TopoShape(Shape)); - face->setNotTracking(); - return face; - } - else if (name.size() > 4 && name.substr(0,4) == "Edge" && name[4]>=48 && name[4]<=57) { - std::unique_ptr s(static_cast - (getTopoShapePtr()->getSubElementByName(attr))); - TopoDS_Shape Shape = s->Shape; - TopoShapeEdgePy* edge = new TopoShapeEdgePy(new TopoShape(Shape)); - edge->setNotTracking(); - return edge; - } - else if (name.size() > 6 && name.substr(0,6) == "Vertex" && name[6]>=48 && name[6]<=57) { - std::unique_ptr s(static_cast - (getTopoShapePtr()->getSubElementByName(attr))); - TopoDS_Shape Shape = s->Shape; - TopoShapeVertexPy* vertex = new TopoShapeVertexPy(new TopoShape(Shape)); - vertex->setNotTracking(); - return vertex; - } - } - catch (Standard_Failure& e) { - - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); - return 0; - } + PY_TRY { + TopoDS_Shape res = getTopoShapePtr()->getSubShape(attr,true); + if(!res.IsNull()) + return Py::new_reference_to(shape2pyshape(res)); + }PY_CATCH_OCC return 0; } diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index c4aba0f9f6c5..efa069cc13be 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -280,7 +280,8 @@ CmdPartCut::CmdPartCut() void CmdPartCut::activated(int iMsg) { Q_UNUSED(iMsg); - std::vector Sel = getSelection().getSelectionEx(0, Part::Feature::getClassTypeId()); + std::vector Sel = + getSelection().getSelectionEx(0, App::DocumentObject::getClassTypeId(),3); if (Sel.size() != 2) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Select two shapes please.")); @@ -290,16 +291,14 @@ void CmdPartCut::activated(int iMsg) bool askUser = false; for (std::vector::iterator it = Sel.begin(); it != Sel.end(); ++it) { App::DocumentObject* obj = it->getObject(); - if (obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - const TopoDS_Shape& shape = static_cast(obj)->Shape.getValue(); - if (!PartGui::checkForSolids(shape) && !askUser) { - int ret = QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Non-solids selected"), - QObject::tr("The use of non-solids for boolean operations may lead to unexpected results.\n" - "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No); - if (ret == QMessageBox::No) - return; - askUser = true; - } + const TopoDS_Shape& shape = Part::Feature::getShape(obj); + if (!PartGui::checkForSolids(shape) && !askUser) { + int ret = QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Non-solids selected"), + QObject::tr("The use of non-solids for boolean operations may lead to unexpected results.\n" + "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No); + if (ret == QMessageBox::No) + return; + askUser = true; } } @@ -335,7 +334,8 @@ void CmdPartCut::activated(int iMsg) bool CmdPartCut::isActive(void) { - return getSelection().countObjectsOfType(Part::Feature::getClassTypeId())==2; + return getSelection().countObjectsOfType( + App::DocumentObject::getClassTypeId(),0,3)==2; } //=========================================================================== @@ -358,21 +358,20 @@ CmdPartCommon::CmdPartCommon() void CmdPartCommon::activated(int iMsg) { Q_UNUSED(iMsg); - std::vector Sel = getSelection().getSelectionEx(0, Part::Feature::getClassTypeId()); + std::vector Sel = + getSelection().getSelectionEx(0, App::DocumentObject::getClassTypeId(), 3); //test if selected object is a compound, and if it is, look how many children it has... std::size_t numShapes = 0; if (Sel.size() == 1){ numShapes = 1; //to be updated later in code, if Gui::SelectionObject selobj = Sel[0]; - if (selobj.getObject()->isDerivedFrom(Part::Feature::getClassTypeId())){ - TopoDS_Shape sh = static_cast(selobj.getObject())->Shape.getValue(); - if (sh.ShapeType() == TopAbs_COMPOUND) { - numShapes = 0; - TopoDS_Iterator it(sh); - for (; it.More(); it.Next()) { - ++numShapes; - } + TopoDS_Shape sh = Part::Feature::getShape(selobj.getObject()); + if (sh.ShapeType() == TopAbs_COMPOUND) { + numShapes = 0; + TopoDS_Iterator it(sh); + for (; it.More(); it.Next()) { + ++numShapes; } } } else { @@ -392,19 +391,17 @@ void CmdPartCommon::activated(int iMsg) str << "App.activeDocument()." << FeatName << ".Shapes = ["; for (std::vector::iterator it = Sel.begin(); it != Sel.end(); ++it) { App::DocumentObject* obj = it->getObject(); - if (obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - const TopoDS_Shape& shape = static_cast(obj)->Shape.getValue(); - if (!PartGui::checkForSolids(shape) && !askUser) { - int ret = QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Non-solids selected"), - QObject::tr("The use of non-solids for boolean operations may lead to unexpected results.\n" - "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No); - if (ret == QMessageBox::No) - return; - askUser = true; - } - str << "App.activeDocument()." << it->getFeatName() << ","; - partObjects.push_back(*it); + const TopoDS_Shape& shape = Part::Feature::getShape(obj); + if (!PartGui::checkForSolids(shape) && !askUser) { + int ret = QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Non-solids selected"), + QObject::tr("The use of non-solids for boolean operations may lead to unexpected results.\n" + "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No); + if (ret == QMessageBox::No) + return; + askUser = true; } + str << "App.activeDocument()." << it->getFeatName() << ","; + partObjects.push_back(*it); } str << "]"; @@ -437,7 +434,8 @@ void CmdPartCommon::activated(int iMsg) bool CmdPartCommon::isActive(void) { - return getSelection().countObjectsOfType(Part::Feature::getClassTypeId())>=1; + return getSelection().countObjectsOfType( + App::DocumentObject::getClassTypeId(),0,3)>=1; } //=========================================================================== @@ -460,21 +458,20 @@ CmdPartFuse::CmdPartFuse() void CmdPartFuse::activated(int iMsg) { Q_UNUSED(iMsg); - std::vector Sel = getSelection().getSelectionEx(0, Part::Feature::getClassTypeId()); + std::vector Sel = + getSelection().getSelectionEx(0, App::DocumentObject::getClassTypeId(),3); //test if selected object is a compound, and if it is, look how many children it has... std::size_t numShapes = 0; if (Sel.size() == 1){ numShapes = 1; //to be updated later in code Gui::SelectionObject selobj = Sel[0]; - if (selobj.getObject()->isDerivedFrom(Part::Feature::getClassTypeId())){ - TopoDS_Shape sh = static_cast(selobj.getObject())->Shape.getValue(); - if (sh.ShapeType() == TopAbs_COMPOUND) { - numShapes = 0; - TopoDS_Iterator it(sh); - for (; it.More(); it.Next()) { - ++numShapes; - } + TopoDS_Shape sh = Part::Feature::getShape(selobj.getObject()); + if (sh.ShapeType() == TopAbs_COMPOUND) { + numShapes = 0; + TopoDS_Iterator it(sh); + for (; it.More(); it.Next()) { + ++numShapes; } } } else { @@ -494,19 +491,17 @@ void CmdPartFuse::activated(int iMsg) str << "App.activeDocument()." << FeatName << ".Shapes = ["; for (std::vector::iterator it = Sel.begin(); it != Sel.end(); ++it) { App::DocumentObject* obj = it->getObject(); - if (obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - const TopoDS_Shape& shape = static_cast(obj)->Shape.getValue(); - if (!PartGui::checkForSolids(shape) && !askUser) { - int ret = QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Non-solids selected"), - QObject::tr("The use of non-solids for boolean operations may lead to unexpected results.\n" - "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No); - if (ret == QMessageBox::No) - return; - askUser = true; - } - str << "App.activeDocument()." << it->getFeatName() << ","; - partObjects.push_back(*it); + const TopoDS_Shape& shape = Part::Feature::getShape(obj); + if (!PartGui::checkForSolids(shape) && !askUser) { + int ret = QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Non-solids selected"), + QObject::tr("The use of non-solids for boolean operations may lead to unexpected results.\n" + "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No); + if (ret == QMessageBox::No) + return; + askUser = true; } + str << "App.activeDocument()." << it->getFeatName() << ","; + partObjects.push_back(*it); } str << "]"; @@ -539,7 +534,8 @@ void CmdPartFuse::activated(int iMsg) bool CmdPartFuse::isActive(void) { - return getSelection().countObjectsOfType(Part::Feature::getClassTypeId())>=1; + return getSelection().countObjectsOfType( + App::DocumentObject::getClassTypeId(),0,3)>=1; } //=========================================================================== @@ -898,7 +894,8 @@ CmdPartCompound::CmdPartCompound() void CmdPartCompound::activated(int iMsg) { Q_UNUSED(iMsg); - unsigned int n = getSelection().countObjectsOfType(Part::Feature::getClassTypeId()); + unsigned int n = getSelection().countObjectsOfType( + App::DocumentObject::getClassTypeId(),0,3); if (n < 1) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Select one shape or more, please.")); @@ -930,7 +927,8 @@ void CmdPartCompound::activated(int iMsg) bool CmdPartCompound::isActive(void) { - return getSelection().countObjectsOfType(Part::Feature::getClassTypeId())>=1; + return getSelection().countObjectsOfType( + App::DocumentObject::getClassTypeId(),0,3)>=1; } //=========================================================================== @@ -953,7 +951,8 @@ CmdPartSection::CmdPartSection() void CmdPartSection::activated(int iMsg) { Q_UNUSED(iMsg); - std::vector Sel = getSelection().getSelectionEx(0, Part::Feature::getClassTypeId()); + std::vector Sel = + getSelection().getSelectionEx(0, App::DocumentObject::getClassTypeId(),3); if (Sel.size() != 2) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Select two shapes please.")); @@ -977,7 +976,7 @@ void CmdPartSection::activated(int iMsg) bool CmdPartSection::isActive(void) { - return getSelection().countObjectsOfType(Part::Feature::getClassTypeId())==2; + return getSelection().countObjectsOfType(App::DocumentObject::getClassTypeId(),0,3)==2; } //=========================================================================== @@ -1084,7 +1083,7 @@ void CmdPartExport::activated(int iMsg) bool CmdPartExport::isActive(void) { - return Gui::Selection().countObjectsOfType(Part::Feature::getClassTypeId()) > 0; + return Gui::Selection().countObjectsOfType(App::DocumentObject::getClassTypeId(),0,3) > 0; } //=========================================================================== @@ -1155,10 +1154,10 @@ void CmdPartMakeSolid::activated(int iMsg) { Q_UNUSED(iMsg); std::vector objs = Gui::Selection().getObjectsOfType - (Part::Feature::getClassTypeId()); + (App::DocumentObject::getClassTypeId(),0,3); runCommand(Doc, "import Part"); for (std::vector::iterator it = objs.begin(); it != objs.end(); ++it) { - const TopoDS_Shape& shape = static_cast(*it)->Shape.getValue(); + const TopoDS_Shape& shape = Part::Feature::getShape(*it); if (!shape.IsNull()) { TopAbs_ShapeEnum type = shape.ShapeType(); QString str; @@ -1210,7 +1209,7 @@ void CmdPartMakeSolid::activated(int iMsg) bool CmdPartMakeSolid::isActive(void) { return Gui::Selection().countObjectsOfType - (Part::Feature::getClassTypeId()) > 0; + (App::DocumentObject::getClassTypeId(),0,3) > 0; } //=========================================================================== @@ -1236,7 +1235,7 @@ void CmdPartReverseShape::activated(int iMsg) (Part::Feature::getClassTypeId()); runCommand(Doc, "import Part"); for (std::vector::iterator it = objs.begin(); it != objs.end(); ++it) { - const TopoDS_Shape& shape = static_cast(*it)->Shape.getValue(); + const TopoDS_Shape& shape = Part::Feature::getShape(*it); if (!shape.IsNull()) { QString str = QString::fromLatin1( "__s__=App.ActiveDocument.%1.Shape.copy()\n" @@ -1345,7 +1344,7 @@ CmdPartMakeFace::CmdPartMakeFace() void CmdPartMakeFace::activated(int iMsg) { Q_UNUSED(iMsg); - std::vector sketches = Gui::Selection().getObjectsOfType(); + auto sketches = Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(),0,3); openCommand("Make face"); try { @@ -1353,9 +1352,8 @@ void CmdPartMakeFace::activated(int iMsg) std::stringstream str; str << doc.getDocumentPython() << ".addObject(\"Part::Face\", \"Face\").Sources = ("; - for (std::vector::iterator it = sketches.begin(); it != sketches.end(); ++it) { - App::DocumentObjectT obj(*it); - str << obj.getObjectPython() << ", "; + for (auto &obj : sketches) { + str << App::DocumentObjectT(obj).getObjectPython() << ", "; } str << ")"; @@ -1372,7 +1370,7 @@ void CmdPartMakeFace::activated(int iMsg) bool CmdPartMakeFace::isActive(void) { - return (Gui::Selection().countObjectsOfType(Part::Feature::getClassTypeId()) > 0 && + return (Gui::Selection().countObjectsOfType(App::DocumentObject::getClassTypeId(),0,3) > 0 && !Gui::Control().activeDialog()); } @@ -1643,7 +1641,6 @@ void CmdPartOffset::activated(int iMsg) doCommand(Doc,"App.ActiveDocument.%s.Source = App.ActiveDocument.%s" ,offset.c_str(), shape->getNameInDocument()); doCommand(Doc,"App.ActiveDocument.%s.Value = 1.0",offset.c_str()); updateActive(); - doCommand(Gui,"Gui.ActiveDocument.%s.DisplayMode = 'Wireframe'", shape->getNameInDocument()); //if (isActiveObjectValid()) // doCommand(Gui,"Gui.ActiveDocument.hide(\"%s\")",shape->getNameInDocument()); doCommand(Gui,"Gui.ActiveDocument.setEdit('%s')",offset.c_str()); @@ -1659,7 +1656,7 @@ void CmdPartOffset::activated(int iMsg) bool CmdPartOffset::isActive(void) { Base::Type partid = Base::Type::fromName("Part::Feature"); - bool objectsSelected = Gui::Selection().countObjectsOfType(partid) == 1; + bool objectsSelected = Gui::Selection().countObjectsOfType(partid,0,3) == 1; return (objectsSelected && !Gui::Control().activeDialog()); } @@ -1685,7 +1682,7 @@ CmdPartOffset2D::CmdPartOffset2D() void CmdPartOffset2D::activated(int iMsg) { Q_UNUSED(iMsg); - App::DocumentObject* shape = getSelection().getObjectsOfType(Part::Feature::getClassTypeId()).front(); + App::DocumentObject* shape = getSelection().getObjectsOfType(Part::Feature::getClassTypeId(),0,3).front(); std::string offset = getUniqueObjectName("Offset2D"); openCommand("Make 2D Offset"); @@ -1708,7 +1705,7 @@ void CmdPartOffset2D::activated(int iMsg) bool CmdPartOffset2D::isActive(void) { Base::Type partid = Base::Type::fromName("Part::Feature"); - bool objectsSelected = Gui::Selection().countObjectsOfType(partid) == 1; + bool objectsSelected = Gui::Selection().countObjectsOfType(partid,0,3) == 1; return (objectsSelected && !Gui::Control().activeDialog()); } @@ -1801,7 +1798,7 @@ void CmdPartCompOffset::languageChange() bool CmdPartCompOffset::isActive(void) { Base::Type partid = Base::Type::fromName("Part::Feature"); - bool objectsSelected = Gui::Selection().countObjectsOfType(partid) == 1; + bool objectsSelected = Gui::Selection().countObjectsOfType(partid,0,3) == 1; return (objectsSelected && !Gui::Control().activeDialog()); } @@ -2225,6 +2222,35 @@ bool CmdMeasureAngular::isActive(void) return hasActiveDocument(); } +//=========================================================================== +// Part_Measure_Refresh +//=========================================================================== + +DEF_STD_CMD_A(CmdMeasureRefresh); + +CmdMeasureRefresh::CmdMeasureRefresh() + : Command("Part_Measure_Refresh") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Refresh"); + sToolTipText = QT_TR_NOOP("Refresh"); + sWhatsThis = "Part_Measure_Refresh"; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Refresh"; +} + +void CmdMeasureRefresh::activated(int iMsg) +{ + Q_UNUSED(iMsg); + PartGui::refreshDimensions(); +} + +bool CmdMeasureRefresh::isActive(void) +{ + return hasActiveDocument(); +} + //=========================================================================== // Part_Measure_Clear_All //=========================================================================== @@ -2449,6 +2475,7 @@ void CreatePartCommands(void) rcCmdMgr.addCommand(new CmdColorPerFace()); rcCmdMgr.addCommand(new CmdMeasureLinear()); rcCmdMgr.addCommand(new CmdMeasureAngular()); + rcCmdMgr.addCommand(new CmdMeasureRefresh()); rcCmdMgr.addCommand(new CmdMeasureClearAll()); rcCmdMgr.addCommand(new CmdMeasureToggleAll()); rcCmdMgr.addCommand(new CmdMeasureToggle3d()); diff --git a/src/Mod/Part/Gui/CommandSimple.cpp b/src/Mod/Part/Gui/CommandSimple.cpp index 72b7d4448506..8f19f2ccfb1e 100644 --- a/src/Mod/Part/Gui/CommandSimple.cpp +++ b/src/Mod/Part/Gui/CommandSimple.cpp @@ -186,33 +186,114 @@ CmdPartSimpleCopy::CmdPartSimpleCopy() sPixmap = "Tree_Part"; } +static void _copyShape(const char *cmdName, bool resolve,bool needElement=false, bool refine=false) { + Gui::WaitCursor wc; + Gui::Command::openCommand(cmdName); + for(auto &sel : Gui::Selection().getSelectionEx("*",App::DocumentObject::getClassTypeId(),resolve)) { + std::map subMap; + auto obj = sel.getObject(); + if(!obj) continue; + if(resolve || !sel.hasSubNames()) + subMap.emplace("",obj); + else { + for(const auto &sub : sel.getSubNames()) { + const char *element = 0; + auto sobj = obj->resolve(sub.c_str(),0,0,&element); + if(!sobj) continue; + if(!needElement && element) + subMap.emplace(sub.substr(0,element-sub.c_str()),sobj); + else + subMap.emplace(sub,sobj); + } + if(subMap.empty()) + continue; + } + auto parentName = Gui::Command::getObjectCmd(obj); + for(auto &v : subMap) { + Gui::Command::doCommand(Gui::Command::Doc, + "__shape = Part.getShape(%s,'%s',needSubElement=%s,refine=%s)%s\n" + "App.ActiveDocument.addObject('Part::Feature','%s').Shape=__shape\n" + "App.ActiveDocument.ActiveObject.Label=%s.Label\n", + parentName.c_str(), v.first.c_str(), + needElement?"True":"False", refine?"True":"False", + needElement?".copy()":"", + v.second->getNameInDocument(), + Gui::Command::getObjectCmd(v.second).c_str()); + auto newObj = App::GetApplication().getActiveDocument()->getActiveObject(); + Gui::Command::copyVisual(newObj, "ShapeColor", v.second); + Gui::Command::copyVisual(newObj, "LineColor", v.second); + Gui::Command::copyVisual(newObj, "PointColor", v.second); + } + } + Gui::Command::commitCommand(); + Gui::Command::updateActive(); +} + void CmdPartSimpleCopy::activated(int iMsg) { Q_UNUSED(iMsg); - Base::Type partid = Base::Type::fromName("Part::Feature"); - std::vector objs = Gui::Selection().getSelectionEx(0, partid); - openCommand("Create Copy"); - for (std::vector::iterator it = objs.begin(); it != objs.end(); ++it) { - doCommand(Doc,"App.ActiveDocument.addObject('Part::Feature','%s').Shape=" - "App.ActiveDocument.%s.Shape\n" - "App.ActiveDocument.ActiveObject.Label=" - "App.ActiveDocument.%s.Label\n", - it->getFeatName(), - it->getFeatName(), - it->getFeatName()); - copyVisual("ActiveObject", "ShapeColor", it->getFeatName()); - copyVisual("ActiveObject", "LineColor", it->getFeatName()); - copyVisual("ActiveObject", "PointColor", it->getFeatName()); - copyVisual("ActiveObject", "DiffuseColor", it->getFeatName()); - } - commitCommand(); - updateActive(); + _copyShape("Simple copy",true); } bool CmdPartSimpleCopy::isActive(void) { - Base::Type partid = Base::Type::fromName("Part::Feature"); - return Gui::Selection().countObjectsOfType(partid) > 0; + return Gui::Selection().hasSelection(); +} + +//=========================================================================== +// Part_TransformedCopy +//=========================================================================== +DEF_STD_CMD_A(CmdPartTransformedCopy); + +CmdPartTransformedCopy::CmdPartTransformedCopy() + : Command("Part_TransformedCopy") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Create transformed copy"); + sToolTipText = QT_TR_NOOP("Create a non-parametric copy with transformed placement"); + sWhatsThis = "Part_TransformCopy"; + sStatusTip = sToolTipText; + sPixmap = "Part_Transformed_Copy.svg"; +} + +void CmdPartTransformedCopy::activated(int iMsg) +{ + Q_UNUSED(iMsg); + _copyShape("Transformed copy",false); +} + +bool CmdPartTransformedCopy::isActive(void) +{ + return Gui::Selection().hasSelection(); +} + +//=========================================================================== +// Part_ElementCopy +//=========================================================================== +DEF_STD_CMD_A(CmdPartElementCopy); + +CmdPartElementCopy::CmdPartElementCopy() + : Command("Part_ElementCopy") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Create shape element copy"); + sToolTipText = QT_TR_NOOP("Create a non-parametric copy of the selected shape element"); + sWhatsThis = "Part_ElementCopy"; + sStatusTip = sToolTipText; + sPixmap = "Part_Element_Copy.svg"; +} + +void CmdPartElementCopy::activated(int iMsg) +{ + Q_UNUSED(iMsg); + _copyShape("Element copy",false,true); +} + +bool CmdPartElementCopy::isActive(void) +{ + return Gui::Selection().hasSelection(); } //=========================================================================== @@ -235,53 +316,12 @@ CmdPartRefineShape::CmdPartRefineShape() void CmdPartRefineShape::activated(int iMsg) { Q_UNUSED(iMsg); - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Part"); - bool parametric = hGrp->GetBool("ParametricRefine", true); - - Gui::WaitCursor wc; - Base::Type partid = Base::Type::fromName("Part::Feature"); - std::vector objs = Gui::Selection().getObjectsOfType(partid); - openCommand("Refine shape"); - for (std::vector::iterator it = objs.begin(); it != objs.end(); ++it) { - try { - if (parametric) { - doCommand(Doc,"App.ActiveDocument.addObject('Part::Refine','%s').Source=" - "App.ActiveDocument.%s\n" - "App.ActiveDocument.ActiveObject.Label=" - "App.ActiveDocument.%s.Label\n" - "Gui.ActiveDocument.%s.hide()\n", - (*it)->getNameInDocument(), - (*it)->getNameInDocument(), - (*it)->getNameInDocument(), - (*it)->getNameInDocument()); - } - else { - doCommand(Doc,"App.ActiveDocument.addObject('Part::Feature','%s').Shape=" - "App.ActiveDocument.%s.Shape.removeSplitter()\n" - "App.ActiveDocument.ActiveObject.Label=" - "App.ActiveDocument.%s.Label\n" - "Gui.ActiveDocument.%s.hide()\n", - (*it)->getNameInDocument(), - (*it)->getNameInDocument(), - (*it)->getNameInDocument(), - (*it)->getNameInDocument()); - } - copyVisual("ActiveObject", "ShapeColor", (*it)->getNameInDocument()); - copyVisual("ActiveObject", "LineColor", (*it)->getNameInDocument()); - copyVisual("ActiveObject", "PointColor", (*it)->getNameInDocument()); - } - catch (const Base::Exception& e) { - Base::Console().Warning("%s: %s\n", (*it)->Label.getValue(), e.what()); - } - } - commitCommand(); - updateActive(); + _copyShape("Refined copy",true,false,true); } bool CmdPartRefineShape::isActive(void) { - Base::Type partid = Base::Type::fromName("Part::Feature"); - return Gui::Selection().countObjectsOfType(partid) > 0; + return Gui::Selection().hasSelection(); } //=========================================================================== @@ -381,6 +421,8 @@ void CreateSimplePartCommands(void) rcCmdMgr.addCommand(new CmdPartSimpleCylinder()); rcCmdMgr.addCommand(new CmdPartShapeFromMesh()); rcCmdMgr.addCommand(new CmdPartSimpleCopy()); + rcCmdMgr.addCommand(new CmdPartElementCopy()); + rcCmdMgr.addCommand(new CmdPartTransformedCopy()); rcCmdMgr.addCommand(new CmdPartRefineShape()); rcCmdMgr.addCommand(new CmdPartDefeaturing()); } diff --git a/src/Mod/Part/Gui/DlgExtrusion.cpp b/src/Mod/Part/Gui/DlgExtrusion.cpp index 6e4d2083c873..1e52d14a5067 100644 --- a/src/Mod/Part/Gui/DlgExtrusion.cpp +++ b/src/Mod/Part/Gui/DlgExtrusion.cpp @@ -57,6 +57,8 @@ #include #include +FC_LOG_LEVEL_INIT("Part",true,true); + using namespace PartGui; class DlgExtrusion::EdgeSelection : public Gui::SelectionFilterGate @@ -433,9 +435,8 @@ void DlgExtrusion::apply() assert(sourceObj); if (!sourceObj->isDerivedFrom(Part::Feature::getClassTypeId())){ - std::stringstream errmsg; - errmsg << "Object " << sourceObj->getNameInDocument() << " is not Part object (has no OCC shape). Can't extrude it.\n"; - Base::Console().Error(errmsg.str().c_str()); + FC_ERR("Object " << sourceObj->getFullName() + << " is not Part object (has no OCC shape). Can't extrude it."); continue; } @@ -447,16 +448,16 @@ void DlgExtrusion::apply() //label = QString::fromLatin1("%1_Extrude").arg((*it)->text(0)); } - Gui::Command::doCommand(Gui::Command::Doc, "f = FreeCAD.getDocument('%s').addObject('Part::Extrusion', '%s')", sourceObj->getDocument()->getName(), name.c_str()); + FCMD_OBJ_DOC_CMD(sourceObj,"addObject('Part::Extrusion','" << name << "')"); + auto newObj = sourceObj->getDocument()->getObject(name.c_str()); - this->writeParametersToFeature(*(sourceObj->getDocument()->getObject(name.c_str())), sourceObj); + this->writeParametersToFeature(*newObj, sourceObj); - std::string sourceObjectName = sourceObj->getNameInDocument(); - Gui::Command::copyVisual(name.c_str(), "ShapeColor", sourceObjectName.c_str()); - Gui::Command::copyVisual(name.c_str(), "LineColor", sourceObjectName.c_str()); - Gui::Command::copyVisual(name.c_str(), "PointColor", sourceObjectName.c_str()); + Gui::Command::copyVisual(newObj, "ShapeColor", sourceObj); + Gui::Command::copyVisual(newObj, "LineColor", sourceObj); + Gui::Command::copyVisual(newObj, "PointColor", sourceObj); - Gui::Command::doCommand(Gui::Command::Gui,"f.Base.ViewObject.hide()"); + FCMD_OBJ_HIDE(sourceObj); } activeDoc->commitTransaction(); diff --git a/src/Mod/Part/Gui/DlgFilletEdges.cpp b/src/Mod/Part/Gui/DlgFilletEdges.cpp index 138ccd386715..23c16a0c237d 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.cpp +++ b/src/Mod/Part/Gui/DlgFilletEdges.cpp @@ -632,6 +632,11 @@ void DlgFilletEdges::setupFillet(const std::vector& objs) std::vector::iterator selIt = std::find_if(selObj.begin(), selObj.end(), Private::SelectionObjectCompare(d->object)); + + /* + * Edit: the following check is no longer necessary, as Gui::Selection + * will do the check + * // If sub-objects are already selected then only add the un-selected parts. // This is impotant to avoid recursive calls of rmvSelection() which // invalidates the internal iterator (#0002200). @@ -645,9 +650,12 @@ void DlgFilletEdges::setupFillet(const std::vector& objs) std::set_difference(subElements.begin(), subElements.end(), selElements.begin(), selElements.end(), biit); subElements = complementary; } + */ + + Gui::Selection().clearSelection(doc->getName()); if (!subElements.empty()) { - Gui::Selection().addSelection(doc->getName(), + Gui::Selection().addSelections(doc->getName(), d->object->getNameInDocument(), subElements); } @@ -795,7 +803,7 @@ void DlgFilletEdges::on_selectAllButton_clicked() if (d->object) { App::Document* doc = d->object->getDocument(); - Gui::Selection().addSelection(doc->getName(), + Gui::Selection().addSelections(doc->getName(), d->object->getNameInDocument(), subElements); } diff --git a/src/Mod/Part/Gui/Resources/Part.qrc b/src/Mod/Part/Gui/Resources/Part.qrc index d633f820e7e0..cbc770bdb74d 100644 --- a/src/Mod/Part/Gui/Resources/Part.qrc +++ b/src/Mod/Part/Gui/Resources/Part.qrc @@ -59,7 +59,10 @@ icons/Part_Measure_Toggle_Delta.svg icons/Part_Measure_Step_Active.svg icons/Part_Measure_Step_Done.svg + icons/Part_Measure_Refresh.svg icons/Part_Refine_Shape.svg + icons/Part_Element_Copy.svg + icons/Part_Transformed_Copy.svg icons/Tree_Part_Box_Parametric.svg icons/Tree_Part_Cylinder_Parametric.svg icons/Tree_Part_Cone_Parametric.svg diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Element_Copy.svg b/src/Mod/Part/Gui/Resources/icons/Part_Element_Copy.svg new file mode 100644 index 000000000000..17035d080446 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Element_Copy.svg @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [wmayer] + + + Tree_Part + 2011-10-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Part/Gui/Resources/icons/Tree_Part.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Refresh.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Refresh.svg new file mode 100644 index 000000000000..182de35bb3b7 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Refresh.svg @@ -0,0 +1,659 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [blobfish] + + + Part_Measure_Linear + 2013-12-17 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Part/Gui/Resources/icons/Part_Measure_Linear.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Transformed_Copy.svg b/src/Mod/Part/Gui/Resources/icons/Part_Transformed_Copy.svg new file mode 100644 index 000000000000..0da8a6d4edd7 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Transformed_Copy.svg @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [wmayer] + + + Part_Box + 2011-10-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Part/Gui/Resources/icons/Part_Box.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index e2bfe4415573..2aff162fa1b4 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -109,6 +109,7 @@ void TaskAttacher::makeRefStrings(std::vector& refstrings, std::vector< TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider,QWidget *parent, QString picture, QString text) : TaskBox(Gui::BitmapFactory().pixmap(picture.toLatin1()), text, true, parent), + SelectionObserver(ViewProvider), ViewProvider(ViewProvider) { //check if we are attachable @@ -348,8 +349,8 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) std::vector refs = pcAttach->Support.getValues(); std::vector refnames = pcAttach->Support.getSubValues(); App::DocumentObject* selObj = ViewProvider->getObject()->getDocument()->getObject(msg.pObjectName); - if (selObj == ViewProvider->getObject()) return;//prevent self-referencing - + if (!selObj || selObj == ViewProvider->getObject()) return;//prevent self-referencing + std::string subname = msg.pSubName; // Remove subname for planes and datum features @@ -1021,36 +1022,28 @@ bool TaskDlgAttacher::accept() return true; Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType(); - std::string name = ViewProvider->getObject()->getNameInDocument(); - std::string appDocument = doc.getAppDocumentPython(); - std::string guiDocument = doc.getGuiDocumentPython(); + auto obj = ViewProvider->getObject(); //DeepSOIC: changed this to heavily rely on dialog constantly updating feature properties if (pcAttach->AttachmentOffset.isTouched()){ Base::Placement plm = pcAttach->AttachmentOffset.getValue(); double yaw, pitch, roll; plm.getRotation().getYawPitchRoll(yaw,pitch,roll); - Gui::Command::doCommand(Gui::Command::Doc,"%s.%s.AttachmentOffset = App.Placement(App.Vector(%.10f, %.10f, %.10f), App.Rotation(%.10f, %.10f, %.10f))", - appDocument.c_str(), name.c_str(), + FCMD_OBJ_CMD2("AttachmentOffset = App.Placement(App.Vector(%.10f, %.10f, %.10f), App.Rotation(%.10f, %.10f, %.10f))", + obj, plm.getPosition().x, plm.getPosition().y, plm.getPosition().z, yaw, pitch, roll); } - Gui::Command::doCommand(Gui::Command::Doc,"%s.%s.MapReversed = %s", - appDocument.c_str(), name.c_str(), - pcAttach->MapReversed.getValue() ? "True" : "False"); + FCMD_OBJ_CMD2("MapReversed = %s", obj, pcAttach->MapReversed.getValue() ? "True" : "False"); - Gui::Command::doCommand(Gui::Command::Doc,"%s.%s.Support = %s", - appDocument.c_str(), name.c_str(), - pcAttach->Support.getPyReprString().c_str()); + FCMD_OBJ_CMD2("Support = %s", obj, pcAttach->Support.getPyReprString().c_str()); - Gui::Command::doCommand(Gui::Command::Doc,"%s.%s.MapMode = '%s'", - appDocument.c_str(), name.c_str(), - AttachEngine::getModeName(eMapMode(pcAttach->MapMode.getValue())).c_str()); + FCMD_OBJ_CMD2("MapMode = '%s'", obj, AttachEngine::getModeName(eMapMode(pcAttach->MapMode.getValue())).c_str()); - Gui::Command::doCommand(Gui::Command::Doc,"%s.recompute()", appDocument.c_str()); + FCMD_OBJ_DOC_CMD(obj, "recompute()"); - Gui::Command::doCommand(Gui::Command::Gui, "%s.resetEdit()", guiDocument.c_str()); + FCMD_VOBJ_DOC_CMD(obj,"resetEdit()"); document->commitCommand(); } catch (const Base::Exception& e) { diff --git a/src/Mod/Part/Gui/TaskCheckGeometry.cpp b/src/Mod/Part/Gui/TaskCheckGeometry.cpp index 0d9a2adee481..9423a2c8337f 100644 --- a/src/Mod/Part/Gui/TaskCheckGeometry.cpp +++ b/src/Mod/Part/Gui/TaskCheckGeometry.cpp @@ -415,38 +415,25 @@ void TaskCheckGeometryResults::goCheck() { Gui::WaitCursor wc; int selectedCount(0), checkedCount(0), invalidShapes(0); - std::vector selection = Gui::Selection().getSelection(); - std::vector::iterator it; ResultEntry *theRoot = new ResultEntry(); - Handle(Message_ProgressIndicator) theProgress = new BOPProgressIndicator(tr("Check geometry"), Gui::getMainWindow()); theProgress->NewScope("BOP check..."); #if OCC_VERSION_HEX >= 0x060900 theProgress->Show(); #endif - selectedCount = static_cast(selection.size()); - for (it = selection.begin(); it != selection.end(); ++it) - { - Part::Feature *feature = dynamic_cast((*it).pObject); - if (!feature) + for(const auto &sel : Gui::Selection().getSelection()) { + selectedCount++; + TopoDS_Shape shape = Part::Feature::getShape(sel.pObject,sel.SubName,true); + if (shape.IsNull()) continue; - currentSeparator = Gui::Application::Instance->activeDocument()->getViewProvider(feature)->getRoot(); + currentSeparator = Gui::Application::Instance->getViewProvider(sel.pObject)->getRoot(); if (!currentSeparator) continue; - TopoDS_Shape shape = feature->Shape.getValue(); QString baseName; QTextStream baseStream(&baseName); - baseStream << (*it).DocName; - baseStream << "." << (*it).FeatName; - if (strlen((*it).SubName) > 0) - { - shape = feature->Shape.getShape().getSubShape((*it).SubName); - baseStream << "." << (*it).SubName; - } - - if (shape.IsNull()) - continue; + baseStream << sel.DocName; + baseStream << "." << sel.FeatName; checkedCount++; checkedMap.Clear(); @@ -483,7 +470,7 @@ void TaskCheckGeometryResults::goCheck() group->SetBool("RunBOPCheck", runSignal); if (runSignal) { std::string label = "Checking "; - label += feature->Label.getStrValue(); + label += sel.pObject->Label.getStrValue(); label += "..."; theProgress->NewScope(label.c_str()); invalidShapes += goBOPSingleCheck(shape, theRoot, baseName, theProgress); diff --git a/src/Mod/Part/Gui/TaskDimension.cpp b/src/Mod/Part/Gui/TaskDimension.cpp index 7e962d19da19..c67e4a7959f4 100644 --- a/src/Mod/Part/Gui/TaskDimension.cpp +++ b/src/Mod/Part/Gui/TaskDimension.cpp @@ -78,7 +78,30 @@ #include "TaskDimension.h" -bool PartGui::getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub) +static bool _MeasureInfoInited; + +static void slotDeleteDocument(const App::Document &doc); + +struct MeasureInfo { + PartGui::DimSelections sel1; + PartGui::DimSelections sel2; + bool linear; + MeasureInfo(const PartGui::DimSelections &sel1, const PartGui::DimSelections &sel2, bool linear) + :sel1(sel1),sel2(sel2),linear(linear) + { + if(!_MeasureInfoInited) { + _MeasureInfoInited = true; + App::GetApplication().signalDeleteDocument.connect(boost::bind(slotDeleteDocument, _1)); + } + } +}; +static std::map > _Measures; + +static void slotDeleteDocument(const App::Document &doc) { + _Measures.erase(doc.getName()); +} + +bool PartGui::getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub, Base::Matrix4D *mat) { App::Document *docPointer = App::GetApplication().getDocument(doc.c_str()); if (!docPointer) @@ -86,15 +109,7 @@ bool PartGui::getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc App::DocumentObject *objectPointer = docPointer->getObject(object.c_str()); if (!objectPointer) return false; - Part::Feature *feature = dynamic_cast(objectPointer); - if (!feature) - return false; - Base::Placement placement = feature->globalPlacement(); - Part::TopoShape topoShape = feature->Shape.getShape(); - topoShape.setPlacement(placement); - shapeOut = topoShape.getShape(); - if (sub.size() > 0) - shapeOut = topoShape.getSubShape(sub.c_str()); + shapeOut = Part::Feature::getShape(objectPointer,sub.c_str(),true,mat); if (shapeOut.IsNull()) return false; return true; @@ -102,23 +117,26 @@ bool PartGui::getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc bool PartGui::evaluateLinearPreSelection(TopoDS_Shape &shape1, TopoDS_Shape &shape2) { - std::vector selections = Gui::Selection().getSelection(); + std::vector selections = Gui::Selection().getSelection(0,false); if (selections.size() != 2) return false; std::vector::iterator it; std::vector shapes; - + DimSelections sels[2]; + + int i=0; for (it = selections.begin(); it != selections.end(); ++it) { - Part::Feature *feature = dynamic_cast((*it).pObject); - if (!feature) - break; - TopoDS_Shape shape = feature->Shape.getValue(); - if (strlen((*it).SubName) > 0) - shape = feature->Shape.getShape().getSubShape((*it).SubName); + TopoDS_Shape shape = Part::Feature::getShape(it->pObject,it->SubName,true); if (shape.IsNull()) break; shapes.push_back(shape); + sels[i].selections.push_back(DimSelections::DimSelection()); + auto &sel = sels[i].selections[0]; + ++i; + sel.documentName = it->DocName; + sel.objectName = it->FeatName; + sel.subObjectName = it->SubName; } if (shapes.size() != 2) @@ -126,7 +144,10 @@ bool PartGui::evaluateLinearPreSelection(TopoDS_Shape &shape1, TopoDS_Shape &sha shape1 = shapes.front(); shape2 = shapes.back(); - + + auto doc = App::GetApplication().getActiveDocument(); + if(doc) + _Measures[doc->getName()].emplace_back(sels[0],sels[1],true); return true; } @@ -266,6 +287,7 @@ void PartGui::eraseAllDimensions() Gui::Document *doc = Gui::Application::Instance->activeDocument(); if (!doc) return; + _Measures.erase(doc->getDocument()->getName()); Gui::View3DInventor *view = dynamic_cast(doc->getActiveView()); if (!view) return; @@ -275,6 +297,24 @@ void PartGui::eraseAllDimensions() viewer->eraseAllDimensions(); } +void PartGui::refreshDimensions() { + auto doc = App::GetApplication().getActiveDocument(); + if(!doc) + return; + auto it = _Measures.find(doc->getName()); + if(it == _Measures.end()) + return; + std::list measures; + measures.swap(it->second); + eraseAllDimensions(); + for(auto &info : measures) { + if(info.linear) + PartGui::TaskMeasureLinear::buildDimension(info.sel1,info.sel2); + else + PartGui::TaskMeasureAngular::buildDimension(info.sel1,info.sel2); + } +} + void PartGui::toggle3d() { ParameterGrp::handle group = App::GetApplication().GetUserParameter(). @@ -470,7 +510,9 @@ void PartGui::DimensionLinear::setupDimension() textSep->addChild(rTrans); } -PartGui::TaskMeasureLinear::TaskMeasureLinear(): selections1(), selections2(), buttonSelectedIndex(0) +PartGui::TaskMeasureLinear::TaskMeasureLinear() + : Gui::SelectionObserver(true,false) + , selections1(), selections2(), buttonSelectedIndex(0) { setUpGui(); } @@ -535,14 +577,18 @@ void PartGui::TaskMeasureLinear::selectionClearDelayedSlot() this->blockConnection(false); } -void PartGui::TaskMeasureLinear::buildDimension() +void PartGui::TaskMeasureLinear::buildDimension() { + buildDimension(selections1,selections2); +} + +void PartGui::TaskMeasureLinear::buildDimension(const DimSelections &sel1, const DimSelections &sel2) { - if(selections1.selections.size() != 1 || selections2.selections.size() != 1) + if(sel1.selections.size() != 1 || sel2.selections.size() != 1) return; - - DimSelections::DimSelection current1 = selections1.selections.at(0); - DimSelections::DimSelection current2 = selections2.selections.at(0); - + + DimSelections::DimSelection current1 = sel1.selections.at(0); + DimSelections::DimSelection current2 = sel2.selections.at(0); + TopoDS_Shape shape1, shape2; if (!getShapeFromStrings(shape1, current1.documentName, current1.objectName, current1.subObjectName)) { @@ -554,6 +600,9 @@ void PartGui::TaskMeasureLinear::buildDimension() Base::Console().Message("\nFailed to get shape\n\n"); return; } + auto doc = App::GetApplication().getActiveDocument(); + if(doc) + _Measures[doc->getName()].emplace_back(sel1,sel2,true); goDimensionLinearNoTask(shape1, shape2); } @@ -771,25 +820,41 @@ void PartGui::goDimensionAngularRoot() bool PartGui::evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapter &vector2Out) { - std::vector selections = Gui::Selection().getSelection(); + std::vector selections = Gui::Selection().getSelection(0,false); if (selections.size() > 4 || selections.size() < 2) return false; std::vector::iterator it; std::vector adapters; + std::vector sels; TopoDS_Vertex lastVertex; for (it = selections.begin(); it != selections.end(); ++it) { - Part::Feature *feature = dynamic_cast((*it).pObject); - if (!feature) - break; - TopoDS_Shape shape = feature->Shape.getValue(); - if (strlen((*it).SubName) > 0) - shape = feature->Shape.getShape().getSubShape((*it).SubName); + Base::Matrix4D mat; + TopoDS_Shape shape = Part::Feature::getShape(it->pObject,it->SubName,true,&mat); if (shape.IsNull()) break; - + mat.inverse(); + if (shape.ShapeType() == TopAbs_VERTEX) { + if(sels.empty() || + sels.back().selections.back().shapeType!=DimSelections::Vertex || + sels.back().selections.size()==1) + { + sels.push_back(PartGui::DimSelections()); + } + sels.back().selections.push_back(DimSelections::DimSelection()); + auto &sel = sels.back().selections.back(); + sel.documentName = it->DocName; + sel.objectName = it->FeatName; + sel.subObjectName = it->SubName; + sel.shapeType = DimSelections::Vertex; + Base::Vector3d v(it->x,it->y,it->z); + v = mat*v; + sel.x = v.x; + sel.y = v.y; + sel.z = v.z; + TopoDS_Vertex currentVertex = TopoDS::Vertex(shape); if (!lastVertex.IsNull()) { @@ -815,8 +880,21 @@ bool PartGui::evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapt continue; } + sels.push_back(PartGui::DimSelections()); + sels.back().selections.push_back(DimSelections::DimSelection()); + auto &sel = sels.back().selections.back(); + sel.documentName = it->DocName; + sel.objectName = it->FeatName; + sel.subObjectName = it->SubName; + Base::Vector3d v(it->x,it->y,it->z); + v = mat*v; + sel.x = v.x; + sel.y = v.y; + sel.z = v.z; + if (shape.ShapeType() == TopAbs_EDGE) { + sel.shapeType = DimSelections::Edge; TopoDS_Edge edge = TopoDS::Edge(shape); // make edge orientation so that end of edge closest to pick is head of vector. gp_Vec firstPoint = PartGui::convert(TopExp::FirstVertex(edge, Standard_True)); @@ -836,6 +914,7 @@ bool PartGui::evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapt if (shape.ShapeType() == TopAbs_FACE) { + sel.shapeType = DimSelections::Face; TopoDS_Face face = TopoDS::Face(shape); adapters.push_back(VectorAdapter(face, pickPoint)); continue; @@ -857,6 +936,9 @@ bool PartGui::evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapt return false; } + auto doc = App::GetApplication().getActiveDocument(); + if(doc) + _Measures[doc->getName()].emplace_back(sels[0],sels[1],false); return true; } @@ -1401,7 +1483,9 @@ void PartGui::DimensionControl::clearAllSlot(bool) PartGui::eraseAllDimensions(); } -PartGui::TaskMeasureAngular::TaskMeasureAngular(): selections1(), selections2(), buttonSelectedIndex(0) +PartGui::TaskMeasureAngular::TaskMeasureAngular() + : Gui::SelectionObserver(true,false) + , selections1(), selections2(), buttonSelectedIndex(0) { setUpGui(); } @@ -1414,16 +1498,21 @@ PartGui::TaskMeasureAngular::~TaskMeasureAngular() void PartGui::TaskMeasureAngular::onSelectionChanged(const Gui::SelectionChanges& msg) { TopoDS_Shape shape; - if (!getShapeFromStrings(shape, std::string(msg.pDocName), std::string(msg.pObjectName), std::string(msg.pSubName))) + Base::Matrix4D mat; + if (!getShapeFromStrings(shape, std::string(msg.pDocName), + std::string(msg.pObjectName), std::string(msg.pSubName),&mat)) return; + mat.inverse(); DimSelections::DimSelection newSelection; newSelection.documentName = msg.pDocName; newSelection.objectName = msg.pObjectName; newSelection.subObjectName = msg.pSubName; - newSelection.x = msg.x; - newSelection.y = msg.y; - newSelection.z = msg.z; gp_Vec pickPoint(msg.x, msg.y, msg.z); + Base::Vector3d v(msg.x,msg.y,msg.z); + v = mat*v; + newSelection.x = v.x; + newSelection.y = v.y; + newSelection.z = v.z; if (buttonSelectedIndex == 0) { if (msg.Type == Gui::SelectionChanges::AddSelection) @@ -1544,8 +1633,9 @@ void PartGui::TaskMeasureAngular::selectionClearDelayedSlot() this->blockConnection(false); } -PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui::DimSelections& selection) const +PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui::DimSelections& selection) { + Base::Matrix4D mat; assert(selection.selections.size() > 0 && selection.selections.size() < 3); if (selection.selections.size() == 1) { @@ -1553,7 +1643,7 @@ PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui:: if (current.shapeType == DimSelections::Edge) { TopoDS_Shape edgeShape; - if (!getShapeFromStrings(edgeShape, current.documentName, current.objectName, current.subObjectName)) + if (!getShapeFromStrings(edgeShape, current.documentName, current.objectName, current.subObjectName,&mat)) return VectorAdapter(); TopoDS_Edge edge = TopoDS::Edge(edgeShape); // make edge orientation so that end of edge closest to pick is head of vector. @@ -1563,7 +1653,9 @@ PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui:: return VectorAdapter(); gp_Vec firstPoint = PartGui::convert(firstVertex); gp_Vec lastPoint = PartGui::convert(lastVertex); - gp_Vec pickPoint(current.x, current.y, current.z); + Base::Vector3d v(current.x,current.y,current.z); + v = mat*v; + gp_Vec pickPoint(v.x, v.y, v.z); double firstDistance = (firstPoint - pickPoint).Magnitude(); double lastDistance = (lastPoint - pickPoint).Magnitude(); if (lastDistance > firstDistance) @@ -1578,11 +1670,13 @@ PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui:: if (current.shapeType == DimSelections::Face) { TopoDS_Shape faceShape; - if (!getShapeFromStrings(faceShape, current.documentName, current.objectName, current.subObjectName)) + if (!getShapeFromStrings(faceShape, current.documentName, current.objectName, current.subObjectName,&mat)) return VectorAdapter(); TopoDS_Face face = TopoDS::Face(faceShape); - gp_Vec pickPoint(current.x, current.y, current.z); + Base::Vector3d v(current.x,current.y,current.z); + v = mat*v; + gp_Vec pickPoint(v.x, v.y, v.z); return VectorAdapter(face, pickPoint); } } @@ -1604,17 +1698,24 @@ PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui:: return VectorAdapter(PartGui::convert(vertex2), PartGui::convert(vertex1)); } -void PartGui::TaskMeasureAngular::buildDimension() +void PartGui::TaskMeasureAngular::buildDimension() { + buildDimension(selections1,selections2); +} + +void PartGui::TaskMeasureAngular::buildDimension(const DimSelections &sel1, const DimSelections &sel2) { //build adapters. - VectorAdapter adapt1 = buildAdapter(selections1); - VectorAdapter adapt2 = buildAdapter(selections2); - + VectorAdapter adapt1 = buildAdapter(sel1); + VectorAdapter adapt2 = buildAdapter(sel2); + if (!adapt1.isValid() || !adapt2.isValid()) { Base::Console().Message("\ncouldn't build adapter\n\n"); return; } + auto doc = App::GetApplication().getActiveDocument(); + if(doc) + _Measures[doc->getName()].emplace_back(sel1,sel2,false); goDimensionAngularNoTask(adapt1, adapt2); } diff --git a/src/Mod/Part/Gui/TaskDimension.h b/src/Mod/Part/Gui/TaskDimension.h index c994e60c4b96..1c3ef840ccd2 100644 --- a/src/Mod/Part/Gui/TaskDimension.h +++ b/src/Mod/Part/Gui/TaskDimension.h @@ -38,6 +38,7 @@ #include #include +#include class TopoDS_Shape; class TopoDS_Face; @@ -61,7 +62,7 @@ namespace PartGui * @param sub sub-object name to search. * @return signal if the search was successful. */ - bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub); + bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub, Base::Matrix4D *mat=0); /*!examine pre selection * @param shape1 first shape in current selection * @param shape2 second shape in current selection @@ -95,6 +96,8 @@ namespace PartGui SoNode* createLinearDimension(const gp_Pnt &point1, const gp_Pnt &point2, const SbColor &color); /*!erases all the dimensions in the viewer.*/ void eraseAllDimensions(); + /*!refresh all the dimensions in the viewer.*/ + void refreshDimensions(); /*!toggles the display status of the 3d dimensions*/ void toggle3d(); /*!toggles the display status of the delta dimensions*/ @@ -259,6 +262,9 @@ protected Q_SLOTS: void clearAllSlot(bool); void selectionClearDelayedSlot(); +public: + static void buildDimension(const DimSelections &sel1, const DimSelections &sel2); + private: void setUpGui(); void buildDimension(); @@ -335,15 +341,18 @@ protected Q_SLOTS: void clearAllSlot(bool); void selectionClearDelayedSlot(); +public: + static void buildDimension(const DimSelections &sel1, const DimSelections &sel2); + private: - void setUpGui(); void buildDimension(); + void setUpGui(); void clearSelection(); DimSelections selections1; DimSelections selections2; uint buttonSelectedIndex; SteppedSelection *stepped; - VectorAdapter buildAdapter(const DimSelections &selection) const; + static VectorAdapter buildAdapter(const DimSelections &selection); }; /*!start of the measure angular command*/ diff --git a/src/Mod/Part/Gui/TaskOffset.cpp b/src/Mod/Part/Gui/TaskOffset.cpp index 8530ce5ab38c..a9115b3159fe 100644 --- a/src/Mod/Part/Gui/TaskOffset.cpp +++ b/src/Mod/Part/Gui/TaskOffset.cpp @@ -178,21 +178,14 @@ void OffsetWidget::on_updateView_toggled(bool on) bool OffsetWidget::accept() { - std::string name = d->offset->getNameInDocument(); - try { double offsetValue = d->ui.spinOffset->value().getValue(); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Value = %f", - name.c_str(),offsetValue); + FCMD_OBJ_CMD2("Value = %f", d->offset,offsetValue); d->ui.spinOffset->apply(); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Mode = %i", - name.c_str(),d->ui.modeType->currentIndex()); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Join = %i", - name.c_str(),d->ui.joinType->currentIndex()); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Intersection = %s", - name.c_str(),d->ui.intersection->isChecked() ? "True" : "False"); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.SelfIntersection = %s", - name.c_str(),d->ui.selfIntersection->isChecked() ? "True" : "False"); + FCMD_OBJ_CMD2("Mode = %i", d->offset,d->ui.modeType->currentIndex()); + FCMD_OBJ_CMD2("Join = %i", d->offset,d->ui.joinType->currentIndex()); + FCMD_OBJ_CMD2("Intersection = %s", d->offset,d->ui.intersection->isChecked() ? "True" : "False"); + FCMD_OBJ_CMD2("SelfIntersection = %s", d->offset,d->ui.selfIntersection->isChecked() ? "True" : "False"); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); if (!d->offset->isValid()) diff --git a/src/Mod/Part/Gui/TaskThickness.cpp b/src/Mod/Part/Gui/TaskThickness.cpp index f907687fddef..18982e373c1b 100644 --- a/src/Mod/Part/Gui/TaskThickness.cpp +++ b/src/Mod/Part/Gui/TaskThickness.cpp @@ -225,22 +225,17 @@ bool ThicknessWidget::accept() if (d->loop.isRunning()) return false; - std::string name = d->thickness->getNameInDocument(); try { if (!d->selection.empty()) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Faces = %s", - name.c_str(),d->selection.c_str()); + FCMD_OBJ_CMD2("Faces = %s", d->thickness,d->selection.c_str()); } - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Value = %f", - name.c_str(),d->ui.spinOffset->value().getValue()); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Mode = %i", - name.c_str(),d->ui.modeType->currentIndex()); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Join = %i", - name.c_str(),d->ui.joinType->currentIndex()); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Intersection = %s", - name.c_str(),d->ui.intersection->isChecked() ? "True" : "False"); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.SelfIntersection = %s", - name.c_str(),d->ui.selfIntersection->isChecked() ? "True" : "False"); + FCMD_OBJ_CMD2("Value = %f", d->thickness,d->ui.spinOffset->value().getValue()); + FCMD_OBJ_CMD2("Mode = %i", d->thickness,d->ui.modeType->currentIndex()); + FCMD_OBJ_CMD2("Join = %i", d->thickness,d->ui.joinType->currentIndex()); + FCMD_OBJ_CMD2("Intersection = %s", + d->thickness,d->ui.intersection->isChecked() ? "True" : "False"); + FCMD_OBJ_CMD2("SelfIntersection = %s", + d->thickness,d->ui.selfIntersection->isChecked() ? "True" : "False"); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); if (!d->thickness->isValid()) diff --git a/src/Mod/Part/Gui/ViewProvider.cpp b/src/Mod/Part/Gui/ViewProvider.cpp index ee38999abef1..9f3531e1a261 100644 --- a/src/Mod/Part/Gui/ViewProvider.cpp +++ b/src/Mod/Part/Gui/ViewProvider.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -54,8 +55,7 @@ bool ViewProviderPart::doubleClicked(void) Msg += this->pcObject->Label.getValue(); try { Gui::Command::openCommand(Msg.c_str()); - Gui::Command::doCommand(Gui::Command::Gui,"Gui.ActiveDocument.setEdit('%s',0)", - this->pcObject->getNameInDocument()); + FCMD_SET_EDIT(pcObject); return true; } catch (const Base::Exception& e) { diff --git a/src/Mod/Part/Gui/ViewProvider2DObject.cpp b/src/Mod/Part/Gui/ViewProvider2DObject.cpp index d303939cfd94..b4c075c44925 100644 --- a/src/Mod/Part/Gui/ViewProvider2DObject.cpp +++ b/src/Mod/Part/Gui/ViewProvider2DObject.cpp @@ -34,6 +34,7 @@ # include # include # include +# include # include #endif @@ -69,7 +70,7 @@ ViewProvider2DObject::ViewProvider2DObject() ADD_PROPERTY_TYPE(TightGrid,(true),"Grid",(App::PropertyType)(App::Prop_None),"Switch the tight grid mode on/off"); ADD_PROPERTY_TYPE(GridSnap,(false),"Grid",(App::PropertyType)(App::Prop_None),"Switch the grid snap on/off"); - GridRoot = new SoSeparator(); + GridRoot = new SoAnnotation(); GridRoot->ref(); GridRoot->setName("GridRoot"); MinX = MinY = -100; @@ -150,9 +151,18 @@ SoSeparator* ViewProvider2DObject::createGrid(void) carpet->vertexProperty = vts; parent->addChild(carpet);*/ + // It seems that SoDepthBuffer will mess up with other object's + // pre-selection highlight. No idea why the setting can leak out of a + // separator. + // + // What's the purpose of using SoDepthBuffer here anyway? If the intension + // is to render grid always on top, shouldn't it be better to use + // SoAnnotation? +#if 0 SoDepthBuffer *depth = new SoDepthBuffer; depth->function = SoDepthBuffer::ALWAYS; parent->addChild(depth); +#endif // gridlines mycolor = new SoBaseColor; diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index 2782326343b9..9f605a66afcc 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -88,8 +88,10 @@ void ViewProviderBoolean::updateData(const App::Property* prop) Part::Boolean* objBool = dynamic_cast(getObject()); if (!objBool) return; - Part::Feature* objBase = dynamic_cast(objBool->Base.getValue()); - Part::Feature* objTool = dynamic_cast(objBool->Tool.getValue()); + Part::Feature* objBase = dynamic_cast( + Part::Feature::getShapeOwner(objBool->Base.getValue())); + Part::Feature* objTool = dynamic_cast( + Part::Feature::getShapeOwner(objBool->Tool.getValue())); if (objBase && objTool) { const TopoDS_Shape& baseShape = objBase->Shape.getValue(); const TopoDS_Shape& toolShape = objTool->Shape.getValue(); @@ -152,18 +154,6 @@ bool ViewProviderBoolean::onDelete(const std::vector &) return true; } -void ViewProviderBoolean::replaceObject(App::DocumentObject* oldValue, App::DocumentObject* newValue) -{ - Part::Boolean* pBool = static_cast(getObject()); - if (oldValue == pBool->Base.getValue()) { - pBool->Base.setValue(newValue); - } - else if (oldValue == pBool->Tool.getValue()) { - pBool->Tool.setValue(newValue); - } -} - - PROPERTY_SOURCE(PartGui::ViewProviderMultiFuse,PartGui::ViewProviderPart) ViewProviderMultiFuse::ViewProviderMultiFuse() @@ -204,7 +194,7 @@ void ViewProviderMultiFuse::updateData(const App::Property* prop) int index=0; for (std::vector::iterator it = sources.begin(); it != sources.end(); ++it, ++index) { - Part::Feature* objBase = dynamic_cast(*it); + Part::Feature* objBase = dynamic_cast(Part::Feature::getShapeOwner(*it)); if (!objBase) continue; const TopoDS_Shape& baseShape = objBase->Shape.getValue(); @@ -257,7 +247,9 @@ bool ViewProviderMultiFuse::canDragObjects() const bool ViewProviderMultiFuse::canDragObject(App::DocumentObject* obj) const { - return obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()); + (void)obj; + // return Part::Feature::hasShapeOwner(obj); + return true; } void ViewProviderMultiFuse::dragObject(App::DocumentObject* obj) @@ -280,7 +272,9 @@ bool ViewProviderMultiFuse::canDropObjects() const bool ViewProviderMultiFuse::canDropObject(App::DocumentObject* obj) const { - return obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()); + (void)obj; + // return Part::Feature::hasShapeOwner(obj); + return true; } void ViewProviderMultiFuse::dropObject(App::DocumentObject* obj) @@ -291,14 +285,6 @@ void ViewProviderMultiFuse::dropObject(App::DocumentObject* obj) pBool->Shapes.setValues(pShapes); } -void ViewProviderMultiFuse::replaceObject(App::DocumentObject* oldValue, App::DocumentObject* newValue) -{ - Part::MultiFuse* pBool = static_cast(getObject()); - std::vector pShapes = pBool->Shapes.getValues(); - std::replace(pShapes.begin(), pShapes.end(), oldValue, newValue); - pBool->Shapes.setValues(pShapes); -} - PROPERTY_SOURCE(PartGui::ViewProviderMultiCommon,PartGui::ViewProviderPart) ViewProviderMultiCommon::ViewProviderMultiCommon() @@ -339,7 +325,7 @@ void ViewProviderMultiCommon::updateData(const App::Property* prop) int index=0; for (std::vector::iterator it = sources.begin(); it != sources.end(); ++it, ++index) { - Part::Feature* objBase = dynamic_cast(*it); + Part::Feature* objBase = dynamic_cast(Part::Feature::getShapeOwner(*it)); if (!objBase) continue; const TopoDS_Shape& baseShape = objBase->Shape.getValue(); @@ -392,7 +378,9 @@ bool ViewProviderMultiCommon::canDragObjects() const bool ViewProviderMultiCommon::canDragObject(App::DocumentObject* obj) const { - return obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()); + (void)obj; + // return Part::Feature::hasShapeOwner(obj); + return true; } void ViewProviderMultiCommon::dragObject(App::DocumentObject* obj) @@ -415,7 +403,9 @@ bool ViewProviderMultiCommon::canDropObjects() const bool ViewProviderMultiCommon::canDropObject(App::DocumentObject* obj) const { - return obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()); + (void)obj; + // return Part::Feature::hasShapeOwner(obj); + return true; } void ViewProviderMultiCommon::dropObject(App::DocumentObject* obj) @@ -426,10 +416,3 @@ void ViewProviderMultiCommon::dropObject(App::DocumentObject* obj) pBool->Shapes.setValues(pShapes); } -void ViewProviderMultiCommon::replaceObject(App::DocumentObject* oldValue, App::DocumentObject* newValue) -{ - Part::MultiFuse* pBool = static_cast(getObject()); - std::vector pShapes = pBool->Shapes.getValues(); - std::replace(pShapes.begin(), pShapes.end(), oldValue, newValue); - pBool->Shapes.setValues(pShapes); -} diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.h b/src/Mod/Part/Gui/ViewProviderBoolean.h index c9abc062636a..3c379309abab 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.h +++ b/src/Mod/Part/Gui/ViewProviderBoolean.h @@ -44,7 +44,6 @@ class PartGuiExport ViewProviderBoolean : public ViewProviderPart QIcon getIcon(void) const; void updateData(const App::Property*); bool onDelete(const std::vector &); - virtual void replaceObject(App::DocumentObject*, App::DocumentObject*); }; /// ViewProvider for the MultiFuse feature @@ -71,8 +70,6 @@ class PartGuiExport ViewProviderMultiFuse : public ViewProviderPart bool canDropObjects() const; bool canDropObject(App::DocumentObject*) const; void dropObject(App::DocumentObject*); - /** Replace an object to the view provider by drag and drop */ - virtual void replaceObject(App::DocumentObject*, App::DocumentObject*); }; /// ViewProvider for the MultiFuse feature @@ -99,8 +96,6 @@ class PartGuiExport ViewProviderMultiCommon : public ViewProviderPart bool canDropObjects() const; bool canDropObject(App::DocumentObject*) const; void dropObject(App::DocumentObject*); - /** Replace an object to the view provider by drag and drop */ - virtual void replaceObject(App::DocumentObject*, App::DocumentObject*); }; diff --git a/src/Mod/Part/Gui/ViewProviderCompound.cpp b/src/Mod/Part/Gui/ViewProviderCompound.cpp index 30fa8f27ba11..0aa42710d0ce 100644 --- a/src/Mod/Part/Gui/ViewProviderCompound.cpp +++ b/src/Mod/Part/Gui/ViewProviderCompound.cpp @@ -102,7 +102,7 @@ void ViewProviderCompound::updateData(const App::Property* prop) int index=0; for (std::vector::iterator it = sources.begin(); it != sources.end(); ++it, ++index) { - Part::Feature* objBase = dynamic_cast(*it); + Part::Feature* objBase = dynamic_cast(Part::Feature::getShapeOwner(*it)); if (!objBase) continue; @@ -174,10 +174,3 @@ void ViewProviderCompound::dropObject(App::DocumentObject* obj) pComp->Links.setValues(pShapes); } -void ViewProviderCompound::replaceObject(App::DocumentObject* oldValue, App::DocumentObject* newValue) -{ - Part::Compound* pBool = static_cast(getObject()); - std::vector pShapes = pBool->Links.getValues(); - std::replace(pShapes.begin(), pShapes.end(), oldValue, newValue); - pBool->Links.setValues(pShapes); -} diff --git a/src/Mod/Part/Gui/ViewProviderCompound.h b/src/Mod/Part/Gui/ViewProviderCompound.h index 0ea8ff955a10..e72b9b278bed 100644 --- a/src/Mod/Part/Gui/ViewProviderCompound.h +++ b/src/Mod/Part/Gui/ViewProviderCompound.h @@ -48,8 +48,6 @@ class PartGuiExport ViewProviderCompound : public ViewProviderPart bool canDropObjects() const; bool canDropObject(App::DocumentObject*) const; void dropObject(App::DocumentObject*); - /** Replace an object to the view provider by drag and drop */ - virtual void replaceObject(App::DocumentObject*, App::DocumentObject*); protected: void updateData(const App::Property*); diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 887a0ef1172f..7d17b6a1836d 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -112,7 +112,9 @@ #include #include #include +#include +#include #include "ViewProviderExt.h" #include "SoBrepPointSet.h" #include "SoBrepEdgeSet.h" @@ -122,6 +124,7 @@ #include #include +FC_LOG_LEVEL_INIT("Part", true, true); using namespace PartGui; @@ -228,14 +231,13 @@ const char* ViewProviderPartExt::DrawStyleEnums[]= {"Solid","Dashed","Dotted","D ViewProviderPartExt::ViewProviderPartExt() { VisualTouched = true; + forceUpdateCount = 0; NormalsFromUV = true; - ParameterGrp::handle hView = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - - unsigned long lcol = hView->GetUnsigned("DefaultShapeLineColor",421075455UL); // dark grey (25,25,25) + unsigned long lcol = Gui::ViewParams::instance()->getDefaultShapeLineColor(); // dark grey (25,25,25) float r,g,b; r = ((lcol >> 24) & 0xff) / 255.0; g = ((lcol >> 16) & 0xff) / 255.0; b = ((lcol >> 8) & 0xff) / 255.0; - int lwidth = hView->GetInt("DefaultShapeLineWidth",2); + int lwidth = Gui::ViewParams::instance()->getDefaultShapeLineWidth(); ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/Mod/Part"); @@ -346,16 +348,15 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) // The lower limit of the deviation has been increased to avoid // to freeze the GUI // https://forum.freecadweb.org/viewtopic.php?f=3&t=24912&p=195613 - Part::Feature* feature = dynamic_cast(pcObject); if (prop == &Deviation) { - if(Visibility.getValue() && feature && !feature->Shape.getValue().IsNull()) - updateVisual(feature->Shape.getValue()); + if(isUpdateForced()||Visibility.getValue()) + updateVisual(); else VisualTouched = true; } if (prop == &AngularDeflection) { - if(Visibility.getValue() && feature && !feature->Shape.getValue().IsNull()) - updateVisual(feature->Shape.getValue()); + if(isUpdateForced()||Visibility.getValue()) + updateVisual(); else VisualTouched = true; } @@ -456,8 +457,8 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) } else { // if the object was invisible and has been changed, recreate the visual - if (prop == &Visibility && Visibility.getValue() && VisualTouched) { - updateVisual(feature->Shape.getValue()); + if (prop == &Visibility && (isUpdateForced() || Visibility.getValue()) && VisualTouched) { + updateVisual(); // The material has to be checked again (#0001736) onChanged(&DiffuseColor); } @@ -472,10 +473,19 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) ViewProviderGeometryObject::attach(pcFeat); // Workaround for #0000433, i.e. use SoSeparator instead of SoGroup - SoGroup* pcNormalRoot = new SoSeparator(); - SoGroup* pcFlatRoot = new SoSeparator(); - SoGroup* pcWireframeRoot = new SoSeparator(); - SoGroup* pcPointsRoot = new SoSeparator(); + auto* pcNormalRoot = new SoSeparator(); + auto* pcFlatRoot = new SoSeparator(); + auto* pcWireframeRoot = new SoSeparator(); + auto* pcPointsRoot = new SoSeparator(); + auto* wireframe = new SoSeparator(); + + // Must turn off all intermediate render caching, and let pcRoot to handle + // cache without interference. + pcNormalRoot->renderCaching = + pcFlatRoot->renderCaching = + pcWireframeRoot->renderCaching = + pcPointsRoot->renderCaching = + wireframe->renderCaching = SoSeparator::OFF; // enable two-side rendering pShapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; @@ -486,7 +496,6 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) SoPolygonOffset* offset = new SoPolygonOffset(); // wireframe node - SoSeparator* wireframe = new SoSeparator(); wireframe->setName("Edge"); wireframe->addChild(pcLineBind); wireframe->addChild(pcLineMaterial); @@ -494,10 +503,10 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) wireframe->addChild(lineset); // normal viewing with edges and points + pcNormalRoot->addChild(pcPointsRoot); pcNormalRoot->addChild(wireframe); pcNormalRoot->addChild(offset); pcNormalRoot->addChild(pcFlatRoot); - pcNormalRoot->addChild(pcPointsRoot); // just faces with no edges or points pcFlatRoot->addChild(pShapeHints); @@ -616,7 +625,7 @@ std::vector ViewProviderPartExt::getModelPoints(const SoPickedPo try { std::vector pts; std::string element = this->getElement(pp->getDetail()); - const Part::TopoShape& shape = static_cast(getObject())->Shape.getShape(); + const auto &shape = Part::Feature::getTopoShape(getObject()); TopoDS_Shape subShape = shape.getSubShape(element.c_str()); @@ -663,6 +672,9 @@ std::vector ViewProviderPartExt::getSelectionShape(const char* / void ViewProviderPartExt::setHighlightedFaces(const std::vector& colors) { + Gui::SoUpdateVBOAction action; + action.apply(this->faceset); + int size = static_cast(colors.size()); if (size > 1 && size == this->faceset->partIndex.getNum()) { pcFaceBind->value = SoMaterialBinding::PER_PART; @@ -766,7 +778,7 @@ void ViewProviderPartExt::setHighlightedPoints(const std::vector& co { int size = static_cast(colors.size()); if (size > 1) { -#ifdef FC_DEBUG +#if 0 int numPoints = coords->point.getNum() - nodeset->startIndex.getValue(); if (numPoints != size) { SoDebugError::postWarning("ViewProviderPartExt::setHighlightedPoints", @@ -813,22 +825,19 @@ bool ViewProviderPartExt::loadParameter() void ViewProviderPartExt::reload() { - if (loadParameter()) { - App::Property* shape = pcObject->getPropertyByName("Shape"); - if (shape) update(shape); - } + if (loadParameter()) + updateVisual(); } void ViewProviderPartExt::updateData(const App::Property* prop) { - if (prop->getTypeId() == Part::PropertyPartShape::getClassTypeId()) { - // get the shape to show - const TopoDS_Shape &cShape = static_cast(prop)->getValue(); - + const char *propName = prop?prop->getName():""; + if(propName && (strcmp(propName,"Shape")==0 || strstr(propName,"Touched")!=0)) + { // calculate the visual only if visible - if (Visibility.getValue()) - updateVisual(cShape); - else + if (isUpdateForced()||Visibility.getValue()) + updateVisual(); + else VisualTouched = true; if (!VisualTouched) { @@ -878,7 +887,7 @@ void ViewProviderPartExt::unsetEdit(int ModNum) } } -void ViewProviderPartExt::updateVisual(const TopoDS_Shape& inputShape) +void ViewProviderPartExt::updateVisual() { Gui::SoUpdateVBOAction action; action.apply(this->faceset); @@ -895,7 +904,7 @@ void ViewProviderPartExt::updateVisual(const TopoDS_Shape& inputShape) haction.apply(this->lineset); haction.apply(this->nodeset); - TopoDS_Shape cShape(inputShape); + TopoDS_Shape cShape = Part::Feature::getShape(getObject()); if (cShape.IsNull()) { coords ->point .setNum(0); norm ->vector .setNum(0); @@ -1214,7 +1223,7 @@ void ViewProviderPartExt::updateVisual(const TopoDS_Shape& inputShape) lineset ->coordIndex .finishEditing(); } catch (...) { - Base::Console().Error("Cannot compute Inventor representation for the shape of %s.\n",pcObject->getNameInDocument()); + FC_ERR("Cannot compute Inventor representation for the shape of " << pcObject->getFullName()); } # ifdef FC_DEBUG @@ -1224,3 +1233,13 @@ void ViewProviderPartExt::updateVisual(const TopoDS_Shape& inputShape) # endif VisualTouched = false; } +void ViewProviderPartExt::forceUpdate(bool enable) { + if(enable) { + if(++forceUpdateCount == 1) { + if(!isShow()) + Visibility.touch(); + } + }else if(forceUpdateCount) + --forceUpdateCount; +} + diff --git a/src/Mod/Part/Gui/ViewProviderExt.h b/src/Mod/Part/Gui/ViewProviderExt.h index 348a313356ed..dcf1a895fbe9 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.h +++ b/src/Mod/Part/Gui/ViewProviderExt.h @@ -33,6 +33,7 @@ #include #include #include +#include class TopoDS_Shape; class TopoDS_Edge; @@ -128,9 +129,15 @@ class PartGuiExport ViewProviderPartExt : public Gui::ViewProviderGeometryObject void unsetHighlightedPoints(); //@} + virtual bool isUpdateForced() const override { + return forceUpdateCount>0; + } + virtual void forceUpdate(bool enable = true) override; + /** @name Edit methods */ //@{ void setupContextMenu(QMenu*, QObject*, const char*); + protected: bool setEdit(int ModNum); void unsetEdit(int ModNum); @@ -140,7 +147,7 @@ class PartGuiExport ViewProviderPartExt : public Gui::ViewProviderGeometryObject /// get called by the container whenever a property has been changed virtual void onChanged(const App::Property* prop); bool loadParameter(); - void updateVisual(const TopoDS_Shape &); + void updateVisual(); void getNormals(const TopoDS_Face& theFace, const Handle(Poly_Triangulation)& aPolyTri, TColgp_Array1OfDir& theNormals); @@ -166,6 +173,7 @@ class PartGuiExport ViewProviderPartExt : public Gui::ViewProviderGeometryObject private: // settings stuff + int forceUpdateCount; static App::PropertyFloatConstraint::Constraints sizeRange; static App::PropertyFloatConstraint::Constraints tessRange; static App::PropertyQuantityConstraint::Constraints angDeflectionRange; diff --git a/src/Mod/Part/Gui/ViewProviderMirror.cpp b/src/Mod/Part/Gui/ViewProviderMirror.cpp index d23469783aac..35319da3a649 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.cpp +++ b/src/Mod/Part/Gui/ViewProviderMirror.cpp @@ -242,7 +242,8 @@ void ViewProviderFillet::updateData(const App::Property* prop) Part::Fillet* objFill = dynamic_cast(getObject()); if (!objFill) return; - Part::Feature* objBase = dynamic_cast(objFill->Base.getValue()); + Part::Feature* objBase = dynamic_cast( + Part::Feature::getShapeOwner(objFill->Base.getValue())); if (objBase) { const TopoDS_Shape& baseShape = objBase->Shape.getValue(); const TopoDS_Shape& fillShape = objFill->Shape.getValue(); @@ -345,7 +346,8 @@ void ViewProviderChamfer::updateData(const App::Property* prop) Part::Chamfer* objCham = dynamic_cast(getObject()); if (!objCham) return; - Part::Feature* objBase = dynamic_cast(objCham->Base.getValue()); + Part::Feature* objBase = dynamic_cast( + Part::Feature::getShapeOwner(objCham->Base.getValue())); if (objBase) { const TopoDS_Shape& baseShape = objBase->Shape.getValue(); const TopoDS_Shape& chamShape = objCham->Shape.getValue(); @@ -493,7 +495,11 @@ ViewProviderSweep::~ViewProviderSweep() std::vector ViewProviderSweep::claimChildren() const { - return static_cast(getObject())->Sections.getValues(); + auto obj = static_cast(getObject()); + auto children = obj->Sections.getValues(); + if(obj->Spine.getValue()) + children.push_back(obj->Spine.getValue()); + return children; } bool ViewProviderSweep::onDelete(const std::vector &) diff --git a/src/Mod/Part/Gui/ViewProviderPlaneParametric.cpp b/src/Mod/Part/Gui/ViewProviderPlaneParametric.cpp index 3995b90b7154..148769504c37 100644 --- a/src/Mod/Part/Gui/ViewProviderPlaneParametric.cpp +++ b/src/Mod/Part/Gui/ViewProviderPlaneParametric.cpp @@ -88,12 +88,9 @@ bool ViewProviderFace::canDragObjects() const bool ViewProviderFace::canDragObject(App::DocumentObject* obj) const { - if (!obj) - return false; - if (obj->getTypeId().isDerivedFrom(Part::Part2DObject::getClassTypeId())) - return true; - else - return false; + (void)obj; + // return Part::Feature::hasShapeOwner(obj); + return true; } void ViewProviderFace::dragObject(App::DocumentObject* obj) diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index 7ac930a6751d..5f050ca91521 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -72,6 +72,13 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Separator" << "Part_MakeTube"; + Gui::MenuItem* copy = new Gui::MenuItem; + copy->setCommand("Create a copy"); + *copy << "Part_SimpleCopy" + << "Part_TransformedCopy" + << "Part_ElementCopy" + << "Part_RefineShape"; + Gui::MenuItem* bop = new Gui::MenuItem; bop->setCommand("Boolean"); *bop << "Part_Boolean" @@ -113,8 +120,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Part_ShapeFromMesh" << "Part_MakeSolid" << "Part_ReverseShape" - << "Part_SimpleCopy" - << "Part_RefineShape" + << copy << "Part_CheckGeometry" << "Part_Defeaturing" << "Separator" @@ -144,6 +150,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *measure << "Part_Measure_Linear" << "Part_Measure_Angular" << "Separator" + << "Part_Measure_Refresh" << "Part_Measure_Clear_All" << "Part_Measure_Toggle_All" << "Part_Measure_Toggle_3d" @@ -211,6 +218,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *measure << "Part_Measure_Linear" << "Part_Measure_Angular" << "Separator" + << "Part_Measure_Refresh" << "Part_Measure_Clear_All" << "Part_Measure_Toggle_All" << "Part_Measure_Toggle_3d"