// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2016 Viktor Titov (DeepSOIC) * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include #include #include "AttachEnginePy.h" #include "AttachEnginePy.cpp" #include "AttachExtensionPy.h" #include "OCCError.h" #include "TopoShapePy.h" using namespace Attacher; // returns a string which represents the object e.g. when printed in python std::string AttachEnginePy::representation() const { return {""}; } 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::PyExc_FC_GeneralError, errMsg.str().c_str()); return -1; } AttachEngine* oldAttacher = this->getAttachEnginePtr(); this->_pcTwinPointer = pNewAttacher; delete oldAttacher; return 0; } PyErr_SetString( PyExc_TypeError, "Wrong set of constructor arguments. Can be: (), ('Attacher::AttachEngine3D'), " "('Attacher::AttachEnginePlane'), ('Attacher::AttachEngineLine'), " "('Attacher::AttachEnginePoint'), (other_attacher_instance)." ); return -1; } Py::String AttachEnginePy::getAttacherType() const { return {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 & e) \ { \ throw Py::Exception(Part::PartExceptionOCCError, e.GetMessageString()); \ } \ catch (Base::Exception & e) \ { \ e.setPyException(); \ throw Py::Exception(); \ } Py::String AttachEnginePy::getMode() const { try { AttachEngine& attacher = *(this->getAttachEnginePtr()); return {attacher.getModeName(attacher.mapMode)}; } ATTACHERPY_STDCATCH_ATTR; } void AttachEnginePy::setMode(Py::String arg) { try { AttachEngine& attacher = *(this->getAttachEnginePtr()); std::string modeName = static_cast(arg); attacher.mapMode = attacher.getModeByName(modeName); } ATTACHERPY_STDCATCH_ATTR; } Py::Object AttachEnginePy::getReferences() const { try { AttachEngine& attacher = *(this->getAttachEnginePtr()); Py::List ret; int i = 0; for (auto obj : attacher.getRefObjects()) { ret.append( Py::TupleN(Py::asObject(obj->getPyObject()), Py::String(attacher.subnames[i++])) ); } return ret; } ATTACHERPY_STDCATCH_ATTR; } void AttachEnginePy::setReferences(Py::Object arg) { try { AttachEngine& attacher = *(this->getAttachEnginePtr()); App::PropertyLinkSubList references; references.setPyObject(arg.ptr()); attacher.setReferences(references); } ATTACHERPY_STDCATCH_ATTR; } Py::Object AttachEnginePy::getAttachmentOffset() const { try { AttachEngine& attacher = *(this->getAttachEnginePtr()); return Py::Object(new Base::PlacementPy(new Base::Placement(attacher.attachmentOffset)), true); } ATTACHERPY_STDCATCH_ATTR; } void AttachEnginePy::setAttachmentOffset(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.attachmentOffset = *(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() const { try { AttachEngine& attacher = *(this->getAttachEnginePtr()); return {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() 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() 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() 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() const { try { Py::List ret; AttachEngine& attacher = *(this->getAttachEnginePtr()); for (int imode = 0; imode < mmDummy_NumberOfModes; imode++) { if (!attacher.modeRefTypes[imode].empty()) { 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 & e) \ { \ PyErr_SetString(Part::PartExceptionOCCError, e.GetMessageString()); \ return NULL; \ } \ catch (Base::Exception & e) \ { \ PyErr_SetString(Base::PyExc_FC_GeneralError, e.what()); \ return NULL; \ } \ catch (const Py::Exception&) \ { \ return NULL; \ } PyObject* AttachEnginePy::getModeInfo(PyObject* args) { char* modeName; if (!PyArg_ParseTuple(args, "s", &modeName)) { return nullptr; } 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 (std::size_t 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::Long(mmode); try { Py::Module module(PyImport_ImportModule("PartGui"), true); if (module.isNull() || !module.hasAttr("AttachEngineResources")) { // in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its // document methods) throw Py::RuntimeError("Gui is not up"); // DeepSOIC: wanted to throw ImportError // here, but it's not defined, so I don't // know... } Py::Object submod(module.getAttr("AttachEngineResources")); Py::Callable method(submod.getAttr("getModeStrings")); Py::Tuple arg(2); arg.setItem(0, Py::String(this->getAttachEnginePtr()->getTypeId().getName())); arg.setItem(1, Py::Long(mmode)); Py::List strs = method.apply(arg); assert(strs.size() == 2); ret["UserFriendlyName"] = strs[0]; ret["BriefDocu"] = strs[1]; } catch (Py::Exception& e) { if (PyErr_ExceptionMatches(PyExc_ImportError)) { // the GUI is not up. Base::Console().warning( "AttachEngine: Gui not up, so no gui-related entries in getModeInfo.\n" ); e.clear(); } else { Base::Console().warning("AttachEngine.getModeInfo: error obtaining GUI strings\n"); e.clear(); } } catch (Base::Exception& e) { Base::Console().warning("AttachEngine.getModeInfo: error obtaining GUI strings:"); Base::Console().warning(e.what()); Base::Console().warning("\n"); } return Py::new_reference_to(ret); } ATTACHERPY_STDCATCH_METH; } PyObject* AttachEnginePy::getRefTypeOfShape(PyObject* args) { PyObject* pcObj; if (!PyArg_ParseTuple(args, "O!", &(Part::TopoShapePy::Type), &pcObj)) { return nullptr; } try { TopoDS_Shape shape = static_cast(pcObj)->getTopoShapePtr()->getShape(); eRefType rt = AttachEngine::getShapeType(shape); return Py::new_reference_to(Py::String(AttachEngine::getRefTypeName(rt))); } ATTACHERPY_STDCATCH_METH; } PyObject* AttachEnginePy::isFittingRefType(PyObject* args) { char* type_shape_str; char* type_need_str; if (!PyArg_ParseTuple(args, "ss", &type_shape_str, &type_need_str)) { return nullptr; } 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::downgradeRefType(PyObject* args) { char* type_shape_str; if (!PyArg_ParseTuple(args, "s", &type_shape_str)) { return nullptr; } 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::getRefTypeInfo(PyObject* args) { char* typeName; if (!PyArg_ParseTuple(args, "s", &typeName)) { return nullptr; } try { AttachEngine& attacher = *(this->getAttachEnginePtr()); eRefType rt = attacher.getRefTypeByName(typeName); Py::Dict ret; ret["TypeIndex"] = Py::Long(rt); ret["Rank"] = Py::Long(AttachEngine::getTypeRank(rt)); try { Py::Module module(PyImport_ImportModule("PartGui"), true); if (module.isNull() || !module.hasAttr("AttachEngineResources")) { // in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its // document methods) throw Py::RuntimeError("Gui is not up"); // DeepSOIC: wanted to throw ImportError // here, but it's not defined, so I don't // know... } Py::Object submod(module.getAttr("AttachEngineResources")); Py::Callable method(submod.getAttr("getRefTypeUserFriendlyName")); Py::Tuple arg(1); arg.setItem(0, Py::Long(rt)); Py::String st = method.apply(arg); ret["UserFriendlyName"] = st; } catch (Py::Exception& e) { if (PyErr_ExceptionMatches(PyExc_ImportError)) { // the GUI is not up. Base::Console().warning( "AttachEngine: Gui not up, so no gui-related entries in getModeInfo.\n" ); e.clear(); } else { Base::Console().warning("AttachEngine.getRefTypeInfo: error obtaining GUI strings\n"); e.clear(); } } catch (Base::Exception& e) { Base::Console().warning("AttachEngine.getRefTypeInfo: error obtaining GUI strings:"); Base::Console().warning(e.what()); Base::Console().warning("\n"); } return Py::new_reference_to(ret); } ATTACHERPY_STDCATCH_METH; } PyObject* AttachEnginePy::copy(PyObject* args) const { if (!PyArg_ParseTuple(args, "")) { return nullptr; } return new AttachEnginePy(this->getAttachEnginePtr()->copy()); } PyObject* AttachEnginePy::calculateAttachedPlacement(PyObject* args) const { PyObject* pcObj; if (!PyArg_ParseTuple(args, "O!", &(Base::PlacementPy::Type), &pcObj)) { return nullptr; } 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 nullptr; } PyObject* AttachEnginePy::suggestModes(PyObject* args) { if (!PyArg_ParseTuple(args, "")) { return nullptr; } 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 nullptr; } try { App::DocumentObjectPy* dobjpy = static_cast(obj); App::DocumentObject* dobj = dobjpy->getDocumentObjectPtr(); if (!dobj->hasExtension(Part::AttachExtension::getExtensionClassTypeId())) { throw Py::TypeError("Supplied object has no Part::AttachExtension"); } Part::AttachExtension* feat = dobj->getExtensionByType(); AttachEngine& attacher = *(this->getAttachEnginePtr()); attacher.setUp( feat->AttachmentSupport, eMapMode(feat->MapMode.getValue()), feat->MapReversed.getValue(), feat->MapPathParameter.getValue(), 0.0, 0.0, feat->AttachmentOffset.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 nullptr; } try { App::DocumentObjectPy* dobjpy = static_cast(obj); App::DocumentObject* dobj = dobjpy->getDocumentObjectPtr(); if (!dobj->hasExtension(Part::AttachExtension::getExtensionClassTypeId())) { throw Py::TypeError("Supplied object has no Part::AttachExtension"); } Part::AttachExtension* feat = dobj->getExtensionByType(); const AttachEngine& attacher = *(this->getAttachEnginePtr()); feat->AttachmentSupport.setValues(attacher.getRefObjects(), attacher.getSubValues()); feat->MapMode.setValue(attacher.mapMode); feat->MapReversed.setValue(attacher.mapReverse); feat->MapPathParameter.setValue(attacher.attachParameter); feat->AttachmentOffset.setValue(attacher.attachmentOffset); return Py::new_reference_to(Py::None()); } ATTACHERPY_STDCATCH_METH; } PyObject* AttachEnginePy::getCustomAttributes(const char*) const { return nullptr; } int AttachEnginePy::setCustomAttributes(const char*, PyObject*) { return 0; }