From f2bf7a45112d1d9e41cf97ceb645f106e80f6442 Mon Sep 17 00:00:00 2001 From: Benno Evers Date: Thu, 27 Aug 2020 20:50:30 +0200 Subject: [PATCH 01/24] Add missing call to initialize interpreter in 'modern' example --- matplotlibcpp.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 98f356c..111b904 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -2321,6 +2321,8 @@ struct plot_impl template bool operator()(const IterableX& x, const IterableY& y, const std::string& format) { + detail::_interpreter::get(); + // 2-phase lookup for distance, begin, end using std::distance; using std::begin; From 6f841d442aebbd22f80eb70c9e0d1fbc590c7638 Mon Sep 17 00:00:00 2001 From: Andre Furlan Date: Tue, 26 May 2020 15:53:17 -0300 Subject: [PATCH 02/24] Added support to barh plot method --- matplotlibcpp.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 111b904..c6762db 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -96,6 +96,7 @@ struct _interpreter { PyObject *s_python_function_text; PyObject *s_python_function_suptitle; PyObject *s_python_function_bar; + PyObject *s_python_function_barh; PyObject *s_python_function_colorbar; PyObject *s_python_function_subplots_adjust; @@ -243,6 +244,7 @@ struct _interpreter { s_python_function_text = safe_import(pymod, "text"); s_python_function_suptitle = safe_import(pymod, "suptitle"); s_python_function_bar = safe_import(pymod,"bar"); + s_python_function_barh = safe_import(pymod, "barh"); s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust"); #ifndef WITHOUT_NUMPY @@ -1010,6 +1012,36 @@ bool bar(const std::vector & y, return bar(x, y, ec, ls, lw, keywords); } + +template +bool barh(const std::vector &x, const std::vector &y, std::string ec = "black", std::string ls = "-", double lw = 1.0, const std::map &keywords = { }) { + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + + PyObject *kwargs = PyDict_New(); + + PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); + PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str())); + PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw)); + + for (std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); + } + + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_barh, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; +} + + inline bool subplots_adjust(const std::map& keywords = {}) { detail::_interpreter::get(); From dfe5a69a950251887f8b8754023e7fc23d8da6fc Mon Sep 17 00:00:00 2001 From: Benno Evers Date: Thu, 27 Aug 2020 21:18:02 +0200 Subject: [PATCH 03/24] Fix whitespace error --- matplotlibcpp.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index c6762db..9b414a1 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -1993,10 +1993,10 @@ inline void axvspan(double xmin, double xmax, double ymin = 0., double ymax = 1. PyObject* kwargs = PyDict_New(); for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) { - if (it->first == "linewidth" || it->first == "alpha") - PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(std::stod(it->second))); - else - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + if (it->first == "linewidth" || it->first == "alpha") + PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(std::stod(it->second))); + else + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); } PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvspan, args, kwargs); From b6f58a5c4c0a1430c7ec92e2583b817309650d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=20creepeur=20petit=C3=A8?= Date: Thu, 18 Jun 2020 07:20:46 +0300 Subject: [PATCH 04/24] Suggestion: Give more multithreading flexibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Python interpreter can be constructed by any thread while the destructor is always the main thread. This can lead in some errors with the Python side of things, where some objects want the thread which constructed them to also destroy them. By adding this 'kill' function, a developer can use your library (btw gj! 👍 ) with `std::thread` in order to create a plot async -aka without blocking the main thread- and then close the plot and 'kill' Python interpreter afterwards, without waiting the program to end or having destructor errors. Doing this in order to showcase this more. Small change but not that polished. Let me know about your opinion first and we can fix it 😃. Thank you for your time and this great library ❤️ --- matplotlibcpp.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 9b414a1..714bfd3 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -108,7 +108,17 @@ struct _interpreter { */ static _interpreter& get() { + return interkeeper(false); + } + + static _interpreter& kill() { + return interkeeper(true); + } + + static _interpreter& interkeeper(bool should_kill) { static _interpreter ctx; + if (should_kill) + ctx.~_interpreter(); return ctx; } From 70d508fcb7febc66535ba923eac1b1a4e571e4d1 Mon Sep 17 00:00:00 2001 From: Benno Evers Date: Thu, 27 Aug 2020 21:29:25 +0200 Subject: [PATCH 05/24] Expand comment for new interkeeper() function --- matplotlibcpp.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 714bfd3..93a72be 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -103,8 +103,14 @@ struct _interpreter { /* For now, _interpreter is implemented as a singleton since its currently not possible to have multiple independent embedded python interpreters without patching the python source code - or starting a separate process for each. - http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program + or starting a separate process for each. [1] + Furthermore, many python objects expect that they are destructed in the same thread as they + were constructed. [2] So for advanced usage, a `kill()` function is provided so that library + users can manually ensure that the interpreter is constructed and destroyed within the + same thread. + + 1: http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program + 2: https://github.com/lava/matplotlib-cpp/pull/202#issue-436220256 */ static _interpreter& get() { @@ -115,6 +121,7 @@ struct _interpreter { return interkeeper(true); } + // Stores the actual singleton object referenced by `get()` and `kill()`. static _interpreter& interkeeper(bool should_kill) { static _interpreter ctx; if (should_kill) From 80bc9cd84da8d40f9b52b838291eb91f00c1c73f Mon Sep 17 00:00:00 2001 From: Joshua Brinsfield Date: Sat, 31 Oct 2020 18:51:05 -0400 Subject: [PATCH 06/24] Add 3D quiver support --- matplotlibcpp.h | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 93a72be..52b2780 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -1202,6 +1202,92 @@ bool quiver(const std::vector& x, const std::vector& y, cons return res; } +template +bool quiver(const std::vector& x, const std::vector& y, const std::vector& z, const std::vector& u, const std::vector& w, const std::vector& v, const std::map& keywords = {}) +{ + //set up 3d axes stuff + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + detail::_interpreter::get(); + + PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } + } + + //assert sizes match up + assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size() && x.size() == z.size() && x.size() == v.size() && u.size() == v.size()); + + //set up parameters + detail::_interpreter::get(); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + PyObject* zarray = detail::get_array(z); + PyObject* uarray = detail::get_array(u); + PyObject* warray = detail::get_array(w); + PyObject* varray = detail::get_array(v); + + PyObject* plot_args = PyTuple_New(6); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, zarray); + PyTuple_SetItem(plot_args, 3, uarray); + PyTuple_SetItem(plot_args, 4, warray); + PyTuple_SetItem(plot_args, 5, varray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + 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); + + 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"); + Py_INCREF(plot3); + PyObject* res = PyObject_Call( + plot3, plot_args, kwargs); + if (!res) throw std::runtime_error("Failed 3D plot"); + Py_DECREF(plot3); + Py_DECREF(axis); + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) + Py_DECREF(res); + + return res; +} + template bool stem(const std::vector& x, const std::vector& y, const std::string& s = "") { From 9d19657a36c3950de2848113a546406cad8f8f29 Mon Sep 17 00:00:00 2001 From: Ruan Luies Date: Sun, 17 Jan 2021 13:30:12 +0200 Subject: [PATCH 07/24] Add 3D scatter plots, allow more than one 3d plot on the same figure and make rcparams changeable. --- matplotlibcpp.h | 185 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 167 insertions(+), 18 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 52b2780..226a16a 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -99,6 +99,7 @@ struct _interpreter { PyObject *s_python_function_barh; PyObject *s_python_function_colorbar; PyObject *s_python_function_subplots_adjust; + PyObject *s_python_function_rcparams; /* For now, _interpreter is implemented as a singleton since its currently not possible to have @@ -189,6 +190,7 @@ struct _interpreter { } PyObject* matplotlib = PyImport_Import(matplotlibname); + Py_DECREF(matplotlibname); if (!matplotlib) { PyErr_Print(); @@ -201,6 +203,8 @@ struct _interpreter { PyObject_CallMethod(matplotlib, const_cast("use"), const_cast("s"), s_backend.c_str()); } + + PyObject* pymod = PyImport_Import(pyplotname); Py_DECREF(pyplotname); if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); } @@ -264,6 +268,7 @@ struct _interpreter { s_python_function_barh = safe_import(pymod, "barh"); s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust"); + s_python_function_rcparams = PyObject_GetAttrString(pymod, "rcParams"); #ifndef WITHOUT_NUMPY s_python_function_imshow = safe_import(pymod, "imshow"); #endif @@ -464,6 +469,7 @@ template void plot_surface(const std::vector<::std::vector> &x, const std::vector<::std::vector> &y, const std::vector<::std::vector> &z, + const long fig_number=0, const std::map &keywords = std::map()) { @@ -516,14 +522,29 @@ void plot_surface(const std::vector<::std::vector> &x, for (std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); + if (it->first == "linewidth" || it->first == "alpha") { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyFloat_FromDouble(std::stod(it->second))); + } else { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } } - - PyObject *fig = - PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple); + PyObject *fig_args = PyTuple_New(1); + PyObject* fig = nullptr; + PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); + PyObject *fig_exists = + PyObject_CallObject( + detail::_interpreter::get().s_python_function_fignum_exists, fig_args); + if (!PyObject_IsTrue(fig_exists)) { + fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + fig_args); + } + Py_DECREF(fig_exists); if (!fig) throw std::runtime_error("Call to figure() failed."); PyObject *gca_kwargs = PyDict_New(); @@ -559,6 +580,7 @@ template void plot3(const std::vector &x, const std::vector &y, const std::vector &z, + const long fig_number=0, const std::map &keywords = std::map()) { @@ -607,9 +629,18 @@ void plot3(const std::vector &x, PyString_FromString(it->second.c_str())); } - PyObject *fig = - PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple); + PyObject *fig_args = PyTuple_New(1); + PyObject* fig = nullptr; + PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); + PyObject *fig_exists = + PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, fig_args); + if (!PyObject_IsTrue(fig_exists)) { + fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + fig_args); + } if (!fig) throw std::runtime_error("Call to figure() failed."); PyObject *gca_kwargs = PyDict_New(); @@ -911,6 +942,103 @@ bool scatter(const std::vector& x, return res; } +template +bool scatter(const std::vector& x, + const std::vector& y, + const std::vector& z, + const double s=1.0, // The marker size in points**2 + const long fig_number=0, + const std::map & keywords = {}) { + detail::_interpreter::get(); + + // Same as with plot_surface: We lazily load the modules here the first time + // this function is called because I'm not sure that we can assume "matplotlib + // installed" implies "mpl_toolkits installed" on all platforms, and we don't + // want to require it for people who don't need 3d plots. + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + detail::_interpreter::get(); + + PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } + } + + assert(x.size() == y.size()); + assert(y.size() == z.size()); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *zarray = detail::get_array(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + PyObject *fig_args = PyTuple_New(1); + PyObject* fig = nullptr; + PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); + PyObject *fig_exists = + PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, fig_args); + if (!PyObject_IsTrue(fig_exists)) { + fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + fig_args); + } + 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 *plot3 = PyObject_GetAttrString(axis, "scatter"); + if (!plot3) throw std::runtime_error("No 3D line plot"); + Py_INCREF(plot3); + PyObject *res = PyObject_Call(plot3, args, kwargs); + if (!res) throw std::runtime_error("Failed 3D line plot"); + Py_DECREF(plot3); + + Py_DECREF(axis); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(fig); + if (res) Py_DECREF(res); + return res; + +} + template bool boxplot(const std::vector>& data, const std::vector& labels = {}, @@ -1139,9 +1267,9 @@ bool contour(const std::vector& x, const std::vector& y, const std::map& keywords = {}) { assert(x.size() == y.size() && x.size() == z.size()); - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - PyObject* zarray = get_array(z); + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + PyObject* zarray = detail::get_array(z); PyObject* plot_args = PyTuple_New(3); PyTuple_SetItem(plot_args, 0, xarray); @@ -2094,12 +2222,14 @@ inline void axvspan(double xmin, double xmax, double ymin = 0., double ymax = 1. // construct keyword args PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - if (it->first == "linewidth" || it->first == "alpha") - PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(std::stod(it->second))); - else - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + if (it->first == "linewidth" || it->first == "alpha") { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyFloat_FromDouble(std::stod(it->second))); + } else { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } } PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvspan, args, kwargs); @@ -2319,6 +2449,25 @@ inline void save(const std::string& filename) Py_DECREF(res); } +inline void rcparams(const std::map& keywords = {}) { + detail::_interpreter::get(); + PyObject* args = PyTuple_New(0); + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + if ("text.usetex" == it->first) + 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."); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(update); + Py_DECREF(res); +} + inline void clf() { detail::_interpreter::get(); From d1b7c72be8b9c3cb28bfc565a0975117414be3e5 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 Aug 2020 19:43:50 +0300 Subject: [PATCH 08/24] Add #include for std::stod See https://en.cppreference.com/w/cpp/string/basic_string/stof for details. --- matplotlibcpp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 226a16a..b3d57f7 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -13,6 +13,7 @@ #include #include // requires c++11 support #include +#include // std::stod #ifndef WITHOUT_NUMPY # define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION From 2bf4f26d727cc164e309122fb29f3fb733c087f2 Mon Sep 17 00:00:00 2001 From: William Leong Date: Fri, 2 Oct 2020 15:48:08 +0800 Subject: [PATCH 09/24] Fix #221 and #225 --- matplotlibcpp.h | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index b3d57f7..18d83b8 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -176,7 +176,12 @@ struct _interpreter { wchar_t const *dummy_args[] = {L"Python", NULL}; // const is needed because literals must not be modified wchar_t const **argv = dummy_args; int argc = sizeof(dummy_args)/sizeof(dummy_args[0])-1; + +#if PY_MAJOR_VERSION >= 3 PySys_SetArgv(argc, const_cast(argv)); +#else + PySys_SetArgv(argc, (char **)(argv)); +#endif #ifndef WITHOUT_NUMPY import_numpy(); // initialize numpy C-API @@ -362,7 +367,7 @@ PyObject* get_array(const std::vector& v) PyArray_UpdateFlags(reinterpret_cast(varray), NPY_ARRAY_OWNDATA); return varray; } - + PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data())); return varray; } @@ -429,7 +434,7 @@ PyObject* get_listlist(const std::vector>& ll) } // namespace detail /// Plot a line through the given x and y data points.. -/// +/// /// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html template bool plot(const std::vector &x, const std::vector &y, const std::map& keywords) @@ -587,9 +592,9 @@ void plot3(const std::vector &x, { detail::_interpreter::get(); - // Same as with plot_surface: We lazily load the modules here the first time - // this function is called because I'm not sure that we can assume "matplotlib - // installed" implies "mpl_toolkits installed" on all platforms, and we don't + // Same as with plot_surface: We lazily load the modules here the first time + // this function is called because I'm not sure that we can assume "matplotlib + // installed" implies "mpl_toolkits installed" on all platforms, and we don't // want to require it for people who don't need 3d plots. static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; if (!mpl_toolkitsmod) { @@ -1849,7 +1854,7 @@ inline void legend(const std::map& keywords) if(!res) throw std::runtime_error("Call to legend() failed."); Py_DECREF(kwargs); - Py_DECREF(res); + Py_DECREF(res); } template @@ -2089,7 +2094,7 @@ inline void tick_params(const std::map& keywords, cons inline void subplot(long nrows, long ncols, long plot_number) { detail::_interpreter::get(); - + // construct positional args PyObject* args = PyTuple_New(3); PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); @@ -2154,7 +2159,7 @@ inline void title(const std::string &titlestr, const std::map &keywords = {}) { detail::_interpreter::get(); - + PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str()); PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, pysuptitlestr); @@ -2286,9 +2291,9 @@ inline void set_zlabel(const std::string &str, const std::map Date: Fri, 27 Nov 2020 17:23:34 +0100 Subject: [PATCH 10/24] Adding possibility to choose dpi when saving --- matplotlibcpp.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 18d83b8..a7318f5 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -2439,7 +2439,7 @@ inline void pause(Numeric interval) Py_DECREF(res); } -inline void save(const std::string& filename) +inline void save(const std::string& filename, const int dpi=0) { detail::_interpreter::get(); @@ -2448,10 +2448,18 @@ inline void save(const std::string& filename) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, pyfilename); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args); + PyObject* kwargs = PyDict_New(); + + if(dpi > 0) + { + PyDict_SetItemString(kwargs, "dpi", PyLong_FromLong(dpi)); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_save, args, kwargs); if (!res) throw std::runtime_error("Call to save() failed."); Py_DECREF(args); + Py_DECREF(kwargs); Py_DECREF(res); } From 490fa9cda0b8ce39dbb900836c4fb1c0296ac7c0 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Fri, 19 Feb 2021 14:12:14 +0000 Subject: [PATCH 11/24] Fix memory leaks in xlim() and ylim() Memory is allocated with new and delete is never called. Use a std::array instead, so no memory will be allocated. --- matplotlibcpp.h | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index a7318f5..643a120 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -1896,43 +1896,33 @@ void xlim(Numeric left, Numeric right) } -inline double* xlim() +inline std::array xlim() { - detail::_interpreter::get(); - PyObject* args = PyTuple_New(0); PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); - - double* arr = new double[2]; - arr[0] = PyFloat_AsDouble(left); - arr[1] = PyFloat_AsDouble(right); if(!res) throw std::runtime_error("Call to xlim() failed."); Py_DECREF(res); - return arr; + + PyObject* left = PyTuple_GetItem(res,0); + PyObject* right = PyTuple_GetItem(res,1); + return { PyFloat_AsDouble(left), PyFloat_AsDouble(right) }; } -inline double* ylim() +inline std::array ylim() { - detail::_interpreter::get(); - PyObject* args = PyTuple_New(0); PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); - - double* arr = new double[2]; - arr[0] = PyFloat_AsDouble(left); - arr[1] = PyFloat_AsDouble(right); if(!res) throw std::runtime_error("Call to ylim() failed."); Py_DECREF(res); - return arr; + + PyObject* left = PyTuple_GetItem(res,0); + PyObject* right = PyTuple_GetItem(res,1); + return { PyFloat_AsDouble(left), PyFloat_AsDouble(right) }; } template From 08ff087d5ec3bfb5066335e7300e40e56b09aefb Mon Sep 17 00:00:00 2001 From: Florian Fervers Date: Wed, 10 Feb 2021 17:37:13 +0100 Subject: [PATCH 12/24] Add modern cmake support --- CMakeLists.txt | 125 ++++++++++++++++++++++++++++ cmake/matplotlib_cppConfig.cmake.in | 7 ++ 2 files changed, 132 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/matplotlib_cppConfig.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4e1ef89 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,125 @@ +cmake_minimum_required(VERSION 3.8 FATAL_ERROR) +project(matplotlib_cpp LANGUAGES CXX) + +include(GNUInstallDirs) +set(PACKAGE_NAME matplotlib_cpp) +set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PACKAGE_NAME}/cmake) + + +# Library target +add_library(matplotlib_cpp INTERFACE) +target_include_directories(matplotlib_cpp + INTERFACE + $ + $ +) +target_compile_features(matplotlib_cpp INTERFACE + cxx_std_11 +) +find_package(Python3 COMPONENTS Interpreter Development REQUIRED) +target_link_libraries(matplotlib_cpp INTERFACE + Python3::Python + Python3::Module +) +find_package(Python3 COMPONENTS NumPy) +if(Python3_NumPy_FOUND) + target_link_libraries(matplotlib_cpp INTERFACE + Python3::NumPy + ) +else() + target_compile_definitions(matplotlib_cpp INTERFACE WITHOUT_NUMPY) +endif() +install( + TARGETS matplotlib_cpp + EXPORT install_targets +) + + +# Examples +add_executable(minimal examples/minimal.cpp) +target_link_libraries(minimal PRIVATE matplotlib_cpp) +set_target_properties(minimal PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(basic examples/basic.cpp) +target_link_libraries(basic PRIVATE matplotlib_cpp) +set_target_properties(basic PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(modern examples/modern.cpp) +target_link_libraries(modern PRIVATE matplotlib_cpp) +set_target_properties(modern PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(animation examples/animation.cpp) +target_link_libraries(animation PRIVATE matplotlib_cpp) +set_target_properties(animation PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(nonblock examples/nonblock.cpp) +target_link_libraries(nonblock PRIVATE matplotlib_cpp) +set_target_properties(nonblock PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(xkcd examples/xkcd.cpp) +target_link_libraries(xkcd PRIVATE matplotlib_cpp) +set_target_properties(xkcd PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(bar examples/bar.cpp) +target_link_libraries(bar PRIVATE matplotlib_cpp) +set_target_properties(bar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(fill_inbetween examples/fill_inbetween.cpp) +target_link_libraries(fill_inbetween PRIVATE matplotlib_cpp) +set_target_properties(fill_inbetween PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(fill examples/fill.cpp) +target_link_libraries(fill PRIVATE matplotlib_cpp) +set_target_properties(fill PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(update examples/update.cpp) +target_link_libraries(update PRIVATE matplotlib_cpp) +set_target_properties(update PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(subplot2grid examples/subplot2grid.cpp) +target_link_libraries(subplot2grid PRIVATE matplotlib_cpp) +set_target_properties(subplot2grid PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(lines3d examples/lines3d.cpp) +target_link_libraries(lines3d PRIVATE matplotlib_cpp) +set_target_properties(lines3d PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +if(Python3_NumPy_FOUND) + add_executable(surface examples/surface.cpp) + target_link_libraries(surface PRIVATE matplotlib_cpp) + set_target_properties(surface PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + + add_executable(colorbar examples/colorbar.cpp) + target_link_libraries(colorbar PRIVATE matplotlib_cpp) + set_target_properties(colorbar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +endif() + + +# Install headers +install(FILES + "${PROJECT_SOURCE_DIR}/matplotlibcpp.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + +# Install targets file +install(EXPORT install_targets + FILE + ${PACKAGE_NAME}Targets.cmake + NAMESPACE + ${PACKAGE_NAME}:: + DESTINATION + ${INSTALL_CONFIGDIR} +) + + +# Install matplotlib_cppConfig.cmake +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PACKAGE_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake + INSTALL_DESTINATION ${INSTALL_CONFIGDIR} +) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake + DESTINATION ${INSTALL_CONFIGDIR} +) diff --git a/cmake/matplotlib_cppConfig.cmake.in b/cmake/matplotlib_cppConfig.cmake.in new file mode 100644 index 0000000..1793f29 --- /dev/null +++ b/cmake/matplotlib_cppConfig.cmake.in @@ -0,0 +1,7 @@ +get_filename_component(matplotlib_cpp_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +if(NOT TARGET matplotlib_cpp::matplotlib_cpp) + find_package(Python3 COMPONENTS Interpreter Development REQUIRED) + find_package(Python3 COMPONENTS NumPy) + include("${matplotlib_cpp_CMAKE_DIR}/matplotlib_cppTargets.cmake") +endif() From 3d3f9da65108e17c19799805786bb81e0776ea0f Mon Sep 17 00:00:00 2001 From: Benno Evers Date: Fri, 26 Mar 2021 10:30:33 +0100 Subject: [PATCH 13/24] Unbreak examples by moving 'fig_number' paramter to the end of the list --- matplotlibcpp.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 643a120..9363b71 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -475,9 +475,9 @@ template void plot_surface(const std::vector<::std::vector> &x, const std::vector<::std::vector> &y, const std::vector<::std::vector> &z, - const long fig_number=0, const std::map &keywords = - std::map()) + std::map(), + const long fig_number=0) { detail::_interpreter::get(); @@ -586,9 +586,9 @@ template void plot3(const std::vector &x, const std::vector &y, const std::vector &z, - const long fig_number=0, const std::map &keywords = - std::map()) + std::map(), + const long fig_number=0) { detail::_interpreter::get(); @@ -953,8 +953,8 @@ bool scatter(const std::vector& x, const std::vector& y, const std::vector& z, const double s=1.0, // The marker size in points**2 - const long fig_number=0, - const std::map & keywords = {}) { + const std::map & keywords = {}, + const long fig_number=0) { detail::_interpreter::get(); // Same as with plot_surface: We lazily load the modules here the first time From cab80f33cd137dca50b98fe4ccb830d7e657721d Mon Sep 17 00:00:00 2001 From: Benno Evers Date: Fri, 26 Mar 2021 10:40:39 +0100 Subject: [PATCH 14/24] Remove obsoleted build systems After the switch to cmake, we don't need the hand-written Makefile nor the contrib/ version of the cmake file. --- Makefile | 41 --------------------------------- README.md | 52 +++++++++++++++--------------------------- contrib/CMakeLists.txt | 26 --------------------- numpy_flags.py | 12 ---------- 4 files changed, 18 insertions(+), 113 deletions(-) delete mode 100644 Makefile delete mode 100644 contrib/CMakeLists.txt delete mode 100644 numpy_flags.py diff --git a/Makefile b/Makefile deleted file mode 100644 index 67b5ac3..0000000 --- a/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -# Use C++11, dont warn on long-to-float conversion -CXXFLAGS += -std=c++11 -Wno-conversion - -# Default to using system's default version of python -PYTHON_BIN ?= python3 -PYTHON_CONFIG := $(PYTHON_BIN)-config -PYTHON_INCLUDE ?= $(shell $(PYTHON_CONFIG) --includes) -EXTRA_FLAGS := $(PYTHON_INCLUDE) -# NOTE: Since python3.8, the correct invocation is `python3-config --libs --embed`. -# So of course the proper way to get python libs for embedding now is to -# invoke that, check if it crashes, and fall back to just `--libs` if it does. -LDFLAGS += $(shell if $(PYTHON_CONFIG) --ldflags --embed >/dev/null; then $(PYTHON_CONFIG) --ldflags --embed; else $(PYTHON_CONFIG) --ldflags; fi) - -# Either finds numpy or set -DWITHOUT_NUMPY -EXTRA_FLAGS += $(shell $(PYTHON_BIN) $(CURDIR)/numpy_flags.py) -WITHOUT_NUMPY := $(findstring $(EXTRA_FLAGS), WITHOUT_NUMPY) - -# Examples requiring numpy support to compile -EXAMPLES_NUMPY := surface colorbar -EXAMPLES := minimal basic modern animation nonblock xkcd quiver bar \ - fill_inbetween fill update subplot2grid lines3d \ - $(if $(WITHOUT_NUMPY),,$(EXAMPLES_NUMPY)) - -# Prefix every example with 'examples/build/' -EXAMPLE_TARGETS := $(patsubst %,examples/build/%,$(EXAMPLES)) - -.PHONY: examples - -examples: $(EXAMPLE_TARGETS) - -docs: - doxygen - moxygen doc/xml --noindex -o doc/api.md - -# Assume every *.cpp file is a separate example -$(EXAMPLE_TARGETS): examples/build/%: examples/%.cpp matplotlibcpp.h - mkdir -p examples/build - $(CXX) -o $@ $< $(EXTRA_FLAGS) $(CXXFLAGS) $(LDFLAGS) - -clean: - rm -f ${EXAMPLE_TARGETS} diff --git a/README.md b/README.md index 61ffef4..0f8479f 100644 --- a/README.md +++ b/README.md @@ -202,39 +202,34 @@ If, for some reason, you're unable to get a working installation of numpy on you you can define the macro `WITHOUT_NUMPY` before including the header file to erase this dependency. -The C++-part of the library consists of the single header file `matplotlibcpp.h` which can be placed -anywhere. +The C++-part of the library consists of the single header file `matplotlibcpp.h` which +can be placed anywhere. -Since a python interpreter is opened internally, it is necessary to link against `libpython` in order -to user matplotlib-cpp. Most versions should work, although `libpython2.7` and `libpython3.6` are -probably the most regularly testedr. +Since a python interpreter is opened internally, it is necessary to link +against `libpython` in order to user matplotlib-cpp. Most versions should +work, although python likes to randomly break compatibility from time to time +so some caution is advised when using the bleeding edge. # CMake -If you prefer to use CMake as build system, you will want to add something like this to your -CMakeLists.txt: +The C++ code is compatible to both python2 and python3. However, the `CMakeLists.txt` +file is currently set up to use python3 by default, so if python2 is required this +has to be changed manually. (a PR that adds a cmake option for this would be highly +welcomed) -**Recommended way (since CMake 3.12):** +**NOTE**: By design (of python), only a single python interpreter can be created per +process. When using this library, *no other* library that is spawning a python +interpreter internally can be used. -It's easy to use cmake official [docs](https://cmake.org/cmake/help/git-stage/module/FindPython2.html#module:FindPython2) to find Python 2(or 3) interpreter, compiler and development environment (include directories and libraries). +To compile the code without using cmake, the compiler invocation should look like +this: -NumPy is optional here, delete it from cmake script, if you don't need it. + g++ example.cpp -I/usr/include/python2.7 -lpython2.7 -```cmake -find_package(Python2 COMPONENTS Development NumPy) -target_include_directories(myproject PRIVATE ${Python2_INCLUDE_DIRS} ${Python2_NumPy_INCLUDE_DIRS}) -target_link_libraries(myproject Python2::Python Python2::NumPy) -``` - -**Alternative way (for CMake <= 3.11):** - -```cmake -find_package(PythonLibs 2.7) -target_include_directories(myproject PRIVATE ${PYTHON_INCLUDE_DIRS}) -target_link_libraries(myproject ${PYTHON_LIBRARIES}) -``` +This can also be used for linking against a custom build of python + g++ example.cpp -I/usr/local/include/fancy-python4 -L/usr/local/lib -lfancy-python4 # Vcpkg @@ -258,17 +253,6 @@ Note that support for c++98 was dropped more or less accidentally, so if you hav with an ancient compiler and still want to enjoy the latest additional features, I'd probably merge a PR that restores support. -# Python 3 - -This library supports both python2 and python3 (although the python3 support is probably far less tested, -so it is recommended to prefer python2.7). To switch the used python version, simply change -the compiler flags accordingly. - - g++ example.cpp -I/usr/include/python3.6 -lpython3.6 - -The same technique can be used for linking against a custom build of python - - g++ example.cpp -I/usr/local/include/fancy-python4 -L/usr/local/lib -lfancy-python4 Why? diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt deleted file mode 100644 index edb40b1..0000000 --- a/contrib/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.7) -project (MatplotlibCPP_Test) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -include_directories(${PYTHONHOME}/include) -include_directories(${PYTHONHOME}/Lib/site-packages/numpy/core/include) -link_directories(${PYTHONHOME}/libs) - -add_definitions(-DMATPLOTLIBCPP_PYTHON_HEADER=Python.h) - -# message(STATUS "*** dump start cmake variables ***") -# get_cmake_property(_variableNames VARIABLES) -# foreach(_variableName ${_variableNames}) -# message(STATUS "${_variableName}=${${_variableName}}") -# endforeach() -# message(STATUS "*** dump end ***") - -add_executable(minimal ${CMAKE_CURRENT_SOURCE_DIR}/../examples/minimal.cpp) -add_executable(basic ${CMAKE_CURRENT_SOURCE_DIR}/../examples/basic.cpp) -add_executable(modern ${CMAKE_CURRENT_SOURCE_DIR}/../examples/modern.cpp) -add_executable(animation ${CMAKE_CURRENT_SOURCE_DIR}/../examples/animation.cpp) -add_executable(nonblock ${CMAKE_CURRENT_SOURCE_DIR}/../examples/nonblock.cpp) -add_executable(xkcd ${CMAKE_CURRENT_SOURCE_DIR}/../examples/xkcd.cpp) -add_executable(bar ${CMAKE_CURRENT_SOURCE_DIR}/../examples/bar.cpp) diff --git a/numpy_flags.py b/numpy_flags.py deleted file mode 100644 index 56fd95c..0000000 --- a/numpy_flags.py +++ /dev/null @@ -1,12 +0,0 @@ -from os import path - -try: - from numpy import __file__ as numpyloc - - # Get numpy directory - numpy_dir = path.dirname(numpyloc) - - # Print the result of joining this to core and include - print("-I" + path.join(numpy_dir, "core", "include")) -except: - print("-DWITHOUT_NUMPY") From 1875696c55c1ae0ae93546f738fbf0047ffa6190 Mon Sep 17 00:00:00 2001 From: Pierre Narvor Date: Wed, 31 Mar 2021 15:16:49 +0200 Subject: [PATCH 15/24] Added 'set_aspect' and set_aspect_equal function --- examples/modern.cpp | 3 +++ matplotlibcpp.h | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/examples/modern.cpp b/examples/modern.cpp index a8aa0c7..871ef2b 100644 --- a/examples/modern.cpp +++ b/examples/modern.cpp @@ -24,6 +24,9 @@ int main() // y must either be callable (providing operator() const) or iterable. plt::plot(x, y, "r-", x, [](double d) { return 12.5+abs(sin(d)); }, "k-"); + //plt::set_aspect(0.5); + plt::set_aspect_equal(); + // show plots plt::show(); diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 9363b71..a151d6f 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -1857,6 +1857,62 @@ inline void legend(const std::map& keywords) Py_DECREF(res); } +template +inline void set_aspect(Numeric ratio) +{ + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(ratio)); + PyObject* kwargs = PyDict_New(); + + PyObject *ax = + PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, + detail::_interpreter::get().s_python_empty_tuple); + if (!ax) throw std::runtime_error("Call to gca() failed."); + Py_INCREF(ax); + + PyObject *set_aspect = PyObject_GetAttrString(ax, "set_aspect"); + if (!set_aspect) throw std::runtime_error("Attribute set_aspect not found."); + Py_INCREF(set_aspect); + + PyObject *res = PyObject_Call(set_aspect, args, kwargs); + if (!res) throw std::runtime_error("Call to set_aspect() failed."); + Py_DECREF(set_aspect); + + Py_DECREF(ax); + Py_DECREF(args); + Py_DECREF(kwargs); +} + +inline void set_aspect_equal() +{ + // expect ratio == "equal". Leaving error handling to matplotlib. + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyString_FromString("equal")); + PyObject* kwargs = PyDict_New(); + + PyObject *ax = + PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, + detail::_interpreter::get().s_python_empty_tuple); + if (!ax) throw std::runtime_error("Call to gca() failed."); + Py_INCREF(ax); + + PyObject *set_aspect = PyObject_GetAttrString(ax, "set_aspect"); + if (!set_aspect) throw std::runtime_error("Attribute set_aspect not found."); + Py_INCREF(set_aspect); + + PyObject *res = PyObject_Call(set_aspect, args, kwargs); + if (!res) throw std::runtime_error("Call to set_aspect() failed."); + Py_DECREF(set_aspect); + + Py_DECREF(ax); + Py_DECREF(args); + Py_DECREF(kwargs); +} + template void ylim(Numeric left, Numeric right) { From ec3745302a8295e3d7b28f910fb1a39936886a40 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Sat, 23 May 2020 17:10:42 +0200 Subject: [PATCH 16/24] add axhline --- matplotlibcpp.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index a151d6f..f6365cb 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -75,6 +75,7 @@ struct _interpreter { PyObject *s_python_function_ylim; PyObject *s_python_function_title; PyObject *s_python_function_axis; + PyObject *s_python_function_axhline; PyObject *s_python_function_axvline; PyObject *s_python_function_axvspan; PyObject *s_python_function_xlabel; @@ -247,6 +248,7 @@ struct _interpreter { s_python_function_ylim = safe_import(pymod, "ylim"); s_python_function_title = safe_import(pymod, "title"); s_python_function_axis = safe_import(pymod, "axis"); + s_python_function_axhline = safe_import(pymod, "axhline"); s_python_function_axvline = safe_import(pymod, "axvline"); s_python_function_axvspan = safe_import(pymod, "axvspan"); s_python_function_xlabel = safe_import(pymod, "xlabel"); @@ -2238,6 +2240,31 @@ inline void axis(const std::string &axisstr) Py_DECREF(res); } +inline void axhline(double y, double xmin = 0., double xmax = 1., const std::map& keywords = std::map()) +{ + detail::_interpreter::get(); + + // construct positional args + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(y)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmin)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(xmax)); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axhline, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if(res) Py_DECREF(res); +} + inline void axvline(double x, double ymin = 0., double ymax = 1., const std::map& keywords = std::map()) { detail::_interpreter::get(); From bbfb240ba80e9cb890a0c4dc259552ee8b235854 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Sat, 23 May 2020 17:34:06 +0200 Subject: [PATCH 17/24] add contour plot --- examples/contour.cpp | 24 ++++++++++++++++++++ matplotlibcpp.h | 54 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 examples/contour.cpp diff --git a/examples/contour.cpp b/examples/contour.cpp new file mode 100644 index 0000000..9289d0a --- /dev/null +++ b/examples/contour.cpp @@ -0,0 +1,24 @@ +#include "../matplotlibcpp.h" + +#include + +namespace plt = matplotlibcpp; + +int main() +{ + std::vector> x, y, z; + for (double i = -5; i <= 5; i += 0.25) { + std::vector x_row, y_row, z_row; + for (double j = -5; j <= 5; j += 0.25) { + x_row.push_back(i); + y_row.push_back(j); + z_row.push_back(::std::sin(::std::hypot(i, j))); + } + x.push_back(x_row); + y.push_back(y_row); + z.push_back(z_row); + } + + plt::contour(x, y, z); + plt::show(); +} diff --git a/matplotlibcpp.h b/matplotlibcpp.h index f6365cb..abf0284 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -103,7 +103,6 @@ struct _interpreter { PyObject *s_python_function_subplots_adjust; PyObject *s_python_function_rcparams; - /* For now, _interpreter is implemented as a singleton since its currently not possible to have multiple independent embedded python interpreters without patching the python source code or starting a separate process for each. [1] @@ -245,6 +244,7 @@ struct _interpreter { s_python_function_subplot = safe_import(pymod, "subplot"); s_python_function_subplot2grid = safe_import(pymod, "subplot2grid"); s_python_function_legend = safe_import(pymod, "legend"); + s_python_function_xlim = safe_import(pymod, "xlim"); s_python_function_ylim = safe_import(pymod, "ylim"); s_python_function_title = safe_import(pymod, "title"); s_python_function_axis = safe_import(pymod, "axis"); @@ -259,7 +259,6 @@ struct _interpreter { s_python_function_margins = safe_import(pymod, "margins"); s_python_function_tick_params = safe_import(pymod, "tick_params"); s_python_function_grid = safe_import(pymod, "grid"); - s_python_function_xlim = safe_import(pymod, "xlim"); s_python_function_ion = safe_import(pymod, "ion"); s_python_function_ginput = safe_import(pymod, "ginput"); s_python_function_save = safe_import(pylabmod, "savefig"); @@ -349,10 +348,10 @@ template <> struct select_npy_type { 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 { const static NPY_TYPES type = NPY_INT64; }; -static_assert(sizeof(unsigned long long) == 8); -template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; +// static_assert(sizeof(long long) == 8); +// template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; +// static_assert(sizeof(unsigned long long) == 8); +// template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; // TODO: add int, long, etc. template @@ -582,6 +581,49 @@ void plot_surface(const std::vector<::std::vector> &x, Py_DECREF(kwargs); if (res) Py_DECREF(res); } + +template +void contour(const std::vector<::std::vector> &x, + const std::vector<::std::vector> &y, + const std::vector<::std::vector> &z, + const std::map &keywords = {}) +{ + detail::_interpreter::get(); + + // using numpy arrays + PyObject *xarray = detail::get_2darray(x); + PyObject *yarray = detail::get_2darray(y); + PyObject *zarray = detail::get_2darray(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + + PyObject *python_colormap_coolwarm = PyObject_GetAttrString( + detail::_interpreter::get().s_python_colormap, "coolwarm"); + + PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_contour, args, kwargs); + if (!res) + throw std::runtime_error("failed contour"); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); +} #endif // WITHOUT_NUMPY template From 9ff7a4b29db0ef27da0136a9e6930d385b93fe50 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Sat, 23 May 2020 18:40:22 +0200 Subject: [PATCH 18/24] add spy --- examples/spy.cpp | 29 +++++++++++++++++++++++++++++ matplotlibcpp.h | 45 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 examples/spy.cpp diff --git a/examples/spy.cpp b/examples/spy.cpp new file mode 100644 index 0000000..80bd544 --- /dev/null +++ b/examples/spy.cpp @@ -0,0 +1,29 @@ +#import +#import +#import "../matplotlibcpp.h" + +namespace plt = matplotlibcpp; + +int main() +{ + const int n = 20; + std::vector> matrix; + + for (int i = 0; i < n; ++i) { + std::vector row; + for (int j = 0; j < n; ++j) { + if (i == j) + row.push_back(-2); + else if (j == i - 1 || j == i + 1) + row.push_back(1); + else + row.push_back(0); + } + matrix.push_back(row); + } + + plt::spy(matrix, 5, {{"marker", "o"}}); + plt::show(); + + return 0; +} \ No newline at end of file diff --git a/matplotlibcpp.h b/matplotlibcpp.h index abf0284..9ad7faa 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -102,6 +102,7 @@ struct _interpreter { PyObject *s_python_function_colorbar; PyObject *s_python_function_subplots_adjust; PyObject *s_python_function_rcparams; + PyObject *s_python_function_spy; /* For now, _interpreter is implemented as a singleton since its currently not possible to have multiple independent embedded python interpreters without patching the python source code @@ -276,6 +277,7 @@ struct _interpreter { s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust"); s_python_function_rcparams = PyObject_GetAttrString(pymod, "rcParams"); + s_python_function_spy = PyObject_GetAttrString(pymod, "spy"); #ifndef WITHOUT_NUMPY s_python_function_imshow = safe_import(pymod, "imshow"); #endif @@ -348,11 +350,11 @@ template <> struct select_npy_type { 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 { const static NPY_TYPES type = NPY_INT64; }; -// static_assert(sizeof(unsigned long long) == 8); -// template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; -// TODO: add int, long, etc. +static_assert(sizeof(long long) == 8); +template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; +static_assert(sizeof(unsigned long long) == 8); +template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; +TODO: add int, long, etc. template PyObject* get_array(const std::vector& v) @@ -621,8 +623,37 @@ void contour(const std::vector<::std::vector> &x, Py_DECREF(args); Py_DECREF(kwargs); - if (res) - Py_DECREF(res); + if (res) Py_DECREF(res); +} + +template +void spy(const std::vector<::std::vector> &x, + const double markersize = -1, // -1 for default matplotlib size + const std::map &keywords = {}) +{ + detail::_interpreter::get(); + + PyObject *xarray = detail::get_2darray(x); + + PyObject *kwargs = PyDict_New(); + if (markersize != -1) { + PyDict_SetItemString(kwargs, "markersize", PyFloat_FromDouble(markersize)); + } + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *plot_args = PyTuple_New(1); + PyTuple_SetItem(plot_args, 0, xarray); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_spy, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); } #endif // WITHOUT_NUMPY From 630ba843d5c92309279d9c10798be169a20b75a5 Mon Sep 17 00:00:00 2001 From: Benno Evers Date: Fri, 2 Apr 2021 21:57:56 +0200 Subject: [PATCH 19/24] Some bugfixes and adjustments for contour/spy --- CMakeLists.txt | 8 ++++++++ examples/spy.cpp | 9 +++++---- matplotlibcpp.h | 1 - 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e1ef89..9353473 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ target_include_directories(matplotlib_cpp target_compile_features(matplotlib_cpp INTERFACE cxx_std_11 ) +# TODO: Use `Development.Embed` component when requiring cmake >= 3.18 find_package(Python3 COMPONENTS Interpreter Development REQUIRED) target_link_libraries(matplotlib_cpp INTERFACE Python3::Python @@ -92,6 +93,13 @@ if(Python3_NumPy_FOUND) add_executable(colorbar examples/colorbar.cpp) target_link_libraries(colorbar PRIVATE matplotlib_cpp) set_target_properties(colorbar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + add_executable(contour examples/contour.cpp) + target_link_libraries(contour PRIVATE matplotlib_cpp) + set_target_properties(contour PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + + add_executable(spy examples/spy.cpp) + target_link_libraries(spy PRIVATE matplotlib_cpp) + set_target_properties(spy PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") endif() diff --git a/examples/spy.cpp b/examples/spy.cpp index 80bd544..6027a48 100644 --- a/examples/spy.cpp +++ b/examples/spy.cpp @@ -1,6 +1,7 @@ -#import -#import -#import "../matplotlibcpp.h" +#include "../matplotlibcpp.h" + +#include +#include namespace plt = matplotlibcpp; @@ -26,4 +27,4 @@ int main() plt::show(); return 0; -} \ No newline at end of file +} diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 9ad7faa..e6f64e7 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -354,7 +354,6 @@ static_assert(sizeof(long long) == 8); template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; static_assert(sizeof(unsigned long long) == 8); template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; -TODO: add int, long, etc. template PyObject* get_array(const std::vector& v) From 5b88e8b98f630f305e4ea09bb975cb9a9c0cd513 Mon Sep 17 00:00:00 2001 From: Benno Evers Date: Fri, 2 Apr 2021 22:07:08 +0200 Subject: [PATCH 20/24] Change line endings in CMakeLists.txt --- CMakeLists.txt | 266 ++++++++++++++++++++++++------------------------- 1 file changed, 133 insertions(+), 133 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9353473..bb2decd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,133 +1,133 @@ -cmake_minimum_required(VERSION 3.8 FATAL_ERROR) -project(matplotlib_cpp LANGUAGES CXX) - -include(GNUInstallDirs) -set(PACKAGE_NAME matplotlib_cpp) -set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PACKAGE_NAME}/cmake) - - -# Library target -add_library(matplotlib_cpp INTERFACE) -target_include_directories(matplotlib_cpp - INTERFACE - $ - $ -) -target_compile_features(matplotlib_cpp INTERFACE - cxx_std_11 -) -# TODO: Use `Development.Embed` component when requiring cmake >= 3.18 -find_package(Python3 COMPONENTS Interpreter Development REQUIRED) -target_link_libraries(matplotlib_cpp INTERFACE - Python3::Python - Python3::Module -) -find_package(Python3 COMPONENTS NumPy) -if(Python3_NumPy_FOUND) - target_link_libraries(matplotlib_cpp INTERFACE - Python3::NumPy - ) -else() - target_compile_definitions(matplotlib_cpp INTERFACE WITHOUT_NUMPY) -endif() -install( - TARGETS matplotlib_cpp - EXPORT install_targets -) - - -# Examples -add_executable(minimal examples/minimal.cpp) -target_link_libraries(minimal PRIVATE matplotlib_cpp) -set_target_properties(minimal PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(basic examples/basic.cpp) -target_link_libraries(basic PRIVATE matplotlib_cpp) -set_target_properties(basic PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(modern examples/modern.cpp) -target_link_libraries(modern PRIVATE matplotlib_cpp) -set_target_properties(modern PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(animation examples/animation.cpp) -target_link_libraries(animation PRIVATE matplotlib_cpp) -set_target_properties(animation PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(nonblock examples/nonblock.cpp) -target_link_libraries(nonblock PRIVATE matplotlib_cpp) -set_target_properties(nonblock PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(xkcd examples/xkcd.cpp) -target_link_libraries(xkcd PRIVATE matplotlib_cpp) -set_target_properties(xkcd PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(bar examples/bar.cpp) -target_link_libraries(bar PRIVATE matplotlib_cpp) -set_target_properties(bar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(fill_inbetween examples/fill_inbetween.cpp) -target_link_libraries(fill_inbetween PRIVATE matplotlib_cpp) -set_target_properties(fill_inbetween PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(fill examples/fill.cpp) -target_link_libraries(fill PRIVATE matplotlib_cpp) -set_target_properties(fill PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(update examples/update.cpp) -target_link_libraries(update PRIVATE matplotlib_cpp) -set_target_properties(update PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(subplot2grid examples/subplot2grid.cpp) -target_link_libraries(subplot2grid PRIVATE matplotlib_cpp) -set_target_properties(subplot2grid PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(lines3d examples/lines3d.cpp) -target_link_libraries(lines3d PRIVATE matplotlib_cpp) -set_target_properties(lines3d PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -if(Python3_NumPy_FOUND) - add_executable(surface examples/surface.cpp) - target_link_libraries(surface PRIVATE matplotlib_cpp) - set_target_properties(surface PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - - add_executable(colorbar examples/colorbar.cpp) - target_link_libraries(colorbar PRIVATE matplotlib_cpp) - set_target_properties(colorbar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - add_executable(contour examples/contour.cpp) - target_link_libraries(contour PRIVATE matplotlib_cpp) - set_target_properties(contour PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - - add_executable(spy examples/spy.cpp) - target_link_libraries(spy PRIVATE matplotlib_cpp) - set_target_properties(spy PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") -endif() - - -# Install headers -install(FILES - "${PROJECT_SOURCE_DIR}/matplotlibcpp.h" - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - -# Install targets file -install(EXPORT install_targets - FILE - ${PACKAGE_NAME}Targets.cmake - NAMESPACE - ${PACKAGE_NAME}:: - DESTINATION - ${INSTALL_CONFIGDIR} -) - - -# Install matplotlib_cppConfig.cmake -include(CMakePackageConfigHelpers) -configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PACKAGE_NAME}Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake - INSTALL_DESTINATION ${INSTALL_CONFIGDIR} -) -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake - DESTINATION ${INSTALL_CONFIGDIR} -) +cmake_minimum_required(VERSION 3.8 FATAL_ERROR) +project(matplotlib_cpp LANGUAGES CXX) + +include(GNUInstallDirs) +set(PACKAGE_NAME matplotlib_cpp) +set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PACKAGE_NAME}/cmake) + + +# Library target +add_library(matplotlib_cpp INTERFACE) +target_include_directories(matplotlib_cpp + INTERFACE + $ + $ +) +target_compile_features(matplotlib_cpp INTERFACE + cxx_std_11 +) +# TODO: Use `Development.Embed` component when requiring cmake >= 3.18 +find_package(Python3 COMPONENTS Interpreter Development REQUIRED) +target_link_libraries(matplotlib_cpp INTERFACE + Python3::Python + Python3::Module +) +find_package(Python3 COMPONENTS NumPy) +if(Python3_NumPy_FOUND) + target_link_libraries(matplotlib_cpp INTERFACE + Python3::NumPy + ) +else() + target_compile_definitions(matplotlib_cpp INTERFACE WITHOUT_NUMPY) +endif() +install( + TARGETS matplotlib_cpp + EXPORT install_targets +) + + +# Examples +add_executable(minimal examples/minimal.cpp) +target_link_libraries(minimal PRIVATE matplotlib_cpp) +set_target_properties(minimal PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(basic examples/basic.cpp) +target_link_libraries(basic PRIVATE matplotlib_cpp) +set_target_properties(basic PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(modern examples/modern.cpp) +target_link_libraries(modern PRIVATE matplotlib_cpp) +set_target_properties(modern PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(animation examples/animation.cpp) +target_link_libraries(animation PRIVATE matplotlib_cpp) +set_target_properties(animation PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(nonblock examples/nonblock.cpp) +target_link_libraries(nonblock PRIVATE matplotlib_cpp) +set_target_properties(nonblock PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(xkcd examples/xkcd.cpp) +target_link_libraries(xkcd PRIVATE matplotlib_cpp) +set_target_properties(xkcd PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(bar examples/bar.cpp) +target_link_libraries(bar PRIVATE matplotlib_cpp) +set_target_properties(bar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(fill_inbetween examples/fill_inbetween.cpp) +target_link_libraries(fill_inbetween PRIVATE matplotlib_cpp) +set_target_properties(fill_inbetween PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(fill examples/fill.cpp) +target_link_libraries(fill PRIVATE matplotlib_cpp) +set_target_properties(fill PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(update examples/update.cpp) +target_link_libraries(update PRIVATE matplotlib_cpp) +set_target_properties(update PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(subplot2grid examples/subplot2grid.cpp) +target_link_libraries(subplot2grid PRIVATE matplotlib_cpp) +set_target_properties(subplot2grid PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +add_executable(lines3d examples/lines3d.cpp) +target_link_libraries(lines3d PRIVATE matplotlib_cpp) +set_target_properties(lines3d PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +if(Python3_NumPy_FOUND) + add_executable(surface examples/surface.cpp) + target_link_libraries(surface PRIVATE matplotlib_cpp) + set_target_properties(surface PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + + add_executable(colorbar examples/colorbar.cpp) + target_link_libraries(colorbar PRIVATE matplotlib_cpp) + set_target_properties(colorbar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + add_executable(contour examples/contour.cpp) + target_link_libraries(contour PRIVATE matplotlib_cpp) + set_target_properties(contour PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + + add_executable(spy examples/spy.cpp) + target_link_libraries(spy PRIVATE matplotlib_cpp) + set_target_properties(spy PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +endif() + + +# Install headers +install(FILES + "${PROJECT_SOURCE_DIR}/matplotlibcpp.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + +# Install targets file +install(EXPORT install_targets + FILE + ${PACKAGE_NAME}Targets.cmake + NAMESPACE + ${PACKAGE_NAME}:: + DESTINATION + ${INSTALL_CONFIGDIR} +) + + +# Install matplotlib_cppConfig.cmake +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PACKAGE_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake + INSTALL_DESTINATION ${INSTALL_CONFIGDIR} +) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake + DESTINATION ${INSTALL_CONFIGDIR} +) From 14807809928ffec468b41a2ebe720d5187e3deb7 Mon Sep 17 00:00:00 2001 From: SpiritSeeker Date: Fri, 18 Sep 2020 17:22:46 +0530 Subject: [PATCH 21/24] Updated named plots to infer different datatypes in case of x and y inputs. --- matplotlibcpp.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index e6f64e7..fcd7c8a 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -1661,8 +1661,8 @@ bool named_plot(const std::string& name, const std::vector& y, const st return res; } -template -bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +template +bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { detail::_interpreter::get(); @@ -1688,8 +1688,8 @@ bool named_plot(const std::string& name, const std::vector& x, const st return res; } -template -bool named_semilogx(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +template +bool named_semilogx(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { detail::_interpreter::get(); @@ -1715,8 +1715,8 @@ bool named_semilogx(const std::string& name, const std::vector& x, cons return res; } -template -bool named_semilogy(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +template +bool named_semilogy(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { detail::_interpreter::get(); @@ -1742,8 +1742,8 @@ bool named_semilogy(const std::string& name, const std::vector& x, cons return res; } -template -bool named_loglog(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") +template +bool named_loglog(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { detail::_interpreter::get(); From 61501081ea32549df1a02dca26cb4edbe0b6a890 Mon Sep 17 00:00:00 2001 From: kesha787898 <45235408+kesha787898@users.noreply.github.com> Date: Sun, 28 Feb 2021 00:00:30 +0600 Subject: [PATCH 22/24] added scatter with colors --- matplotlibcpp.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/matplotlibcpp.h b/matplotlibcpp.h index fcd7c8a..d95d46a 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -1022,6 +1022,44 @@ bool scatter(const std::vector& x, return res; } +template + bool scatter_colored(const std::vector& x, + const std::vector& y, + const std::vector& colors, + const double s=1.0, // The marker size in points**2 + const std::map & keywords = {}) + { + detail::_interpreter::get(); + + assert(x.size() == y.size()); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::get_array(y); + PyObject* colors_array = detail::get_array(colors); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); + PyDict_SetItemString(kwargs, "c", colors_array); + + for (const auto& it : keywords) + { + PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); + } + + PyObject* plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; + } + + template bool scatter(const std::vector& x, const std::vector& y, From 3dda5267e5f76f86a7888221df6151f2f09c93c6 Mon Sep 17 00:00:00 2001 From: Kenta Yonekura Date: Tue, 13 Apr 2021 11:20:27 +0900 Subject: [PATCH 23/24] Fix a missing M_PI in windows environment --- examples/lines3d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lines3d.cpp b/examples/lines3d.cpp index f3c201c..fd4610d 100644 --- a/examples/lines3d.cpp +++ b/examples/lines3d.cpp @@ -1,5 +1,5 @@ +#define _USE_MATH_DEFINES #include "../matplotlibcpp.h" - #include namespace plt = matplotlibcpp; From ef0383f1315d32e0156335e10b82e90b334f6d9f Mon Sep 17 00:00:00 2001 From: Kenta Yonekura Date: Tue, 13 Apr 2021 14:36:10 +0900 Subject: [PATCH 24/24] Enable cmake include definition --- cmake/matplotlib_cppConfig.cmake.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/matplotlib_cppConfig.cmake.in b/cmake/matplotlib_cppConfig.cmake.in index 1793f29..86d25d0 100644 --- a/cmake/matplotlib_cppConfig.cmake.in +++ b/cmake/matplotlib_cppConfig.cmake.in @@ -4,4 +4,7 @@ if(NOT TARGET matplotlib_cpp::matplotlib_cpp) find_package(Python3 COMPONENTS Interpreter Development REQUIRED) find_package(Python3 COMPONENTS NumPy) include("${matplotlib_cpp_CMAKE_DIR}/matplotlib_cppTargets.cmake") + + get_target_property(matplotlib_cpp_INCLUDE_DIRS matplotlib_cpp::matplotlib_cpp INTERFACE_INCLUDE_DIRECTORIES) + endif()