/****************************************************************************** * * MantaFlow fluid solver framework * Copyright 2011 Tobias Pfaff, Nils Thuerey * * This program is free software, distributed under the terms of the * Apache License, Version 2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Python argument wrappers and conversion tools * ******************************************************************************/ #include "pythonInclude.h" #include #include #include "vectorbase.h" #include "manta.h" using namespace std; //****************************************************************************** // Explicit definition and instantiation of python object converters namespace Manta { extern PyTypeObject PbVec3Type; extern PyTypeObject PbVec4Type; struct PbVec3 { PyObject_HEAD float data[3]; }; struct PbVec4 { PyObject_HEAD float data[4]; }; PyObject *getPyNone() { Py_INCREF(Py_None); return Py_None; } PyObject *incref(PyObject *obj) { Py_INCREF(obj); return obj; } /*template<> PyObject* toPy(PyObject* obj) { return obj; }*/ template<> PyObject *toPy(const int &v) { return PyLong_FromLong(v); } /*template<> PyObject* toPy(const (char*) & val) { return PyUnicode_DecodeLatin1(val,strlen(val),"replace"); }*/ template<> PyObject *toPy(const string &val) { return PyUnicode_DecodeLatin1(val.c_str(), val.length(), "replace"); } template<> PyObject *toPy(const float &v) { return PyFloat_FromDouble(v); } template<> PyObject *toPy(const double &v) { return PyFloat_FromDouble(v); } template<> PyObject *toPy(const bool &v) { return PyBool_FromLong(v); } template<> PyObject *toPy(const Vec3i &v) { float x = (float)v.x, y = (float)v.y, z = (float)v.z; return PyObject_CallFunction((PyObject *)&PbVec3Type, (char *)"fff", x, y, z); } template<> PyObject *toPy(const Vec3 &v) { float x = (float)v.x, y = (float)v.y, z = (float)v.z; return PyObject_CallFunction((PyObject *)&PbVec3Type, (char *)"fff", x, y, z); } template<> PyObject *toPy(const Vec4i &v) { float x = (float)v.x, y = (float)v.y, z = (float)v.z; return PyObject_CallFunction((PyObject *)&PbVec4Type, (char *)"ffff", x, y, z); } template<> PyObject *toPy(const Vec4 &v) { float x = (float)v.x, y = (float)v.y, z = (float)v.z; return PyObject_CallFunction((PyObject *)&PbVec4Type, (char *)"ffff", x, y, z); } template<> PyObject *toPy(const PbClass_Ptr &obj) { return obj->getPyObject(); } template<> PyObject *toPy>(const std::vector &vec) { PyObject *listObj = PyList_New(vec.size()); if (!listObj) throw logic_error("Unable to allocate memory for Python list"); for (unsigned int i = 0; i < vec.size(); i++) { PbClass *pb = vec[i]; PyObject *item = pb->getPyObject(); if (!item) { Py_DECREF(listObj); throw logic_error("Unable to allocate memory for Python list"); } PyList_SET_ITEM(listObj, i, item); } return listObj; } template<> PyObject *toPy>(const std::vector &vec) { PyObject *listObj = PyList_New(vec.size()); if (!listObj) throw logic_error("Unable to allocate memory for Python list"); for (unsigned int i = 0; i < vec.size(); i++) { PyObject *item = toPy(vec[i]); if (!item) { Py_DECREF(listObj); throw logic_error("Unable to allocate memory for Python list"); } PyList_SET_ITEM(listObj, i, item); } return listObj; } template<> float fromPy(PyObject *obj) { #if PY_MAJOR_VERSION <= 2 if (PyInt_Check(obj)) return PyInt_AsLong(obj); #endif if (PyFloat_Check(obj)) return PyFloat_AsDouble(obj); if (PyLong_Check(obj)) return PyLong_AsDouble(obj); errMsg("argument is not a float"); } template<> double fromPy(PyObject *obj) { #if PY_MAJOR_VERSION <= 2 if (PyInt_Check(obj)) return PyInt_AsLong(obj); #endif if (PyFloat_Check(obj)) return PyFloat_AsDouble(obj); if (PyLong_Check(obj)) return PyLong_AsDouble(obj); errMsg("argument is not a double"); } template<> PyObject *fromPy(PyObject *obj) { return obj; } template<> PbClass *fromPy(PyObject *obj) { PbClass *pbo = Pb::objFromPy(obj); if (!PyType_Check(obj)) return pbo; const char *tname = ((PyTypeObject *)obj)->tp_name; pbo->setName(tname); return pbo; } template<> std::vector fromPy>(PyObject *obj) { std::vector vec; if (PyList_Check(obj)) { int sz = PyList_Size(obj); for (int i = 0; i < sz; ++i) { PyObject *lobj = PyList_GetItem(obj, i); vec.push_back(fromPy(lobj)); } } return vec; } template<> std::vector fromPy>(PyObject *obj) { std::vector vec; if (PyList_Check(obj)) { int sz = PyList_Size(obj); for (int i = 0; i < sz; ++i) { PyObject *lobj = PyList_GetItem(obj, i); vec.push_back(fromPy(lobj)); } } return vec; } template<> int fromPy(PyObject *obj) { #if PY_MAJOR_VERSION <= 2 if (PyInt_Check(obj)) return PyInt_AsLong(obj); #endif if (PyLong_Check(obj)) return PyLong_AsDouble(obj); if (PyFloat_Check(obj)) { double a = PyFloat_AsDouble(obj); if (fabs(a - floor(a + 0.5)) > 1e-5) errMsg("argument is not an int"); return (int)(a + 0.5); } errMsg("argument is not an int"); } template<> string fromPy(PyObject *obj) { if (PyUnicode_Check(obj)) #ifdef BLENDER // Blender is completely UTF-8 based return PyBytes_AsString(PyUnicode_AsUTF8String(obj)); #else return PyBytes_AsString(PyUnicode_AsLatin1String(obj)); #endif #if PY_MAJOR_VERSION <= 2 else if (PyString_Check(obj)) return PyString_AsString(obj); #endif else errMsg("argument is not a string"); } template<> const char *fromPy(PyObject *obj) { if (PyUnicode_Check(obj)) #ifdef BLENDER // Blender is completely UTF-8 based return PyBytes_AsString(PyUnicode_AsUTF8String(obj)); #else return PyBytes_AsString(PyUnicode_AsLatin1String(obj)); #endif #if PY_MAJOR_VERSION <= 2 else if (PyString_Check(obj)) return PyString_AsString(obj); #endif else errMsg("argument is not a string"); } template<> bool fromPy(PyObject *obj) { if (!PyBool_Check(obj)) errMsg("argument is not a boolean"); return PyLong_AsLong(obj) != 0; } template<> Vec3 fromPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec3Type)) { return Vec3(((PbVec3 *)obj)->data); } else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { return Vec3(fromPy(PyTuple_GetItem(obj, 0)), fromPy(PyTuple_GetItem(obj, 1)), fromPy(PyTuple_GetItem(obj, 2))); } errMsg("argument is not a Vec3"); } template<> Vec3i fromPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec3Type)) { return toVec3iChecked(((PbVec3 *)obj)->data); } else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { return Vec3i(fromPy(PyTuple_GetItem(obj, 0)), fromPy(PyTuple_GetItem(obj, 1)), fromPy(PyTuple_GetItem(obj, 2))); } errMsg("argument is not a Vec3i"); } template<> Vec4 fromPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec4Type)) { return Vec4(((PbVec4 *)obj)->data); } else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 4) { return Vec4(fromPy(PyTuple_GetItem(obj, 0)), fromPy(PyTuple_GetItem(obj, 1)), fromPy(PyTuple_GetItem(obj, 2)), fromPy(PyTuple_GetItem(obj, 3))); } errMsg("argument is not a Vec4"); } template<> Vec4i fromPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec4Type)) { return toVec4i(((PbVec4 *)obj)->data); } else if (PyTuple_Check(obj) && PyTuple_Size(obj) == 4) { return Vec4i(fromPy(PyTuple_GetItem(obj, 0)), fromPy(PyTuple_GetItem(obj, 1)), fromPy(PyTuple_GetItem(obj, 2)), fromPy(PyTuple_GetItem(obj, 3))); } errMsg("argument is not a Vec4i"); } template<> PbType fromPy(PyObject *obj) { PbType pb = {""}; if (!PyType_Check(obj)) return pb; const char *tname = ((PyTypeObject *)obj)->tp_name; pb.S = tname; return pb; } template<> PbTypeVec fromPy(PyObject *obj) { PbTypeVec vec; if (PyType_Check(obj)) { vec.T.push_back(fromPy(obj)); } else if (PyTuple_Check(obj)) { int sz = PyTuple_Size(obj); for (int i = 0; i < sz; i++) vec.T.push_back(fromPy(PyTuple_GetItem(obj, i))); } else errMsg("argument is not a type tuple"); return vec; } template T *tmpAlloc(PyObject *obj, std::vector *tmp) { if (!tmp) throw Error("dynamic de-ref not supported for this type"); T *ptr = new T(fromPy(obj)); tmp->push_back(ptr); return ptr; } template<> float *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> double *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> int *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> std::string *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> bool *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> Vec3 *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> Vec3i *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> Vec4 *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> Vec4i *fromPyPtr(PyObject *obj, std::vector *tmp) { return tmpAlloc(obj, tmp); } template<> std::vector *fromPyPtr>(PyObject *obj, std::vector *tmp) { return tmpAlloc>(obj, tmp); } template<> bool isPy(PyObject *obj) { #if PY_MAJOR_VERSION <= 2 if (PyInt_Check(obj)) return true; #endif return PyFloat_Check(obj) || PyLong_Check(obj); } template<> bool isPy(PyObject *obj) { #if PY_MAJOR_VERSION <= 2 if (PyInt_Check(obj)) return true; #endif return PyFloat_Check(obj) || PyLong_Check(obj); } template<> bool isPy(PyObject *obj) { return true; } template<> bool isPy(PyObject *obj) { #if PY_MAJOR_VERSION <= 2 if (PyInt_Check(obj)) return true; #endif if (PyLong_Check(obj)) return true; if (PyFloat_Check(obj)) { double a = PyFloat_AsDouble(obj); return fabs(a - floor(a + 0.5)) < 1e-5; } return false; } template<> bool isPy(PyObject *obj) { if (PyUnicode_Check(obj)) return true; #if PY_MAJOR_VERSION <= 2 if (PyString_Check(obj)) return true; #endif return false; } template<> bool isPy(PyObject *obj) { if (PyUnicode_Check(obj)) return true; #if PY_MAJOR_VERSION <= 2 if (PyString_Check(obj)) return true; #endif return false; } template<> bool isPy(PyObject *obj) { return PyBool_Check(obj); } template<> bool isPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec3Type)) return true; if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { return isPy(PyTuple_GetItem(obj, 0)) && isPy(PyTuple_GetItem(obj, 1)) && isPy(PyTuple_GetItem(obj, 2)); } return false; } template<> bool isPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec3Type)) return true; if (PyTuple_Check(obj) && PyTuple_Size(obj) == 3) { return isPy(PyTuple_GetItem(obj, 0)) && isPy(PyTuple_GetItem(obj, 1)) && isPy(PyTuple_GetItem(obj, 2)); } return false; } template<> bool isPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec4Type)) return true; if (PyTuple_Check(obj) && PyTuple_Size(obj) == 4) { return isPy(PyTuple_GetItem(obj, 0)) && isPy(PyTuple_GetItem(obj, 1)) && isPy(PyTuple_GetItem(obj, 2)) && isPy(PyTuple_GetItem(obj, 3)); } return false; } template<> bool isPy(PyObject *obj) { if (PyObject_IsInstance(obj, (PyObject *)&PbVec4Type)) return true; if (PyTuple_Check(obj) && PyTuple_Size(obj) == 4) { return isPy(PyTuple_GetItem(obj, 0)) && isPy(PyTuple_GetItem(obj, 1)) && isPy(PyTuple_GetItem(obj, 2)) && isPy(PyTuple_GetItem(obj, 3)); } return false; } template<> bool isPy(PyObject *obj) { return PyType_Check(obj); } template<> bool isPy>(PyObject *obj) { if (PyList_Check(obj)) return true; return false; } template<> bool isPy>(PyObject *obj) { if (PyList_Check(obj)) return true; return false; } //****************************************************************************** // PbArgs class defs PbArgs PbArgs::EMPTY(NULL, NULL); PbArgs::PbArgs(PyObject *linarg, PyObject *dict) : mLinArgs(0), mKwds(0) { setup(linarg, dict); } PbArgs::~PbArgs() { for (int i = 0; i < (int)mTmpStorage.size(); i++) operator delete(mTmpStorage[i]); mTmpStorage.clear(); } void PbArgs::copy(PbArgs &a) { mKwds = a.mKwds; mData = a.mData; mLinData = a.mLinData; mLinArgs = a.mLinArgs; } void PbArgs::clear() { mLinArgs = 0; mKwds = 0; mData.clear(); mLinData.clear(); } PbArgs &PbArgs::operator=(const PbArgs &a) { // mLinArgs = 0; // mKwds = 0; return *this; } void PbArgs::setup(PyObject *linarg, PyObject *dict) { if (dict) { PyObject *key, *value; Py_ssize_t pos = 0; while (PyDict_Next(dict, &pos, &key, &value)) { DataElement el; el.obj = value; el.visited = false; mData[fromPy(key)] = el; } mKwds = dict; } if (linarg) { size_t len = PyTuple_Size(linarg); for (size_t i = 0; i < len; i++) { DataElement el; el.obj = PyTuple_GetItem(linarg, i); el.visited = false; mLinData.push_back(el); } mLinArgs = linarg; } } void PbArgs::addLinArg(PyObject *obj) { DataElement el = {obj, false}; mLinData.push_back(el); } void PbArgs::check() { if (has("nocheck")) return; for (map::iterator it = mData.begin(); it != mData.end(); it++) { if (!it->second.visited) errMsg("Argument '" + it->first + "' unknown"); } for (size_t i = 0; i < mLinData.size(); i++) { if (!mLinData[i].visited) { stringstream s; s << "Function does not read argument number #" << i; errMsg(s.str()); } } } FluidSolver *PbArgs::obtainParent() { FluidSolver *solver = getPtrOpt("solver", -1, NULL); if (solver != 0) return solver; for (map::iterator it = mData.begin(); it != mData.end(); it++) { PbClass *obj = Pb::objFromPy(it->second.obj); if (obj) { if (solver == NULL) solver = obj->getParent(); } } for (vector::iterator it = mLinData.begin(); it != mLinData.end(); it++) { PbClass *obj = Pb::objFromPy(it->obj); if (obj) { if (solver == NULL) solver = obj->getParent(); } } return solver; } void PbArgs::visit(int number, const string &key) { if (number >= 0 && number < (int)mLinData.size()) mLinData[number].visited = true; map::iterator lu = mData.find(key); if (lu != mData.end()) lu->second.visited = true; } PyObject *PbArgs::getItem(const std::string &key, bool strict, ArgLocker *lk) { map::iterator lu = mData.find(key); if (lu == mData.end()) { if (strict) errMsg("Argument '" + key + "' is not defined."); return NULL; } PbClass *pbo = Pb::objFromPy(lu->second.obj); // try to lock if (pbo && lk) lk->add(pbo); return lu->second.obj; } PyObject *PbArgs::getItem(size_t number, bool strict, ArgLocker *lk) { if (number >= mLinData.size()) { if (!strict) return NULL; stringstream s; s << "Argument number #" << number << " not specified."; errMsg(s.str()); } PbClass *pbo = Pb::objFromPy(mLinData[number].obj); // try to lock if (pbo && lk) lk->add(pbo); return mLinData[number].obj; } //****************************************************************************** // ArgLocker class defs void ArgLocker::add(PbClass *p) { if (find(locks.begin(), locks.end(), p) == locks.end()) { locks.push_back(p); p->lock(); } } ArgLocker::~ArgLocker() { for (size_t i = 0; i < locks.size(); i++) locks[i]->unlock(); locks.clear(); } } // namespace Manta