From 8b4f1210ed5a874f9fca292b68a2ef870424bb1f Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Sat, 7 May 2016 21:06:18 +0300 Subject: [PATCH] Attacher: Py: extend AttachEngine's interface * add constructor attributes: * Mode * CompleteModeList * ImplementedModes * GetModeInfo * References * SuperPlacement * Reverse * Parameter * CompleteRefTypeList methods: * getShapeType * isShapeOfType * downgradeType * getTypeRank * copy * calculateAttachedPlacement * suggestMapModes * readParametersFromFeature * writeParametersToFeature --- src/Mod/Part/App/AttachEnginePy.xml | 143 ++++++++ src/Mod/Part/App/AttachEnginePyImp.cpp | 449 +++++++++++++++++++++++++ 2 files changed, 592 insertions(+) diff --git a/src/Mod/Part/App/AttachEnginePy.xml b/src/Mod/Part/App/AttachEnginePy.xml index a38a81c2a..3c4c3edad 100644 --- a/src/Mod/Part/App/AttachEnginePy.xml +++ b/src/Mod/Part/App/AttachEnginePy.xml @@ -8,6 +8,7 @@ Include="Mod/Part/App/Attacher.h" FatherInclude="Base/BaseClassPy.h" Namespace="Attacher" + Constructor="true" Delete="true" FatherNamespace="Base"> @@ -21,5 +22,147 @@ + + + Current attachment mode. + + + + + + Current attachment mode. + + + + + + Current attachment mode. + + + + + + If True, Z axis of attached placement is flipped. X axis is flipped in addition (CS has to remain right-handed). + + + + + + Value of parameter for some curve attachment modes. Range of 0..1 spans the length of the edge (parameter value can be outside of the range for curves that allow extrapolation. + + + + + + + List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects. + + + + + + List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects. + + + + + + List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects. + + + + + + + getModeInfo(mode): returns supported reference combinations, user-friendly name, and so on. + + + + + getShapeType(shape): returns shape type as interpreted by AttachEngine. Returns a string. + + + + + isShapeOfType(type_shape, type_needed): tests if shape type, specified by type_shape (string), fits a type required by attachment mode type_needed (string). e.g. 'Circle' fits a requirement of 'Edge', and 'Curve' doesn't fit if a 'Circle' is required. + + + + + downgradeType(type): returns next more general type. E.g. downgradeType('Circle') yeilds 'Curve'. + + + + + getTypeRank(type): returns rank of shape type. Rank is how many times the type can be downgraded, before it becomes 'Any'. + + + + + copy(): returns a new instance of AttachEngine. + + + + + calculateAttachedPlacement(orig_placement): returns result of attachment, based +on current Mode, References, etc. SuperPlacment is included. + +original_placement is the previous placement of the object being attached. It +is used to preserve orientation for Translate attachment mode. For other modes, +it is ignored. + +Returns the new placement. If not attached, returns None. If attachment fails, +an exception is raised. + + + + + +suggestMapModes(): runs mode suggestion routine and returns a dictionary with +results and supplementary information. + +Keys: +'allApplicableModes': list of modes that can accept current references. Note +that it is only a check by types, and does not guarantee the modes will +actually work. + +'bestFitMode': mode that fits current references best. Note that the mode may +not be valid for the set of references; check for if 'message' is 'OK'. + +'error': error message for when 'message' is 'UnexpectedError' or +'LinkBroken'. + +'message': general result of suggestion. 'IncompatibleGeometry', 'NoModesFit': +no modes accept current set of references; 'OK': some modes do accept current +set of references (though it's not guarantted the modes will work - surrestor +only checks for correct types); 'UnexpectedError': should never happen. + +'nextRefTypeHint': what more can be added to references to reach other modes +('reachableModes' provide more extended information on this) + +'reachableModes': a dict, where key is mode, and value is a list of sequences +of references that can be added to fit that mode. + +'references_Types': a list of types of geometry linked by references (that's +the input information for suggestor, actually). + + + + + readParametersFromFeature(document_object): sets AttachEngine parameters (References, Mode, etc.) by reading out properties of AttachableObject-derived feature. + + + + + +writeParametersToFeature(document_object): updates properties of +AttachableObject-derived feature with current AttachEngine parameters +(References, Mode, etc.). + +Warning: if a feature linked by AttachEngine.References was deleted, this method +will crash FreeCAD. + + + diff --git a/src/Mod/Part/App/AttachEnginePyImp.cpp b/src/Mod/Part/App/AttachEnginePyImp.cpp index ebc2e06d9..a670b6f05 100644 --- a/src/Mod/Part/App/AttachEnginePyImp.cpp +++ b/src/Mod/Part/App/AttachEnginePyImp.cpp @@ -1,6 +1,13 @@ #include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif #include "Mod/Part/App/Attacher.h" +#include +#include +#include "AttachableObjectPy.h" +#include "TopoShapePy.h" #include "OCCError.h" @@ -16,11 +23,453 @@ std::string AttachEnginePy::representation(void) const return std::string(""); } +PyObject* AttachEnginePy::PyMake(struct _typeobject *, PyObject *, PyObject *) +{ + // create a new instance of AttachEngine3D + return new AttachEnginePy(new AttachEngine3D); +} + +// constructor method +int AttachEnginePy::PyInit(PyObject* args, PyObject* /*kwd*/) +{ + PyObject* o; + if (PyArg_ParseTuple(args, "")) { + return 0; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "O!", &(AttachEnginePy::Type), &o)) { + AttachEngine* attacher = static_cast(o)->getAttachEnginePtr(); + AttachEngine* oldAttacher = this->getAttachEnginePtr(); + this->_pcTwinPointer = attacher->copy(); + delete oldAttacher; + return 0; + } + + PyErr_Clear(); + char* typeName; + if (PyArg_ParseTuple(args, "s", &typeName)) { + Base::Type t = Base::Type::fromName(typeName); + AttachEngine* pNewAttacher = nullptr; + if (t.isDerivedFrom(AttachEngine::getClassTypeId())){ + pNewAttacher = static_cast(Base::Type::createInstanceByName(typeName)); + } + if (!pNewAttacher) { + std::stringstream errMsg; + errMsg << "Object if this type is not derived from AttachEngine: " << typeName; + PyErr_SetString(Base::BaseExceptionFreeCADError, errMsg.str().c_str()); + return -1; + } + AttachEngine* oldAttacher = this->getAttachEnginePtr(); + this->_pcTwinPointer = pNewAttacher; + delete oldAttacher; + return 0; + } + + PyErr_SetString(Base::BaseExceptionFreeCADError, "Wrong set of constructor arguments. Can be: (), ('Attacher::AttachEngine3D'), ('Attacher::AttachEnginePlane'), ('Attacher::AttachEngineLine'), ('Attacher::AttachEnginePoint'), (other_attacher_instance)."); + return -1; + +} + + Py::String AttachEnginePy::getAttacherType(void) const { return Py::String(std::string(this->getAttachEnginePtr()->getTypeId().getName())); } +/** + * @brief macro ATTACHERPY_STDCATCH_ATTR: catch for exceptions in attribute + * code (re-throws the exception converted to Py::Exception). It is a helper + * to avoid repeating the same error handling code over and over again. + */ +#define ATTACHERPY_STDCATCH_ATTR \ + catch (Standard_Failure) {\ + Handle_Standard_Failure e = Standard_Failure::Caught();\ + throw Py::Exception(Part::PartExceptionOCCError, e->GetMessageString());\ + } catch (Base::Exception &e) {\ + throw Py::Exception(Base::BaseExceptionFreeCADError, e.what());\ + } + +Py::String AttachEnginePy::getMode(void) const +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + return Py::String(attacher.getModeName(attacher.mapMode)); + } ATTACHERPY_STDCATCH_ATTR; +} + +void AttachEnginePy::setMode(Py::String arg) +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + std::string modeName = (std::string)arg; + attacher.mapMode = attacher.getModeByName(modeName); + } ATTACHERPY_STDCATCH_ATTR; +} + +Py::Object AttachEnginePy::getReferences(void) const +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + return Py::Object(attacher.references.getPyObject(),true); + } ATTACHERPY_STDCATCH_ATTR; +} + +void AttachEnginePy::setReferences(Py::Object arg) +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + attacher.references.setPyObject(arg.ptr()); + } ATTACHERPY_STDCATCH_ATTR; +} + +Py::Object AttachEnginePy::getSuperPlacement(void) const +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + return Py::Object(new Base::PlacementPy(new Base::Placement(attacher.superPlacement)),true); + } ATTACHERPY_STDCATCH_ATTR; +} + +void AttachEnginePy::setSuperPlacement(Py::Object arg) +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + if (PyObject_TypeCheck(arg.ptr(), &(Base::PlacementPy::Type))) { + const Base::PlacementPy* plmPy = static_cast(arg.ptr()); + attacher.superPlacement = *(plmPy->getPlacementPtr()); + } else { + std::string error = std::string("type must be 'Placement', not "); + error += arg.type().as_string(); + throw Py::TypeError(error); + } + } ATTACHERPY_STDCATCH_ATTR; +} + +Py::Boolean AttachEnginePy::getReverse(void) const +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + return Py::Boolean(attacher.mapReverse); + } ATTACHERPY_STDCATCH_ATTR; +} + +void AttachEnginePy::setReverse(Py::Boolean arg) +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + attacher.mapReverse = arg.isTrue(); + } ATTACHERPY_STDCATCH_ATTR; +} + +Py::Float AttachEnginePy::getParameter(void) const +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + return Py::Float(attacher.attachParameter); + } ATTACHERPY_STDCATCH_ATTR; +} + +void AttachEnginePy::setParameter(Py::Float arg) +{ + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + attacher.attachParameter = (double)arg; + } ATTACHERPY_STDCATCH_ATTR; +} + + +Py::List AttachEnginePy::getCompleteModeList(void) const +{ + try { + Py::List ret; + AttachEngine &attacher = *(this->getAttachEnginePtr()); + for(int imode = 0 ; imode < mmDummy_NumberOfModes ; imode++){ + ret.append(Py::String(attacher.getModeName(eMapMode(imode)))); + } + return ret; + } ATTACHERPY_STDCATCH_ATTR; +} + +Py::List AttachEnginePy::getCompleteRefTypeList(void) const +{ + try { + Py::List ret; + AttachEngine &attacher = *(this->getAttachEnginePtr()); + for(int irt = 0 ; irt < rtDummy_numberOfShapeTypes ; irt++){ + ret.append(Py::String(attacher.getRefTypeName(eRefType(irt)))); + } + return ret; + } ATTACHERPY_STDCATCH_ATTR; +} + +Py::List AttachEnginePy::getImplementedModes(void) const +{ + try { + Py::List ret; + AttachEngine &attacher = *(this->getAttachEnginePtr()); + for(int imode = 0 ; imode < mmDummy_NumberOfModes ; imode++){ + if(attacher.modeRefTypes[imode].size() > 0){ + ret.append(Py::String(attacher.getModeName(eMapMode(imode)))); + } + } + return ret; + } ATTACHERPY_STDCATCH_ATTR; +} + +/** + * @brief macro ATTACHERPY_STDCATCH_METH: catch for exceptions in method code + * (returns NULL if an exception is caught). It is a helper to avoid repeating + * the same error handling code over and over again. + */ +#define ATTACHERPY_STDCATCH_METH \ + catch (Standard_Failure) {\ + Handle_Standard_Failure e = Standard_Failure::Caught();\ + PyErr_SetString(Part::PartExceptionOCCError, e->GetMessageString());\ + return NULL;\ + } catch (Base::Exception &e) {\ + PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());\ + return NULL;\ + } catch (const Py::Exception &){\ + return NULL;\ + } + + +PyObject* AttachEnginePy::getModeInfo(PyObject* args) +{ + char* modeName; + if (!PyArg_ParseTuple(args, "s", &modeName)) + return 0; + + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + eMapMode mmode = attacher.getModeByName(modeName); + Py::List pyListOfCombinations; + Py::List pyCombination; + refTypeStringList &listOfCombinations = attacher.modeRefTypes.at(mmode); + for(const refTypeString &combination: listOfCombinations){ + pyCombination = Py::List(combination.size()); + for(int iref = 0 ; iref < combination.size() ; iref++){ + pyCombination[iref] = Py::String(AttachEngine::getRefTypeName(combination[iref])); + } + pyListOfCombinations.append(pyCombination); + } + Py::Dict ret; + ret["ReferenceCombinations"] = pyListOfCombinations; + ret["ModeIndex"] = Py::Int(mmode); + return Py::new_reference_to(ret); + } ATTACHERPY_STDCATCH_METH; +} + +PyObject* AttachEnginePy::getShapeType(PyObject* args) +{ + PyObject *pcObj; + if (!PyArg_ParseTuple(args, "O!", &(Part::TopoShapePy::Type), &pcObj)) + return NULL; + + try{ + TopoDS_Shape shape = static_cast(pcObj)->getTopoShapePtr()->_Shape; + eRefType rt = AttachEngine::getShapeType(shape); + return Py::new_reference_to(Py::String(AttachEngine::getRefTypeName(rt))); + } ATTACHERPY_STDCATCH_METH; +} + +PyObject* AttachEnginePy::isShapeOfType(PyObject* args) +{ + char* type_shape_str; + char* type_need_str; + if (!PyArg_ParseTuple(args, "ss", &type_shape_str, &type_need_str)) + return 0; + try { + eRefType type_shape = AttachEngine::getRefTypeByName(std::string(type_shape_str)); + eRefType type_need = AttachEngine::getRefTypeByName(std::string(type_need_str)); + bool result = AttachEngine::isShapeOfType(type_shape, type_need) > -1; + return Py::new_reference_to(Py::Boolean(result)); + } ATTACHERPY_STDCATCH_METH; +} + +PyObject* AttachEnginePy::downgradeType(PyObject* args) +{ + char* type_shape_str; + if (!PyArg_ParseTuple(args, "s", &type_shape_str)) + return 0; + try { + eRefType type_shape = AttachEngine::getRefTypeByName(std::string(type_shape_str)); + eRefType result = AttachEngine::downgradeType(type_shape); + return Py::new_reference_to(Py::String(AttachEngine::getRefTypeName(result))); + } ATTACHERPY_STDCATCH_METH; +} + +PyObject* AttachEnginePy::getTypeRank(PyObject* args) +{ + char* type_shape_str; + if (!PyArg_ParseTuple(args, "s", &type_shape_str)) + return 0; + try { + eRefType type_shape = AttachEngine::getRefTypeByName(std::string(type_shape_str)); + int result = AttachEngine::getTypeRank(type_shape); + return Py::new_reference_to(Py::Int(result)); + } ATTACHERPY_STDCATCH_METH; + +} + +PyObject* AttachEnginePy::copy(PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return 0; + + return new AttachEnginePy(this->getAttachEnginePtr()->copy()); +} + +PyObject* AttachEnginePy::calculateAttachedPlacement(PyObject* args) +{ + PyObject *pcObj; + if (!PyArg_ParseTuple(args, "O!", &(Base::PlacementPy::Type), &pcObj)) + return NULL; + try{ + const Base::Placement& plm = *(static_cast(pcObj)->getPlacementPtr()); + Base::Placement result; + try{ + result = this->getAttachEnginePtr()->calculateAttachedPlacement(plm); + } catch (ExceptionCancel) { + Py_IncRef(Py_None); + return Py_None; + } + return new Base::PlacementPy(new Base::Placement(result)); + } ATTACHERPY_STDCATCH_METH; + + return NULL; +} + +PyObject* AttachEnginePy::suggestMapModes(PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return 0; + + try { + AttachEngine &attacher = *(this->getAttachEnginePtr()); + SuggestResult sugr; + attacher.suggestMapModes(sugr); + Py::Dict result; + { //sugr.allApplicableModes + Py::List pyList; + for(eMapMode mmode: sugr.allApplicableModes){ + pyList.append(Py::String(AttachEngine::getModeName(mmode))); + } + result["allApplicableModes"] = pyList; + } + { //sugr.bestFitMode + result["bestFitMode"] = Py::String(AttachEngine::getModeName(sugr.bestFitMode)); + } + {//sugr.error + bool isError = sugr.message == SuggestResult::srUnexpectedError + || sugr.message == SuggestResult::srLinkBroken; + result["error"] = Py::String(isError ? sugr.error.what() : ""); + } + {//sugr.message + std::string msg; + switch(sugr.message){ + case SuggestResult::srIncompatibleGeometry: + msg = "IncompatibleGeometry"; + break; + case SuggestResult::srLinkBroken: + msg = "LinkBroken"; + break; + case SuggestResult::srNoModesFit: + msg = "NoModesFit"; + break; + case SuggestResult::srOK: + msg = "OK"; + break; + case SuggestResult::srUnexpectedError: + msg = "UnexpectedError"; + break; + default: + msg = ""; + } + result["message"] = Py::String(msg); + } + {//sugr.nextRefTypeHint + Py::List pyList; + for(eRefType rt : sugr.nextRefTypeHint){ + pyList.append(Py::String(AttachEngine::getRefTypeName(rt))); + } + result["nextRefTypeHint"] = pyList; + } + {//sugr.reachableModes + Py::Dict pyRM; + for(std::pair &rm: sugr.reachableModes){ + Py::List pyListOfCombinations; + for(refTypeString &rts : rm.second){ + Py::List pyCombination; + for(eRefType rt : rts){ + pyCombination.append(Py::String(AttachEngine::getRefTypeName(rt))); + } + pyListOfCombinations.append(pyCombination); + } + pyRM[AttachEngine::getModeName(rm.first)] = pyListOfCombinations; + } + result["reachableModes"] = pyRM; + } + {//sugr.references_Types + Py::List pyList; + for(eRefType rt : sugr.references_Types){ + pyList.append(Py::String(AttachEngine::getRefTypeName(rt))); + } + result["references_Types"] = pyList; + } + + return Py::new_reference_to(result); + } ATTACHERPY_STDCATCH_METH; +} + +PyObject* AttachEnginePy::readParametersFromFeature(PyObject* args) +{ + PyObject* obj; + if (!PyArg_ParseTuple(args, "O!",&(App::DocumentObjectPy::Type),&obj)) + return NULL; // NULL triggers exception + + try{ + const App::DocumentObjectPy* dobjpy = static_cast(obj); + const App::DocumentObject* dobj = dobjpy->getDocumentObjectPtr(); + if (! dobj->isDerivedFrom(Part::AttachableObject::getClassTypeId())){ + throw Py::TypeError("Supplied object isn't Part::AttachableObject"); + } + const Part::AttachableObject* feat = static_cast(dobj); + AttachEngine &attacher = *(this->getAttachEnginePtr()); + attacher.setUp(feat->Support, + eMapMode(feat->MapMode.getValue()), + feat->MapReversed.getValue(), + feat->MapPathParameter.getValue(), + 0.0,0.0, + feat->superPlacement.getValue()); + return Py::new_reference_to(Py::None()); + } ATTACHERPY_STDCATCH_METH; +} + +PyObject* AttachEnginePy::writeParametersToFeature(PyObject* args) +{ + PyObject* obj; + if (!PyArg_ParseTuple(args, "O!",&(App::DocumentObjectPy::Type),&obj)) + return NULL; // NULL triggers exception + + try{ + App::DocumentObjectPy* dobjpy = static_cast(obj); + App::DocumentObject* dobj = dobjpy->getDocumentObjectPtr(); + if (! dobj->isDerivedFrom(Part::AttachableObject::getClassTypeId())){ + throw Py::TypeError("Supplied object isn't Part::AttachableObject"); + } + Part::AttachableObject* feat = static_cast(dobj); + const AttachEngine &attacher = *(this->getAttachEnginePtr()); + feat->Support.Paste(attacher.references); + feat->MapMode.setValue(attacher.mapMode); + feat->MapReversed.setValue(attacher.mapReverse); + feat->MapPathParameter.setValue(attacher.attachParameter); + feat->superPlacement.setValue(attacher.superPlacement); + return Py::new_reference_to(Py::None()); + } ATTACHERPY_STDCATCH_METH; +} + PyObject* AttachEnginePy::getCustomAttributes(const char*) const { return 0;