Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update api to support matplotlib-3.7.1 version #352

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 47 additions & 60 deletions matplotlibcpp.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#pragma once
#ifndef __MATPLOTLIBCPP__H__
#define __MATPLOTLIBCPP__H__

// Python headers must be included before any system headers, since
// they define _POSIX_C_SOURCE
Expand Down Expand Up @@ -351,9 +353,9 @@ template <> struct select_npy_type<uint64_t> { const static NPY_TYPES type = NPY
// Sanity checks; comment them out or change the numpy type below if you're compiling on
// a platform where they don't apply
static_assert(sizeof(long long) == 8);
template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
// template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
static_assert(sizeof(unsigned long long) == 8);
template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };
// template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };

template<typename Numeric>
PyObject* get_array(const std::vector<Numeric>& v)
Expand Down Expand Up @@ -470,6 +472,33 @@ bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const st
return res;
}

// FIXME: <https://stackoverflow.com/questions/76077363/why-does-matplotlib-cpp-throw-runtime-exceptions-in-the-3d-examples>
//Given a figure, create a subplot with a 3d axis and return a pointer to that axis. also calls incref on the axis pointer.
PyObject* init_3d_axis(PyObject *fig)
{
PyObject *asp_kwargs = PyDict_New();
PyDict_SetItemString(asp_kwargs, "projection", PyString_FromString("3d"));

PyObject *asp = PyObject_GetAttrString(fig, "add_subplot");
Py_INCREF(asp);
PyObject *tmpax = PyObject_Call(asp, detail::_interpreter::get().s_python_empty_tuple, asp_kwargs);
Py_INCREF(tmpax);

PyObject *gca = PyObject_GetAttrString(fig, "gca");
if (!gca) throw std::runtime_error("No gca");
Py_INCREF(gca);
PyObject *axis = PyObject_Call(gca, detail::_interpreter::get().s_python_empty_tuple, detail::_interpreter::get().s_python_empty_tuple);

if (!axis) throw std::runtime_error("No axis");
Py_INCREF(axis);

Py_DECREF(gca);
Py_DECREF(tmpax);
Py_DECREF(asp);
return axis;
}


// TODO - it should be possible to make this work by implementing
// a non-numpy alternative for `detail::get_2darray()`.
#ifndef WITHOUT_NUMPY
Expand Down Expand Up @@ -555,20 +584,8 @@ void plot_surface(const std::vector<::std::vector<Numeric>> &x,
Py_DECREF(fig_exists);
if (!fig) throw std::runtime_error("Call to figure() failed.");

PyObject *gca_kwargs = PyDict_New();
PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
PyObject *axis = init_3d_axis(fig);

PyObject *gca = PyObject_GetAttrString(fig, "gca");
if (!gca) throw std::runtime_error("No gca");
Py_INCREF(gca);
PyObject *axis = PyObject_Call(
gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);

if (!axis) throw std::runtime_error("No axis");
Py_INCREF(axis);

Py_DECREF(gca);
Py_DECREF(gca_kwargs);

PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface");
if (!plot_surface) throw std::runtime_error("No surface");
Expand Down Expand Up @@ -723,20 +740,7 @@ void plot3(const std::vector<Numeric> &x,
}
if (!fig) throw std::runtime_error("Call to figure() failed.");

PyObject *gca_kwargs = PyDict_New();
PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));

PyObject *gca = PyObject_GetAttrString(fig, "gca");
if (!gca) throw std::runtime_error("No gca");
Py_INCREF(gca);
PyObject *axis = PyObject_Call(
gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);

if (!axis) throw std::runtime_error("No axis");
Py_INCREF(axis);

Py_DECREF(gca);
Py_DECREF(gca_kwargs);
PyObject *axis = init_3d_axis(fig);

PyObject *plot3 = PyObject_GetAttrString(axis, "plot");
if (!plot3) throw std::runtime_error("No 3D line plot");
Expand Down Expand Up @@ -1126,20 +1130,7 @@ bool scatter(const std::vector<NumericX>& x,
Py_DECREF(fig_exists);
if (!fig) throw std::runtime_error("Call to figure() failed.");

PyObject *gca_kwargs = PyDict_New();
PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));

PyObject *gca = PyObject_GetAttrString(fig, "gca");
if (!gca) throw std::runtime_error("No gca");
Py_INCREF(gca);
PyObject *axis = PyObject_Call(
gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);

if (!axis) throw std::runtime_error("No axis");
Py_INCREF(axis);

Py_DECREF(gca);
Py_DECREF(gca_kwargs);
PyObject *axis = init_3d_axis(fig);

PyObject *plot3 = PyObject_GetAttrString(axis, "scatter");
if (!plot3) throw std::runtime_error("No 3D line plot");
Expand Down Expand Up @@ -1496,27 +1487,15 @@ bool quiver(const std::vector<NumericX>& x, const std::vector<NumericY>& y, cons
{
PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
}

//get figure gca to enable 3d projection
PyObject *fig =
PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
detail::_interpreter::get().s_python_empty_tuple);
if (!fig) throw std::runtime_error("Call to figure() failed.");

PyObject *gca_kwargs = PyDict_New();
PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));

PyObject *gca = PyObject_GetAttrString(fig, "gca");
if (!gca) throw std::runtime_error("No gca");
Py_INCREF(gca);
PyObject *axis = PyObject_Call(
gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
PyObject *axis = init_3d_axis(fig);

if (!axis) throw std::runtime_error("No axis");
Py_INCREF(axis);
Py_DECREF(gca);
Py_DECREF(gca_kwargs);

//plot our boys bravely, plot them strongly, plot them with a wink and clap
PyObject *plot3 = PyObject_GetAttrString(axis, "quiver");
if (!plot3) throw std::runtime_error("No 3D line plot");
Expand Down Expand Up @@ -2522,7 +2501,7 @@ inline void set_zlabel(const std::string &str, const std::map<std::string, std::
if (res) Py_DECREF(res);
}

inline void grid(bool flag)
inline void grid(bool flag, const std::map<std::string, std::string>& keywords = std::map<std::string, std::string>())
{
detail::_interpreter::get();

Expand All @@ -2532,10 +2511,16 @@ inline void grid(bool flag)
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, pyflag);

PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args);
PyObject* kwargs = PyDict_New();
for (auto it = keywords.begin(); it != keywords.end(); ++it) {
PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
}

PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_grid, args, kwargs);
if(!res) throw std::runtime_error("Call to grid() failed.");

Py_DECREF(args);
Py_DECREF(kwargs);
Py_DECREF(res);
}

Expand Down Expand Up @@ -2655,7 +2640,7 @@ inline void rcparams(const std::map<std::string, std::string>& keywords = {}) {
PyDict_SetItemString(kwargs, it->first.c_str(), PyLong_FromLong(std::stoi(it->second.c_str())));
else PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
}

PyObject * update = PyObject_GetAttrString(detail::_interpreter::get().s_python_function_rcparams, "update");
PyObject * res = PyObject_Call(update, args, kwargs);
if(!res) throw std::runtime_error("Call to rcParams.update() failed.");
Expand Down Expand Up @@ -2984,3 +2969,5 @@ class Plot
};

} // end namespace matplotlibcpp

#endif //!__MATPLOTLIBCPP__H__