Commit 6ec7a795 authored by Alexander Dielen's avatar Alexander Dielen

added python bindings

parent 33267a57
if (NOT DISABLE_OPENFLIPPER_PYTHON_SYSTEM)
include(plugin)
openflipper_plugin(PYTHONINTERFACE TYPES POLYMESH TRIANGLEMESH)
endif()
#include <pybind11/include/pybind11/pybind11.h>
#include <pybind11/include/pybind11/embed.h>
#include <iostream>
#include <OpenMeshPythonPlugin.hh>
#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <OpenFlipper/BasePlugin/PythonFunctions.hh>
#include <OpenFlipper/PythonInterpreter/PythonTypeConversions.hh>
#include <OpenFlipper/common/Types.hh>
#include <ObjectTypes/PolyMesh/PolyMesh.hh>
#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
#include "PythonInterface/MeshTypes.hh"
namespace py = pybind11;
py::object get_mesh(int _id) {
TriMesh* trimesh = nullptr;
PolyMesh* polymesh = nullptr;
if (PluginFunctions::getMesh(_id, trimesh)) {
return py::cast(static_cast<OpenMeshPython::TriMesh*>(trimesh));
}
else if (PluginFunctions::getMesh(_id, polymesh)) {
return py::cast(static_cast<OpenMeshPython::PolyMesh*>(polymesh));
}
else {
std::stringstream msg;
msg << "object with id " << _id << " is not a mesh";
PyErr_SetString(PyExc_RuntimeError, msg.str().c_str());
throw py::error_already_set();
}
}
void init_plugin(py::module m) {
QObject* pluginPointer = getPluginPointer("OpenMeshPython");
if (!pluginPointer) {
std::cerr << "Error Getting plugin pointer for Plugin-OpenMeshPython" << std::endl;
return;
}
OpenMeshPythonPlugin* plugin = qobject_cast<OpenMeshPythonPlugin*>(pluginPointer);
if (!plugin) {
std::cerr << "Error converting plugin pointer for Plugin-OpenMeshPython" << std::endl;
return;
}
// Export our core. Make sure that the c++ worlds core object is not deleted if
// the python side gets deleted!!
py::class_< OpenMeshPythonPlugin,std::unique_ptr<OpenMeshPythonPlugin, py::nodelete> > openMeshPython(m, "OpenMeshPython");
// On the c++ side we will just return the existing core instance
// and prevent the system to recreate a new core as we need
// to work on the existing one.
openMeshPython.def(py::init([plugin]() { return plugin; }));
m.def("getMesh", &get_mesh);
m.def("getMesh", [](const std::string& _name) -> py::object {
using namespace PluginFunctions;
DataType MESH_TYPES = DATA_TRIANGLE_MESH | DATA_POLY_MESH;
for (ObjectIterator o_it(ALL_OBJECTS, MESH_TYPES); o_it != objectsEnd(); ++o_it) {
if (o_it->name() == QString::fromStdString(_name)) {
return get_mesh(o_it->id());
}
}
std::stringstream msg;
msg << "there is no mesh with name \"" << _name << "\"";
PyErr_SetString(PyExc_RuntimeError, msg.str().c_str());
throw py::error_already_set();
});
m.def("addMesh", [plugin](OpenMeshPython::TriMesh& _mesh, std::string _name) {
// make sure that all properties used by OpenFlipper
// are present and update the normals
_mesh.request_vertex_normals();
_mesh.request_halfedge_normals();
_mesh.request_face_normals();
_mesh.update_normals();
_mesh.request_vertex_status();
_mesh.request_halfedge_status();
_mesh.request_edge_status();
_mesh.request_face_status();
int newObjectId = -1;
plugin->addEmptyObject(DATA_TRIANGLE_MESH, newObjectId);
TriMeshObject* object = nullptr;
PluginFunctions::getObject(newObjectId, object);
if (object && object->mesh()) {
TriMesh* mesh = object->mesh();
*mesh = _mesh;
if (_name != "")
object->setName(QString::fromStdString(_name));
plugin->updatedObject(object->id(), UPDATE_ALL);
return object->id();
}
else {
PyErr_SetString(PyExc_RuntimeError, "could not create new object");
throw py::error_already_set();
}
}, py::arg("mesh"), py::arg("name") = "");
}
#include "OpenMeshPythonPlugin.hh"
#ifndef OPENMESHPYTHONPLUGIN_HH_INCLUDED
#define OPENMESHPYTHONPLUGIN_HH_INCLUDED
#include <OpenFlipper/BasePlugin/BaseInterface.hh>
#include <OpenFlipper/BasePlugin/LoadSaveInterface.hh>
#include <OpenFlipper/BasePlugin/PythonInterface.hh>
class OpenMeshPythonPlugin : public QObject, BaseInterface, LoadSaveInterface, PythonInterface
{
Q_OBJECT
Q_INTERFACES(BaseInterface)
Q_INTERFACES(LoadSaveInterface)
Q_INTERFACES(PythonInterface)
Q_PLUGIN_METADATA(IID "org.OpenFlipper.Plugins.Plugin-OpenMeshPython")
signals:
void updatedObject(int _identifier, const UpdateType & _type);
void addEmptyObject(DataType _type, int& _id);
public:
~OpenMeshPythonPlugin() {}
QString name() { return QString("OpenMeshPython"); }
QString description() { return QString("OpenMesh Python Bindings for OpenFlipper"); }
public slots:
QString version() { return QString("1.0"); }
private slots:
void noguiSupported() {}
};
#endif
#include "MeshTypes.hh"
#include "Miscellaneous.hh"
#include "Mesh.hh"
#include "Iterator.hh"
#include "Circulator.hh"
#include "InputOutput.hh"
#include "Decimater.hh"
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#ifdef OPENMESH_PYTHON_EMBEDDED_MODULE
#include "InitPlugin.hh"
#endif
namespace py = pybind11;
namespace OM = OpenMesh;
namespace OpenMeshPython {
#ifdef OPENMESH_PYTHON_EMBEDDED_MODULE
PYBIND11_EMBEDDED_MODULE(OpenMeshPython, m) {
init_plugin(m);
#else
PYBIND11_MODULE(openmesh, m) {
#endif
expose_handles(m);
expose_mesh<PolyMesh>(m, "PolyMesh");
expose_mesh<TriMesh>(m, "TriMesh");
expose_iterator<OM::PolyConnectivity::VertexIter, &OM::ArrayKernel::n_vertices>(m, "VertexIter");
expose_iterator<OM::PolyConnectivity::HalfedgeIter, &OM::ArrayKernel::n_halfedges>(m, "HalfedgeIter");
expose_iterator<OM::PolyConnectivity::EdgeIter, &OM::ArrayKernel::n_edges>(m, "EdgeIter");
expose_iterator<OM::PolyConnectivity::FaceIter, &OM::ArrayKernel::n_faces>(m, "FaceIter");
expose_circulator<OM::PolyConnectivity::VertexVertexIter, OM::VertexHandle>(m, "VertexVertexIter");
expose_circulator<OM::PolyConnectivity::VertexIHalfedgeIter, OM::VertexHandle>(m, "VertexIHalfedgeIter");
expose_circulator<OM::PolyConnectivity::VertexOHalfedgeIter, OM::VertexHandle>(m, "VertexOHalfedgeIter");
expose_circulator<OM::PolyConnectivity::VertexEdgeIter, OM::VertexHandle>(m, "VertexEdgeIter");
expose_circulator<OM::PolyConnectivity::VertexFaceIter, OM::VertexHandle>(m, "VertexFaceIter");
expose_circulator<OM::PolyConnectivity::FaceVertexIter, OM::FaceHandle>(m, "FaceVertexIter");
expose_circulator<OM::PolyConnectivity::FaceHalfedgeIter, OM::FaceHandle>(m, "FaceHalfedgeIter");
expose_circulator<OM::PolyConnectivity::FaceEdgeIter, OM::FaceHandle>(m, "FaceEdgeIter");
expose_circulator<OM::PolyConnectivity::FaceFaceIter, OM::FaceHandle>(m, "FaceFaceIter");
expose_circulator<OM::PolyConnectivity::HalfedgeLoopIter, OM::HalfedgeHandle>(m, "HalfedgeLoopIter");
expose_io(m);
expose_decimater<PolyMesh>(m, "PolyMesh");
expose_decimater<TriMesh>(m, "TriMesh");
}
} // namespace OpenMeshPython
#ifndef OPENMESH_PYTHON_CIRCULATOR_HH
#define OPENMESH_PYTHON_CIRCULATOR_HH
#include "MeshTypes.hh"
#include <pybind11/pybind11.h>
namespace py = pybind11;
namespace OpenMeshPython {
/**
* Wrapper for circulators.
*
* This class template is used to wrap circulators for %Python. It implements
* %Python's iterator protocol (the magic methods \_\_iter\_\_ and
* \_\_next\_\_).
*
* @tparam Circulator A circulator type.
*/
template<class Circulator, class CenterEntityHandle>
class CirculatorWrapperT {
public:
/**
* Constructor
*
* @param _mesh The mesh that contains the items to iterate over.
* @param _center The handle to the center item.
*/
CirculatorWrapperT(PolyMesh& _mesh, CenterEntityHandle _center) :
circulator_(_mesh, _center) {
}
/**
* Constructor
*
* @param _mesh The mesh that contains the items to iterate over.
* @param _center The handle to the center item.
*/
CirculatorWrapperT(TriMesh& _mesh, CenterEntityHandle _center) :
circulator_(_mesh, _center) {
}
/**
* Implementation of %Python's \_\_iter\_\_ magic method.
*
* @return This circulator.
*/
CirculatorWrapperT iter() const {
return *this;
}
/**
* Implementation of %Python's \_\_next\_\_ magic method.
*
* @return The next item. Raises a %Python StopIteration exception if
* there are no more items.
*/
typename Circulator::value_type next() {
if (circulator_.is_valid()) {
typename Circulator::value_type res = *circulator_;
++circulator_;
return res;
}
else {
throw py::stop_iteration();
}
return typename Circulator::value_type();
}
private:
Circulator circulator_;
};
/**
* Expose a circulator type to %Python.
*
* @tparam Circulator A circulator type.
*
* @param _name The name of the circulator type to be exposed.
*
* @note Circulators are wrapped by CirculatorWrapperT before they are exposed
* to %Python, i.e. they are not exposed directly. This means that circulators
* that are passed from %Python to C++ are instances of CirculatorWrapperT.
*/
template<class Circulator, class CenterEntityHandle>
void expose_circulator(py::module& m, const char *_name) {
py::class_<CirculatorWrapperT<Circulator, CenterEntityHandle> >(m, _name)
.def(py::init<TriMesh&, CenterEntityHandle>())
.def(py::init<PolyMesh&, CenterEntityHandle>())
.def("__iter__", &CirculatorWrapperT<Circulator, CenterEntityHandle>::iter)
.def("__next__", &CirculatorWrapperT<Circulator, CenterEntityHandle>::next)
;
}
} // namespace OpenMeshPython
#endif
This diff is collapsed.
#include "InputOutput.hh"
#include "MeshTypes.hh"
#include <pybind11/operators.h>
namespace OM = OpenMesh;
namespace OpenMeshPython {
template <class Mesh>
void def_read_mesh(py::module& m, const char *_name) {
m.def(_name,
[](
const std::string& _filename,
bool _binary,
bool _msb,
bool _lsb,
bool _swap,
bool _vertex_normal,
bool _vertex_color,
bool _vertex_tex_coord,
bool _halfedge_tex_coord,
bool _edge_color,
bool _face_normal,
bool _face_color,
bool _face_texture_index,
bool _color_alpha,
bool _color_float
)
{
Mesh mesh;
OM::IO::Options options;
if (_binary) options += OM::IO::Options::Binary;
if (_msb) options += OM::IO::Options::MSB;
if (_lsb) options += OM::IO::Options::LSB;
if (_swap) options += OM::IO::Options::Swap;
if (_vertex_normal) {
options += OM::IO::Options::VertexNormal;
mesh.request_vertex_normals();
}
if (_vertex_color) {
options += OM::IO::Options::VertexColor;
mesh.request_vertex_colors();
}
if (_vertex_tex_coord) {
options += OM::IO::Options::VertexTexCoord;
mesh.request_vertex_texcoords1D();
mesh.request_vertex_texcoords2D();
mesh.request_vertex_texcoords3D();
}
if (_halfedge_tex_coord) {
options += OM::IO::Options::FaceTexCoord;
mesh.request_halfedge_texcoords1D();
mesh.request_halfedge_texcoords2D();
mesh.request_halfedge_texcoords3D();
}
if (_edge_color) {
options += OM::IO::Options::EdgeColor;
mesh.request_edge_colors();
}
if (_face_normal) {
options += OM::IO::Options::FaceNormal;
mesh.request_face_normals();
}
if (_face_color) {
options += OM::IO::Options::FaceColor;
mesh.request_face_colors();
}
if (_face_texture_index) {
mesh.request_face_texture_index();
}
if (_color_alpha) options += OM::IO::Options::ColorAlpha;
if (_color_float) options += OM::IO::Options::ColorFloat;
const bool ok = OM::IO::read_mesh(mesh, _filename, options);
if (!ok) {
const std::string msg = "File could not be read: " + _filename;
PyErr_SetString(PyExc_RuntimeError, msg.c_str());
throw py::error_already_set();
}
if (_vertex_normal && !options.vertex_has_normal()) {
PyErr_SetString(PyExc_RuntimeError, "Vertex normals could not be read.");
throw py::error_already_set();
}
if (_vertex_color && !options.vertex_has_color()) {
PyErr_SetString(PyExc_RuntimeError, "Vertex colors could not be read.");
throw py::error_already_set();
}
if (_vertex_tex_coord && !options.vertex_has_texcoord()) {
PyErr_SetString(PyExc_RuntimeError, "Vertex texcoords could not be read.");
throw py::error_already_set();
}
if (_edge_color && !options.edge_has_color()) {
PyErr_SetString(PyExc_RuntimeError, "Edge colors could not be read.");
throw py::error_already_set();
}
if (_face_normal && !options.face_has_normal()) {
PyErr_SetString(PyExc_RuntimeError, "Face normals could not be read.");
throw py::error_already_set();
}
if (_face_color && !options.face_has_color()) {
PyErr_SetString(PyExc_RuntimeError, "Face colors could not be read.");
throw py::error_already_set();
}
if (_halfedge_tex_coord && !options.face_has_texcoord()) {
PyErr_SetString(PyExc_RuntimeError, "Halfedge texcoords could not be read.");
throw py::error_already_set();
}
return mesh;
},
py::arg("filename"),
py::arg("binary")=false,
py::arg("msb")=false,
py::arg("lsb")=false,
py::arg("swap")=false,
py::arg("vertex_normal")=false,
py::arg("vertex_color")=false,
py::arg("vertex_tex_coord")=false,
py::arg("halfedge_tex_coord")=false,
py::arg("edge_color")=false,
py::arg("face_normal")=false,
py::arg("face_color")=false,
py::arg("face_texture_index")=false,
py::arg("color_alpha")=false,
py::arg("color_float")=false
);
}
template <class Mesh>
void def_write_mesh(py::module& m) {
m.def("write_mesh",
[](
const std::string& _filename,
const Mesh& _mesh,
bool _binary,
bool _msb,
bool _lsb,
bool _swap,
bool _vertex_normal,
bool _vertex_color,
bool _vertex_tex_coord,
bool _halfedge_tex_coord,
bool _edge_color,
bool _face_normal,
bool _face_color,
bool _color_alpha,
bool _color_float
)
{
OM::IO::Options options;
if (_binary) options += OM::IO::Options::Binary;
if (_msb) options += OM::IO::Options::MSB;
if (_lsb) options += OM::IO::Options::LSB;
if (_swap) options += OM::IO::Options::Swap;
if (_vertex_normal) options += OM::IO::Options::VertexNormal;
if (_vertex_color) options += OM::IO::Options::VertexColor;
if (_vertex_tex_coord) options += OM::IO::Options::VertexTexCoord;
if (_halfedge_tex_coord) options += OM::IO::Options::FaceTexCoord;
if (_edge_color) options += OM::IO::Options::EdgeColor;
if (_face_normal) options += OM::IO::Options::FaceNormal;
if (_face_color) options += OM::IO::Options::FaceColor;
if (_color_alpha) options += OM::IO::Options::ColorAlpha;
if (_color_float) options += OM::IO::Options::ColorFloat;
const bool ok = OM::IO::write_mesh(_mesh, _filename, options);
if (!ok) {
const std::string msg = "File could not be written: " + _filename;
PyErr_SetString(PyExc_RuntimeError, msg.c_str());
throw py::error_already_set();
}
},
py::arg("filename"),
py::arg("mesh"),
py::arg("binary")=false,
py::arg("msb")=false,
py::arg("lsb")=false,
py::arg("swap")=false,
py::arg("vertex_normal")=false,
py::arg("vertex_color")=false,
py::arg("vertex_tex_coord")=false,
py::arg("halfedge_tex_coord")=false,
py::arg("edge_color")=false,
py::arg("face_normal")=false,
py::arg("face_color")=false,
py::arg("color_alpha")=false,
py::arg("color_float")=false
);
}
void expose_io(py::module& m) {
def_read_mesh<TriMesh>(m, "read_trimesh");
def_read_mesh<PolyMesh>(m, "read_polymesh");
def_write_mesh<TriMesh>(m);
def_write_mesh<PolyMesh>(m);
}
} // namespace OpenMeshPython
#ifndef OPENMESH_PYTHON_INPUTOUTPUT_HH
#define OPENMESH_PYTHON_INPUTOUTPUT_HH
#include <pybind11/pybind11.h>
namespace py = pybind11;
namespace OpenMeshPython {
void expose_io(py::module& m);
} // namespace OpenMeshPython
#endif
#ifndef OPENMESH_PYTHON_ITERATOR_HH
#define OPENMESH_PYTHON_ITERATOR_HH
#include "MeshTypes.hh"
#include <pybind11/pybind11.h>
namespace py = pybind11;
namespace OpenMeshPython {
/**
* Wrapper for mesh item iterators.
*
* This class template is used to wrap mesh item iterators for %Python. It
* implements %Python's iterator protocol (the magic methods \_\_iter\_\_ and
* \_\_next\_\_).
*
* @tparam Iterator An iterator type.
* @tparam n_items A member function pointer that points to the mesh function
* that returns the number of items to iterate over (e.g. n_vertices).
*/
template<class Iterator, size_t (OpenMesh::ArrayKernel::*n_items)() const>
class IteratorWrapperT {
public:
/**
* Constructor
*
* @param _mesh The mesh that contains the items to iterate over.
* @param _hnd The handle of the first item to iterate over.
* @param _skip Specifies if deleted/hidden elements are skipped.
*/
IteratorWrapperT(const PolyMesh& _mesh, typename Iterator::value_type _hnd, bool _skip = false) :
mesh_(_mesh), n_items_(n_items),
iterator_(_mesh, _hnd, _skip),
iterator_end_(_mesh, typename Iterator::value_type(int((_mesh.*n_items)()))) {
}
/**
* Constructor
*
* @param _mesh The mesh that contains the items to iterate over.
* @param _hnd The handle of the first item to iterate over.
* @param _skip Specifies if deleted/hidden elements are skipped.
*/
IteratorWrapperT(const TriMesh& _mesh, typename Iterator::value_type _hnd, bool _skip = false) :
mesh_(_mesh), n_items_(n_items),
iterator_(_mesh, _hnd, _skip),
iterator_end_(_mesh, typename Iterator::value_type(int((_mesh.*n_items)()))) {
}
/**
* Implementation of %Python's \_\_iter\_\_ magic method.
*
* @return This iterator.
*/
IteratorWrapperT iter() const {
return *this;
}
/**
* Implementation of %Python's \_\_next\_\_ magic method.
*
* @return The next item. Raises a %Python StopIteration exception if
* there are no more items.
*/
typename Iterator::value_type next() {
if (iterator_ != iterator_end_) {
typename Iterator::value_type res = *iterator_;
++iterator_;
return res;
}
else {
throw py::stop_iteration();
}
return typename Iterator::value_type();
}
/**
* Implementation of %Python's \_\_len\_\_ magic method.
*
* @return The number of items in the mesh.
*/
unsigned int len() const {
return (mesh_.*n_items_)();
}
private:
const OpenMesh::PolyConnectivity& mesh_;
size_t (OpenMesh::ArrayKernel::*n_items_)() const;
Iterator iterator_;
Iterator iterator_end_;
};
/**
* Expose an iterator type to %Python.
*
* @tparam Iterator An iterator type.
* @tparam n_items A member function pointer that points to the mesh function
* that returns the number of items to iterate over (e.g. n_vertices).
*
* @param _name The name of the iterator type to be exposed.
*
* @note %Iterators are wrapped by IteratorWrapperT before they are exposed to
* %Python, i.e. they are not exposed directly. This means that iterators
* that are passed from %Python to C++ are instances of IteratorWrapperT.
*/
template<class Iterator, size_t (OpenMesh::ArrayKernel::*n_items)() const>
void expose_iterator(py::module& m, const char *_name) {
py::class_<IteratorWrapperT<Iterator, n_items> >(m, _name)
.def(py::init<PolyMesh&, typename Iterator::value_type>())
.def(py::init<PolyMesh&, typename Iterator::value_type, bool>())
.def(py::init<TriMesh&, typename Iterator::value_type>())
.def(py::init<TriMesh&, typename Iterator::value_type, bool>())
.def("__iter__", &IteratorWrapperT<Iterator, n_items>::iter)
.def("__next__", &IteratorWrapperT<Iterator, n_items>::next)
.def("__len__", &IteratorWrapperT<Iterator, n_items>::len)
;
}
} // namespace OpenMeshPython
#endif
This diff is collapsed.
This diff is collapsed.
#include "Miscellaneous.hh"
#include <OpenMesh/Core/Mesh/Handles.hh>
#include <OpenMesh/Core/Utils/Property.hh>
namespace OM = OpenMesh;
namespace OpenMeshPython {
/**
* Expose item and property handles to %Python.
*/
void expose_handles(py::module& m) {
py::class_<OM::BaseHandle>(m, "BaseHandle")
.def(py::init<>())
.def(py::init<int>())
.def("idx", &OM::BaseHandle::idx)
.def("is_valid", &OM::BaseHandle::is_valid)
.def("reset", &OM::BaseHandle::reset)
.def("invalidate", &OM::BaseHandle::invalidate)
.def("__eq__", &OM::BaseHandle::operator ==)
.def("__ne__", &OM::BaseHandle::operator !=)
.def("__lt__", &OM::BaseHandle::operator <)
;
py::class_<OM::VertexHandle, OM::BaseHandle>(m, "VertexHandle")
.def(py::init<>())
.def(py::init<int>())
;
py::class_<OM::HalfedgeHandle, OM::BaseHandle>(m, "HalfedgeHandle")
.def(py::init<>())
.def(py::init<int