diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..bb2decd8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +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} +) diff --git a/Makefile b/Makefile deleted file mode 100644 index 67b5ac35..00000000 --- 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 61ffef48..0f8479f1 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/cmake/matplotlib_cppConfig.cmake.in b/cmake/matplotlib_cppConfig.cmake.in new file mode 100644 index 00000000..86d25d09 --- /dev/null +++ b/cmake/matplotlib_cppConfig.cmake.in @@ -0,0 +1,10 @@ +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") + + get_target_property(matplotlib_cpp_INCLUDE_DIRS matplotlib_cpp::matplotlib_cpp INTERFACE_INCLUDE_DIRECTORIES) + +endif() diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt deleted file mode 100644 index edb40b11..00000000 --- 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/examples/contour.cpp b/examples/contour.cpp new file mode 100644 index 00000000..9289d0a0 --- /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/examples/lines3d.cpp b/examples/lines3d.cpp index f3c201c5..fd4610d2 100644 --- a/examples/lines3d.cpp +++ b/examples/lines3d.cpp @@ -1,5 +1,5 @@ +#define _USE_MATH_DEFINES #include "../matplotlibcpp.h" - #include namespace plt = matplotlibcpp; diff --git a/examples/modern.cpp b/examples/modern.cpp index a8aa0c75..871ef2b0 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/examples/spy.cpp b/examples/spy.cpp new file mode 100644 index 00000000..6027a487 --- /dev/null +++ b/examples/spy.cpp @@ -0,0 +1,30 @@ +#include "../matplotlibcpp.h" + +#include +#include + +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; +} diff --git a/matplotlibcpp.h b/matplotlibcpp.h index 93a72be5..d95d46ad 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 @@ -74,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; @@ -99,7 +101,8 @@ struct _interpreter { PyObject *s_python_function_barh; 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 @@ -174,7 +177,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 @@ -189,6 +197,7 @@ struct _interpreter { } PyObject* matplotlib = PyImport_Import(matplotlibname); + Py_DECREF(matplotlibname); if (!matplotlib) { PyErr_Print(); @@ -201,6 +210,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!"); } @@ -234,9 +245,11 @@ 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"); + 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"); @@ -247,7 +260,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"); @@ -264,6 +276,8 @@ 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"); + s_python_function_spy = PyObject_GetAttrString(pymod, "spy"); #ifndef WITHOUT_NUMPY s_python_function_imshow = safe_import(pymod, "imshow"); #endif @@ -340,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) @@ -356,7 +369,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; } @@ -423,7 +436,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) @@ -465,7 +478,8 @@ void plot_surface(const std::vector<::std::vector> &x, const std::vector<::std::vector> &y, const std::vector<::std::vector> &z, const std::map &keywords = - std::map()) + std::map(), + const long fig_number=0) { detail::_interpreter::get(); @@ -516,14 +530,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(); @@ -553,6 +582,78 @@ 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); +} + +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 template @@ -560,13 +661,14 @@ void plot3(const std::vector &x, const std::vector &y, const std::vector &z, const std::map &keywords = - std::map()) + std::map(), + const long fig_number=0) { 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) { @@ -607,9 +709,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 +1022,141 @@ 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, + const std::vector& z, + const double s=1.0, // The marker size in points**2 + 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 + // 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 +1385,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); @@ -1202,6 +1448,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 = "") { @@ -1367,8 +1699,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(); @@ -1394,8 +1726,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(); @@ -1421,8 +1753,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(); @@ -1448,8 +1780,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(); @@ -1634,7 +1966,63 @@ 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 +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 @@ -1676,43 +2064,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 @@ -1874,7 +2252,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)); @@ -1939,7 +2317,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); @@ -1972,6 +2350,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(); @@ -2008,12 +2411,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); @@ -2069,9 +2474,9 @@ inline void set_zlabel(const std::string &str, const std::map 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); +} + +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); } diff --git a/numpy_flags.py b/numpy_flags.py deleted file mode 100644 index 56fd95cc..00000000 --- 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") pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy