diff --git a/.gitignore b/.gitignore index 7622be7..561c987 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ +build + +.ycm_extra_conf.py +.ccls-cache/ +compile_commands.json + + # Compiled Object files *.slo *.lo diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..dffd9c4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.7) + +## +## PROJECT +## name and version +## +project(matplotlibcpp VERSION 0.1.0 LANGUAGES CXX) + +find_package(PythonLibs REQUIRED) +#find_package(xtensor) + +OPTION(USE_NUMPY "Use numpy" ON) +OPTION(BUILD_EXAMPLES "Build examples" OFF) + +if (USE_NUMPY) + set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") + find_package(Numpy REQUIRED) + message(STATUS "Found numpy: ${NUMPY_INCLUDE_DIRS}") +else() + add_compile_definitions(WITHOUT_NUMPY) +endif() + + +## +## TARGET +## create target and add include path +## +set(MPLCPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) + +add_library(matplotlibcpp INTERFACE) +target_include_directories(matplotlibcpp INTERFACE + $ + $ + $ + $ + $ + $ + ) +if (USE_NUMPY) +target_include_directories(matplotlibcpp INTERFACE + $ + $ + ) +endif() +target_link_libraries(matplotlibcpp INTERFACE "${PYTHON_LIBRARIES}") + +if (BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + + + +# Installation +# ============ + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +install(TARGETS matplotlibcpp + EXPORT ${PROJECT_NAME}-targets) + +# Makes the project importable from the build directory +export(EXPORT ${PROJECT_NAME}-targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") + + #install(DIRECTORY ${MPLCPP_INCLUDE_DIR} + install(FILES ${MPLCPP_INCLUDE_DIR}/matplotlibcpp.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +# GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share". +set(MPLCPP_CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING "install path for matplotlibcppConfig.cmake") + +configure_package_config_file(${PROJECT_NAME}Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${MPLCPP_CMAKECONFIG_INSTALL_DIR}) + +# matplotlibcpp is header-only and does not depend on the architecture. +# Remove CMAKE_SIZEOF_VOID_P from matplotlibcppConfigVersion.cmake so that +# an matplotlibcppConfig.cmake generated for a 64 bit target can be used for +# 32 bit targets and vice versa. +set(_MPLCPP_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) +unset(CMAKE_SIZEOF_VOID_P) +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${${PROJECT_NAME}_VERSION} + COMPATIBILITY SameMajorVersion) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION ${MPLCPP_CMAKECONFIG_INSTALL_DIR}) +install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}Targets.cmake + DESTINATION ${MPLCPP_CMAKECONFIG_INSTALL_DIR}) diff --git a/Makefile b/Makefile deleted file mode 100644 index 1df85e6..0000000 --- a/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -examples: minimal basic modern animation nonblock xkcd quiver bar surface fill_inbetween fill update - -minimal: examples/minimal.cpp matplotlibcpp.h - cd examples && g++ -DWITHOUT_NUMPY minimal.cpp -I/usr/include/python2.7 -lpython2.7 -o minimal -std=c++11 - -basic: examples/basic.cpp matplotlibcpp.h - cd examples && g++ basic.cpp -I/usr/include/python2.7 -lpython2.7 -o basic -std=c++11 - -modern: examples/modern.cpp matplotlibcpp.h - cd examples && g++ modern.cpp -I/usr/include/python2.7 -lpython2.7 -o modern -std=c++11 - -animation: examples/animation.cpp matplotlibcpp.h - cd examples && g++ animation.cpp -I/usr/include/python2.7 -lpython2.7 -o animation -std=c++11 - -nonblock: examples/nonblock.cpp matplotlibcpp.h - cd examples && g++ nonblock.cpp -I/usr/include/python2.7 -lpython2.7 -o nonblock -std=c++11 - -quiver: examples/quiver.cpp matplotlibcpp.h - cd examples && g++ quiver.cpp -I/usr/include/python2.7 -lpython2.7 -o quiver -std=c++11 - -xkcd: examples/xkcd.cpp matplotlibcpp.h - cd examples && g++ xkcd.cpp -I/usr/include/python2.7 -lpython2.7 -o xkcd -std=c++11 - -bar: examples/bar.cpp matplotlibcpp.h - cd examples && g++ bar.cpp -I/usr/include/python2.7 -lpython2.7 -o bar -std=c++11 - -surface: examples/surface.cpp matplotlibcpp.h - cd examples && g++ surface.cpp -I/usr/include/python2.7 -lpython2.7 -o surface -std=c++11 - -fill_inbetween: examples/fill_inbetween.cpp matplotlibcpp.h - cd examples && g++ fill_inbetween.cpp -I/usr/include/python2.7 -lpython2.7 -o fill_inbetween -std=c++11 - -fill: examples/fill.cpp matplotlibcpp.h - cd examples && g++ fill.cpp -I/usr/include/python2.7 -lpython2.7 -o fill -std=c++11 - -update: examples/update.cpp matplotlibcpp.h - cd examples && g++ update.cpp -I/usr/include/python2.7 -lpython2.7 -o update -std=c++11 - -clean: - rm -f examples/{minimal,basic,modern,animation,nonblock,xkcd,quiver,bar,surface,fill_inbetween,fill,update} diff --git a/README.md b/README.md index 338bea7..ffd0ec1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ +matplotlib-cpp-xt +================= + +This is a fork of ![matplotlib-cpp](https://github.com/lava/matplotlib-cpp) +that supports plotting ![xtensor](https://github.com/QuantStack/xtensor) objects + +The README below has been edited slightly to exhibit xtensor plotting capabilities. + + + matplotlib-cpp ============== @@ -17,34 +27,34 @@ int main() { plt::show(); } ``` - g++ minimal.cpp -std=c++11 -I/usr/include/python2.7 -lpython2.7 - -**Result:** + g++ minimal.cpp -std=c++14 -I/usr/include/python3.6 -lpython3.6 -![Minimal example](./examples/minimal.png) A more comprehensive example: ```cpp #include "matplotlibcpp.h" #include +#include +#include + namespace plt = matplotlibcpp; int main() { // Prepare data. int n = 5000; - std::vector x(n), y(n), z(n), w(n,2); - for(int i=0; i i = xt::linspace(0, (n-1), n); + std::array shape{static_cast(n)}; + xt::xtensor w(shape); w.fill(2.0); + xt::xtensor x = xt::square(i); + xt::xtensor y = xt::sin(2 * xt::numeric_constants::PI * i / 360.0); + xt::xtensor z = xt::log(i); // Set the size of output image to 1200x780 pixels plt::figure_size(1200, 780); // Plot line from given x and y data. Color is selected automatically. - plt::plot(x, y); + plt::plot(x, y + 1.0); // rvalues are supported // Plot a red dashed line from given x and y data. plt::plot(x, w,"r--"); // Plot a line whose name will show up as "log(x)" in the legend. @@ -59,46 +69,39 @@ int main() plt::save("./basic.png"); } ``` - g++ basic.cpp -I/usr/include/python2.7 -lpython2.7 - -**Result:** + g++ basic.cpp -std=c++14 -I/usr/include/python3.6 -lpython3.6 -![Basic example](./examples/basic.png) Alternatively, matplotlib-cpp also supports some C++11-powered syntactic sugar: ```cpp #include #include "matplotlibcpp.h" -using namespace std; +#include +#include + namespace plt = matplotlibcpp; int main() { // Prepare data. int n = 5000; // number of data points - vector x(n),y(n); - for(int i=0; i i = xt::linspace(0, (n-1), n); + xt::xtensor t = 2 * xt::numeric_constants::PI * i / n; + xt::xtensor x = 16*xt::sin(t)*xt::sin(t)*xt::sin(t); + xt::xtensor y = 13*xt::cos(t) - 5*xt::cos(2*t) - 2*xt::cos(3*t) - xt::cos(4*t); // plot() takes an arbitrary number of (x,y,format)-triples. // x must be iterable (that is, anything providing begin(x) and end(x)), // 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-"); - // show plots plt::show(); } ``` - g++ modern.cpp -std=c++11 -I/usr/include/python2.7 -lpython - -**Result:** + g++ modern.cpp -std=c++14 -I/usr/include/python3.6 -lpython3.6 -![Modern example](./examples/modern.png) Or some *funny-looking xkcd-styled* example: ```cpp @@ -106,16 +109,14 @@ Or some *funny-looking xkcd-styled* example: #include #include +#include +#include + namespace plt = matplotlibcpp; int main() { - std::vector t(1000); - std::vector x(t.size()); - - for(size_t i = 0; i < t.size(); i++) { - t[i] = i / 100.0; - x[i] = sin(2.0 * M_PI * 1.0 * t[i]); - } + xt::xtensor t = xt::linspace(0, 10, 1000); + xt::xtensor x = xt::sin(2.0 * xt::numeric_constants::PI * t); plt::xkcd(); plt::plot(t, x); @@ -124,28 +125,31 @@ int main() { } ``` - g++ xkcd.cpp -std=c++11 -I/usr/include/python2.7 -lpython2.7 + g++ xkcd.cpp -std=c++14 -I/usr/include/python3.6 -lpython3.6 -**Result:** - -![xkcd example](./examples/xkcd.png) When working with vector fields, you might be interested in quiver plots: ```cpp #include "../matplotlibcpp.h" +#include +#include + namespace plt = matplotlibcpp; int main() { // u and v are respectively the x and y components of the arrows we're plotting - std::vector x, y, u, v; + std::array shape{121}; + xt::xtensor x(shape), y(shape), u(shape), v(shape); + std::size_t idx = 0; for (int i = -5; i <= 5; i++) { for (int j = -5; j <= 5; j++) { - x.push_back(i); - u.push_back(-i); - y.push_back(j); - v.push_back(-j); + x[idx] = (i) + u[idx] = (-i); + y[idx] = (j); + v[idx] = (-j); + ++idx; } } @@ -153,16 +157,17 @@ int main() plt::show(); } ``` - g++ quiver.cpp -std=c++11 -I/usr/include/python2.7 -lpython2.7 - -**Result:** + g++ quiver.cpp -std=c++14 -I/usr/include/python3.6 -lpython3.6 -![quiver example](./examples/quiver.png) When working with 3d functions, you might be interested in 3d plots: ```cpp #include "../matplotlibcpp.h" +#include +#include +#include + namespace plt = matplotlibcpp; int main() @@ -180,14 +185,20 @@ int main() z.push_back(z_row); } - plt::plot_surface(x, y, z); + std::array shape{x.size(), x[0].size()}; + xt::xtensor xx(shape), xy(shape), xz(shape); + + for (std::size_t i = 0; i < x.size(); ++i) { + xt::view(xx, i, xt::all()) = xt::adapt(x[i]); + xt::view(xy, i, xt::all()) = xt::adapt(y[i]); + xt::view(xz, i, xt::all()) = xt::adapt(z[i]); + } + + plt::plot_surface(xx, xy, xz); plt::show(); } ``` -**Result:** - -![surface example](./examples/surface.png) Installation ------------ @@ -196,7 +207,7 @@ matplotlib-cpp works by wrapping the popular python plotting library matplotlib. This means you have to have a working python installation, including development headers. On Ubuntu: - sudo apt-get install python-matplotlib python-numpy python2.7-dev + sudo apt-get install python3-matplotlib python3-numpy python3-dev If, for some reason, you're unable to get a working installation of numpy on your system, you can add the define `WITHOUT_NUMPY` to erase this dependency. @@ -204,7 +215,7 @@ you can add the define `WITHOUT_NUMPY` to erase this dependency. 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 `libpython2.7` in order to use +Since a python interpreter is opened internally, it is necessary to link against `libpython` in order to use matplotlib-cpp. # CMake @@ -217,7 +228,9 @@ target_include_directories(myproject PRIVATE ${PYTHON_INCLUDE_DIRS}) target_link_libraries(myproject ${PYTHON_LIBRARIES}) ``` -# C++11 +# C++14 + +c++14 is now required as `xtensor` uses c++14 features. Currently, c++11 is required to build matplotlib-cpp. The last working commit that did not have this requirement was `717e98e752260245407c5329846f5d62605eff08`. @@ -226,7 +239,7 @@ 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 +# Python 2 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 diff --git a/cmake/FindNumpy.cmake b/cmake/FindNumpy.cmake new file mode 100644 index 0000000..48a1046 --- /dev/null +++ b/cmake/FindNumpy.cmake @@ -0,0 +1,89 @@ +# - Find the NumPy libraries +# This module finds if NumPy is installed, and sets the following variables +# indicating where it is. +# +# TODO: Update to provide the libraries and paths for linking npymath lib. +# +# NUMPY_FOUND - was NumPy found +# NUMPY_VERSION - the version of NumPy found as a string +# NUMPY_VERSION_MAJOR - the major version number of NumPy +# NUMPY_VERSION_MINOR - the minor version number of NumPy +# NUMPY_VERSION_PATCH - the patch version number of NumPy +# NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 +# NUMPY_INCLUDE_DIRS - path to the NumPy include files + +#============================================================================ +# Copyright 2012 Continuum Analytics, Inc. +# +# MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +#============================================================================ + +# Finding NumPy involves calling the Python interpreter +if(NumPy_FIND_REQUIRED) + find_package(PythonInterp REQUIRED) +else() + find_package(PythonInterp) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(NUMPY_FOUND FALSE) +endif() + +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "import numpy as n; print(n.__version__); print(n.get_include());" + RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS + OUTPUT_VARIABLE _NUMPY_VALUES + ERROR_VARIABLE _NUMPY_ERROR_VALUE + OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0) + if(NumPy_FIND_REQUIRED) + message(FATAL_ERROR + "NumPy import failure:\n${_NUMPY_ERROR_VALUE}") + endif() + set(NUMPY_FOUND FALSE) +endif() + +# Convert the process output into a list +string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES}) +string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES}) +list(GET _NUMPY_VALUES 0 NUMPY_VERSION) +list(GET _NUMPY_VALUES 1 NUMPY_INCLUDE_DIRS) + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS}) + +# Get the major and minor version numbers +string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION}) +list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR) +list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR) +list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH) +string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH}) +math(EXPR NUMPY_VERSION_DECIMAL + "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}") + +find_package_message(NUMPY + "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}" + "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}") + +set(NUMPY_FOUND TRUE) diff --git a/cmake/FindPythonLibs.cmake b/cmake/FindPythonLibs.cmake new file mode 100644 index 0000000..1d62ac4 --- /dev/null +++ b/cmake/FindPythonLibs.cmake @@ -0,0 +1,399 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPythonLibs +-------------- + +.. deprecated:: 3.12 + + Use :module:`FindPython3`, :module:`FindPython2` or :module:`FindPython` instead. + +Find python libraries + +This module finds if Python is installed and determines where the +include files and libraries are. It also determines what the name of +the library is. This code sets the following variables: + +:: + + PYTHONLIBS_FOUND - have the Python libs been found + PYTHON_LIBRARIES - path to the python library + PYTHON_INCLUDE_PATH - path to where Python.h is found (deprecated) + PYTHON_INCLUDE_DIRS - path to where Python.h is found + PYTHON_DEBUG_LIBRARIES - path to the debug library (deprecated) + PYTHONLIBS_VERSION_STRING - version of the Python libs found (since CMake 2.8.8) + + + +The Python_ADDITIONAL_VERSIONS variable can be used to specify a list +of version numbers that should be taken into account when searching +for Python. You need to set this variable before calling +find_package(PythonLibs). + +If you'd like to specify the installation of Python to use, you should +modify the following cache variables: + +:: + + PYTHON_LIBRARY - path to the python library + PYTHON_INCLUDE_DIR - path to where Python.h is found + +If calling both ``find_package(PythonInterp)`` and +``find_package(PythonLibs)``, call ``find_package(PythonInterp)`` first to +get the currently active Python version by default with a consistent version +of PYTHON_LIBRARIES. +#]=======================================================================] + +# Use the executable's path as a hint +set(_Python_LIBRARY_PATH_HINT) +if(IS_ABSOLUTE "${PYTHON_EXECUTABLE}") + if(WIN32) + get_filename_component(_Python_PREFIX "${PYTHON_EXECUTABLE}" PATH) + if(_Python_PREFIX) + set(_Python_LIBRARY_PATH_HINT ${_Python_PREFIX}/libs) + endif() + unset(_Python_PREFIX) + else() + get_filename_component(_Python_PREFIX "${PYTHON_EXECUTABLE}" PATH) + get_filename_component(_Python_PREFIX "${_Python_PREFIX}" PATH) + if(_Python_PREFIX) + set(_Python_LIBRARY_PATH_HINT ${_Python_PREFIX}/lib) + endif() + unset(_Python_PREFIX) + endif() +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/CMakeFindFrameworks.cmake) +# Search for the python framework on Apple. +CMAKE_FIND_FRAMEWORKS(Python) + +# Save CMAKE_FIND_FRAMEWORK +if(DEFINED CMAKE_FIND_FRAMEWORK) + set(_PythonLibs_CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) +else() + unset(_PythonLibs_CMAKE_FIND_FRAMEWORK) +endif() +# To avoid picking up the system Python.h pre-maturely. +set(CMAKE_FIND_FRAMEWORK LAST) + +set(_PYTHON1_VERSIONS 1.6 1.5) +set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) +set(_PYTHON3_VERSIONS 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) + +if(PythonLibs_FIND_VERSION) + if(PythonLibs_FIND_VERSION_COUNT GREATER 1) + set(_PYTHON_FIND_MAJ_MIN "${PythonLibs_FIND_VERSION_MAJOR}.${PythonLibs_FIND_VERSION_MINOR}") + unset(_PYTHON_FIND_OTHER_VERSIONS) + if(PythonLibs_FIND_VERSION_EXACT) + if(_PYTHON_FIND_MAJ_MIN STREQUAL PythonLibs_FIND_VERSION) + set(_PYTHON_FIND_OTHER_VERSIONS "${PythonLibs_FIND_VERSION}") + else() + set(_PYTHON_FIND_OTHER_VERSIONS "${PythonLibs_FIND_VERSION}" "${_PYTHON_FIND_MAJ_MIN}") + endif() + else() + foreach(_PYTHON_V ${_PYTHON${PythonLibs_FIND_VERSION_MAJOR}_VERSIONS}) + if(NOT _PYTHON_V VERSION_LESS _PYTHON_FIND_MAJ_MIN) + list(APPEND _PYTHON_FIND_OTHER_VERSIONS ${_PYTHON_V}) + endif() + endforeach() + endif() + unset(_PYTHON_FIND_MAJ_MIN) + else() + set(_PYTHON_FIND_OTHER_VERSIONS ${_PYTHON${PythonLibs_FIND_VERSION_MAJOR}_VERSIONS}) + endif() +else() + set(_PYTHON_FIND_OTHER_VERSIONS ${_PYTHON3_VERSIONS} ${_PYTHON2_VERSIONS} ${_PYTHON1_VERSIONS}) +endif() + +# Set up the versions we know about, in the order we will search. Always add +# the user supplied additional versions to the front. +# If FindPythonInterp has already found the major and minor version, +# insert that version between the user supplied versions and the stock +# version list. +set(_Python_VERSIONS ${Python_ADDITIONAL_VERSIONS}) +if(DEFINED PYTHON_VERSION_MAJOR AND DEFINED PYTHON_VERSION_MINOR) + list(APPEND _Python_VERSIONS ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}) +endif() +list(APPEND _Python_VERSIONS ${_PYTHON_FIND_OTHER_VERSIONS}) + +unset(_PYTHON_FIND_OTHER_VERSIONS) +unset(_PYTHON1_VERSIONS) +unset(_PYTHON2_VERSIONS) +unset(_PYTHON3_VERSIONS) + +# Python distribution: define which architectures can be used +if (CMAKE_SIZEOF_VOID_P) + # In this case, search only for 64bit or 32bit + math (EXPR _PYTHON_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") + set (_PYTHON_ARCH2 _PYTHON_PREFIX_ARCH}) +else() + if (PYTHON_EXECUTABLE) + # determine interpreter architecture + execute_process (COMMAND "${PYTHON_EXECUTABLE}" -c "import sys; print(sys.maxsize > 2**32)" + RESULT_VARIABLE _PYTHON_RESULT + OUTPUT_VARIABLE _PYTHON_IS64BIT + ERROR_VARIABLE _PYTHON_IS64BIT) + if (NOT _PYTHON_RESULT) + if (_PYTHON_IS64BIT) + set (_PYTHON_ARCH 64) + set (_PYTHON_ARCH2 64) + else() + set (_PYTHON_ARCH 32) + set (_PYTHON_ARCH2 32) + endif() + endif() + else() + # architecture unknown, search for both 64bit and 32bit + set (_PYTHON_ARCH 64) + set (_PYTHON_ARCH2 32) + endif() +endif() + +foreach(_CURRENT_VERSION ${_Python_VERSIONS}) + string(REPLACE "." "" _CURRENT_VERSION_NO_DOTS ${_CURRENT_VERSION}) + if(WIN32) + find_library(PYTHON_DEBUG_LIBRARY + NAMES python${_CURRENT_VERSION_NO_DOTS}_d python + NAMES_PER_DIR + HINTS ${_Python_LIBRARY_PATH_HINT} + PATHS + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs/Debug + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/libs/Debug + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/libs/Debug + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs/Debug + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/libs/Debug + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/libs/Debug + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/libs + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/libs + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/libs + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/libs + ) + endif() + + set(PYTHON_FRAMEWORK_LIBRARIES) + if(Python_FRAMEWORKS AND NOT PYTHON_LIBRARY) + foreach(dir ${Python_FRAMEWORKS}) + list(APPEND PYTHON_FRAMEWORK_LIBRARIES + ${dir}/Versions/${_CURRENT_VERSION}/lib) + endforeach() + endif() + find_library(PYTHON_LIBRARY + NAMES + python${_CURRENT_VERSION_NO_DOTS} + python${_CURRENT_VERSION}mu + python${_CURRENT_VERSION}m + python${_CURRENT_VERSION}u + python${_CURRENT_VERSION} + NAMES_PER_DIR + HINTS + ${_Python_LIBRARY_PATH_HINT} + PATHS + ${PYTHON_FRAMEWORK_LIBRARIES} + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/libs + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/libs + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/libs + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/libs + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/libs + ) + # Look for the static library in the Python config directory + find_library(PYTHON_LIBRARY + NAMES python${_CURRENT_VERSION_NO_DOTS} python${_CURRENT_VERSION} + NAMES_PER_DIR + # This is where the static library is usually located + PATH_SUFFIXES python${_CURRENT_VERSION}/config + ) + + # Don't search for include dir until library location is known + if(PYTHON_LIBRARY) + + # Use the library's install prefix as a hint + set(_Python_INCLUDE_PATH_HINT) + # PYTHON_LIBRARY may contain a list because of SelectLibraryConfigurations + # which may have been run previously. If it is the case, the list can be: + # optimized;;debug; + foreach(lib ${PYTHON_LIBRARY} ${PYTHON_DEBUG_LIBRARY}) + if(IS_ABSOLUTE "${lib}") + get_filename_component(_Python_PREFIX "${lib}" PATH) + get_filename_component(_Python_PREFIX "${_Python_PREFIX}" PATH) + if(_Python_PREFIX) + list(APPEND _Python_INCLUDE_PATH_HINT ${_Python_PREFIX}/include) + endif() + unset(_Python_PREFIX) + endif() + endforeach() + + # Add framework directories to the search paths + set(PYTHON_FRAMEWORK_INCLUDES) + if(Python_FRAMEWORKS AND NOT PYTHON_INCLUDE_DIR) + foreach(dir ${Python_FRAMEWORKS}) + list(APPEND PYTHON_FRAMEWORK_INCLUDES + ${dir}/Versions/${_CURRENT_VERSION}/include) + endforeach() + endif() + + find_path(PYTHON_INCLUDE_DIR + NAMES Python.h + HINTS + ${_Python_INCLUDE_PATH_HINT} + PATHS + ${PYTHON_FRAMEWORK_INCLUDES} + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/include + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/include + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/include + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath]/include + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH}\\InstallPath]/include + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-${_PYTHON_ARCH2}\\InstallPath]/include + PATH_SUFFIXES + python${_CURRENT_VERSION}mu + python${_CURRENT_VERSION}m + python${_CURRENT_VERSION}u + python${_CURRENT_VERSION} + ) + endif() + + # For backward compatibility, set PYTHON_INCLUDE_PATH. + set(PYTHON_INCLUDE_PATH "${PYTHON_INCLUDE_DIR}") + + if(PYTHON_INCLUDE_DIR AND EXISTS "${PYTHON_INCLUDE_DIR}/patchlevel.h") + file(STRINGS "${PYTHON_INCLUDE_DIR}/patchlevel.h" python_version_str + REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") + string(REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" + PYTHONLIBS_VERSION_STRING "${python_version_str}") + unset(python_version_str) + endif() + + if(PYTHON_LIBRARY AND PYTHON_INCLUDE_DIR) + break() + endif() +endforeach() + +unset(_Python_INCLUDE_PATH_HINT) +unset(_Python_LIBRARY_PATH_HINT) + +mark_as_advanced( + PYTHON_DEBUG_LIBRARY + PYTHON_LIBRARY + PYTHON_INCLUDE_DIR +) + +# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the +# cache entries because they are meant to specify the location of a single +# library. We now set the variables listed by the documentation for this +# module. +set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") +set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") + +# These variables have been historically named in this module different from +# what SELECT_LIBRARY_CONFIGURATIONS() expects. +set(PYTHON_LIBRARY_DEBUG "${PYTHON_DEBUG_LIBRARY}") +set(PYTHON_LIBRARY_RELEASE "${PYTHON_LIBRARY}") +include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) +SELECT_LIBRARY_CONFIGURATIONS(PYTHON) +# SELECT_LIBRARY_CONFIGURATIONS() sets ${PREFIX}_FOUND if it has a library. +# Unset this, this prefix doesn't match the module prefix, they are different +# for historical reasons. +unset(PYTHON_FOUND) + +# Restore CMAKE_FIND_FRAMEWORK +if(DEFINED _PythonLibs_CMAKE_FIND_FRAMEWORK) + set(CMAKE_FIND_FRAMEWORK ${_PythonLibs_CMAKE_FIND_FRAMEWORK}) + unset(_PythonLibs_CMAKE_FIND_FRAMEWORK) +else() + unset(CMAKE_FIND_FRAMEWORK) +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PythonLibs + REQUIRED_VARS PYTHON_LIBRARIES PYTHON_INCLUDE_DIRS + VERSION_VAR PYTHONLIBS_VERSION_STRING) + +# PYTHON_ADD_MODULE( src1 src2 ... srcN) is used to build modules for python. +# PYTHON_WRITE_MODULES_HEADER() writes a header file you can include +# in your sources to initialize the static python modules +function(PYTHON_ADD_MODULE _NAME ) + get_property(_TARGET_SUPPORTS_SHARED_LIBS + GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) + option(PYTHON_ENABLE_MODULE_${_NAME} "Add module ${_NAME}" TRUE) + option(PYTHON_MODULE_${_NAME}_BUILD_SHARED + "Add module ${_NAME} shared" ${_TARGET_SUPPORTS_SHARED_LIBS}) + + # Mark these options as advanced + mark_as_advanced(PYTHON_ENABLE_MODULE_${_NAME} + PYTHON_MODULE_${_NAME}_BUILD_SHARED) + + if(PYTHON_ENABLE_MODULE_${_NAME}) + if(PYTHON_MODULE_${_NAME}_BUILD_SHARED) + set(PY_MODULE_TYPE MODULE) + else() + set(PY_MODULE_TYPE STATIC) + set_property(GLOBAL APPEND PROPERTY PY_STATIC_MODULES_LIST ${_NAME}) + endif() + + set_property(GLOBAL APPEND PROPERTY PY_MODULES_LIST ${_NAME}) + add_library(${_NAME} ${PY_MODULE_TYPE} ${ARGN}) +# target_link_libraries(${_NAME} ${PYTHON_LIBRARIES}) + + if(PYTHON_MODULE_${_NAME}_BUILD_SHARED) + set_target_properties(${_NAME} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") + if(WIN32 AND NOT CYGWIN) + set_target_properties(${_NAME} PROPERTIES SUFFIX ".pyd") + endif() + endif() + + endif() +endfunction() + +function(PYTHON_WRITE_MODULES_HEADER _filename) + + get_property(PY_STATIC_MODULES_LIST GLOBAL PROPERTY PY_STATIC_MODULES_LIST) + + get_filename_component(_name "${_filename}" NAME) + string(REPLACE "." "_" _name "${_name}") + string(TOUPPER ${_name} _nameUpper) + set(_filename ${CMAKE_CURRENT_BINARY_DIR}/${_filename}) + + set(_filenameTmp "${_filename}.in") + file(WRITE ${_filenameTmp} "/*Created by cmake, do not edit, changes will be lost*/\n") + file(APPEND ${_filenameTmp} +"#ifndef ${_nameUpper} +#define ${_nameUpper} + +#include + +#ifdef __cplusplus +extern \"C\" { +#endif /* __cplusplus */ + +") + + foreach(_currentModule ${PY_STATIC_MODULES_LIST}) + file(APPEND ${_filenameTmp} "extern void init${PYTHON_MODULE_PREFIX}${_currentModule}(void);\n\n") + endforeach() + + file(APPEND ${_filenameTmp} +"#ifdef __cplusplus +} +#endif /* __cplusplus */ + +") + + + foreach(_currentModule ${PY_STATIC_MODULES_LIST}) + file(APPEND ${_filenameTmp} "int ${_name}_${_currentModule}(void) \n{\n static char name[]=\"${PYTHON_MODULE_PREFIX}${_currentModule}\"; return PyImport_AppendInittab(name, init${PYTHON_MODULE_PREFIX}${_currentModule});\n}\n\n") + endforeach() + + file(APPEND ${_filenameTmp} "void ${_name}_LoadAllPythonModules(void)\n{\n") + foreach(_currentModule ${PY_STATIC_MODULES_LIST}) + file(APPEND ${_filenameTmp} " ${_name}_${_currentModule}();\n") + endforeach() + file(APPEND ${_filenameTmp} "}\n\n") + file(APPEND ${_filenameTmp} "#ifndef EXCLUDE_LOAD_ALL_FUNCTION\nvoid CMakeLoadAllPythonModules(void)\n{\n ${_name}_LoadAllPythonModules();\n}\n#endif\n\n#endif\n") + +# with configure_file() cmake complains that you may not use a file created using file(WRITE) as input file for configure_file() + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_filenameTmp}" "${_filename}" OUTPUT_QUIET ERROR_QUIET) + +endfunction() diff --git a/cmake/xtensorConfig.cmake b/cmake/xtensorConfig.cmake new file mode 100644 index 0000000..69ee380 --- /dev/null +++ b/cmake/xtensorConfig.cmake @@ -0,0 +1,56 @@ +############################################################################ +# Copyright (c) 2016, Johan Mabille and Sylvain Corlay # +# # +# Distributed under the terms of the BSD 3-Clause License. # +# # +# The full license is in the file LICENSE, distributed with this software. # +############################################################################ + +# xtensor cmake module +# This module sets the following variables in your project:: +# +# xtensor_FOUND - true if xtensor found on the system +# xtensor_INCLUDE_DIRS - the directory containing xtensor headers +# xtensor_LIBRARY - empty + + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was xtensorConfig.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +include(CMakeFindDependencyMacro) +find_dependency(xtl 0.5.3) + +if(XTENSOR_USE_XSIMD) + find_dependency(xsimd ) +endif() + +if(XTENSOR_USE_TBB) + find_dependency(TBB) +endif() + +if(NOT TARGET xtensor) + include("${CMAKE_CURRENT_LIST_DIR}/xtensorTargets.cmake") + get_target_property(xtensor_INCLUDE_DIRS xtensor INTERFACE_INCLUDE_DIRECTORIES) +endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..c02bf74 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,27 @@ +file(GLOB files "*.cpp") +if (NOT USE_NUMPY) + message(STATUS "Can't build surface.cpp without numpy. Skipping...") + get_filename_component(surface_path ${CMAKE_CURRENT_SOURCE_DIR}/surface.cpp ABSOLUTE) + list(REMOVE_ITEM files "${surface_path}") +endif() + +foreach(file ${files}) + get_filename_component(file_basename ${file} NAME_WE) + + add_executable(${file_basename} ${file}) + target_include_directories(${file_basename} PRIVATE + ${PYTHON_INCLUDE_DIRS} + ${xtensor_INCLUDE_DIRS} + ${MPLCPP_INCLUDE_DIR} + ) + if(NUMPY_FOUND) + target_include_directories(${file_basename} PRIVATE + ${NUMPY_INCLUDE_DIRS} + ) + endif() + + target_link_libraries(${file_basename} + ${PYTHON_LIBRARIES} + ) +endforeach() + diff --git a/examples/animation.cpp b/examples/animation.cpp index d979430..554cdfe 100644 --- a/examples/animation.cpp +++ b/examples/animation.cpp @@ -1,29 +1,32 @@ #define _USE_MATH_DEFINES #include -#include "../matplotlibcpp.h" +#include + +#include +#include namespace plt = matplotlibcpp; int main() { int n = 1000; - std::vector x, y, z; + xt::xtensor x, y, z; for(int i=0; i{static_cast(i*i)})); + y = xt::concatenate(xt::xtuple(y, xt::xtensor{sin(2*M_PI*i/360.0)})); + z = xt::concatenate(xt::xtuple(z, xt::xtensor{log(i)})); if (i % 10 == 0) { // Clear previous plot plt::clf(); // Plot line from given x and y data. Color is selected automatically. - plt::plot(x, y); + plt::plot(x-50000, y+2); // Plot a line whose name will show up as "log(x)" in the legend. plt::named_plot("log(x)", x, z); // Set x-axis to interval [0,1000000] - plt::xlim(0, n*n); + plt::xlim(-50000, n*n); // Add graph title plt::title("Sample figure"); diff --git a/examples/animation.gif b/examples/animation.gif deleted file mode 100644 index ef8eb5a..0000000 Binary files a/examples/animation.gif and /dev/null differ diff --git a/examples/bar.cpp b/examples/bar.cpp index 86423ad..35bb5f1 100644 --- a/examples/bar.cpp +++ b/examples/bar.cpp @@ -2,14 +2,14 @@ #include #include -#include "../matplotlibcpp.h" +#include namespace plt = matplotlibcpp; +#include +#include + int main(int argc, char **argv) { - std::vector test_data; - for (int i = 0; i < 20; i++) { - test_data.push_back(i); - } + xt::xtensor test_data = xt::arange(20); plt::bar(test_data); plt::show(); diff --git a/examples/bar.png b/examples/bar.png deleted file mode 100644 index be6af0f..0000000 Binary files a/examples/bar.png and /dev/null differ diff --git a/examples/basic.cpp b/examples/basic.cpp index 2dc34c7..5c3e6aa 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -1,7 +1,10 @@ #define _USE_MATH_DEFINES #include #include -#include "../matplotlibcpp.h" +#include + +#include +#include namespace plt = matplotlibcpp; @@ -9,24 +12,30 @@ int main() { // Prepare data. int n = 5000; - std::vector x(n), y(n), z(n), w(n,2); - for(int i=0; i i = xt::linspace(0, (n-1), n); + std::array shape{static_cast(n)}; + xt::xtensor w(shape); + w.fill(2.0); + xt::xtensor x = xt::square(i); + xt::xtensor y = xt::sin(2 * xt::numeric_constants::PI * i / 360.0); + xt::xtensor z = xt::log(i); + + std::vector vy(n, 5); // Set the size of output image = 1200x780 pixels plt::figure_size(1200, 780); // Plot line from given x and y data. Color is selected automatically. - plt::plot(x, y); + plt::plot(x, y+1); // Plot a red dashed line from given x and y data. - plt::plot(x, w,"r--"); + plt::plot(x, w+1, "r--"); + + plt::plot(x, vy, "g-."); // Plot a line whose name will show up as "log(x)" in the legend. plt::named_plot("log(x)", x, z); + plt::named_plot("log(x)+1", x, z+1); // Set x-axis to interval [0,1000000] plt::xlim(0, 1000*1000); @@ -37,8 +46,9 @@ int main() // Enable legend. plt::legend(); + plt::show(); // save figure - const char* filename = "./basic.png"; - std::cout << "Saving result to " << filename << std::endl;; - plt::save(filename); + //const char* filename = "./basic.png"; + //std::cout << "Saving result to " << filename << std::endl;; + //plt::save(filename); } diff --git a/examples/basic.png b/examples/basic.png deleted file mode 100644 index 4d87ff0..0000000 Binary files a/examples/basic.png and /dev/null differ diff --git a/examples/fill.cpp b/examples/fill.cpp index 6059b47..31774a6 100644 --- a/examples/fill.cpp +++ b/examples/fill.cpp @@ -1,35 +1,32 @@ #define _USE_MATH_DEFINES -#include "../matplotlibcpp.h" +#include #include -using namespace std; +#include +#include + +using namespace xt::placeholders; namespace plt = matplotlibcpp; // Example fill plot taken from: // https://matplotlib.org/gallery/misc/fill_spiral.html int main() { // Prepare data. - vector theta; - for (double d = 0; d < 8 * M_PI; d += 0.1) - theta.push_back(d); + xt::xtensor theta = xt::arange(0, 8*xt::numeric_constants::PI, 0.1); const int a = 1; const double b = 0.2; for (double dt = 0; dt < 2 * M_PI; dt += M_PI/2.0) { - vector x1, y1, x2, y2; - for (double th : theta) { - x1.push_back( a*cos(th + dt) * exp(b*th) ); - y1.push_back( a*sin(th + dt) * exp(b*th) ); - - x2.push_back( a*cos(th + dt + M_PI/4.0) * exp(b*th) ); - y2.push_back( a*sin(th + dt + M_PI/4.0) * exp(b*th) ); - } + xt::xtensor x1 = a*xt::cos(theta + dt) * xt::exp(b*theta); + xt::xtensor y1 = a*xt::sin(theta + dt) * xt::exp(b*theta); + xt::xtensor x2 = a*xt::cos(theta + dt + M_PI/4.0) * xt::exp(b*theta); + xt::xtensor y2 = a*xt::sin(theta + dt + M_PI/4.0) * xt::exp(b*theta); - x1.insert(x1.end(), x2.rbegin(), x2.rend()); - y1.insert(y1.end(), y2.rbegin(), y2.rend()); + x1 = xt::concatenate(xt::xtuple(x1, xt::view(x2, xt::range(x2.size(), _, -1)))); + y1 = xt::concatenate(xt::xtuple(y1, xt::view(y2, xt::range(y2.size(), _, -1)))); - plt::fill(x1, y1, {}); + plt::fill(x1+50, y1+50, {}); } plt::show(); } diff --git a/examples/fill.png b/examples/fill.png deleted file mode 100644 index aa1fc0d..0000000 Binary files a/examples/fill.png and /dev/null differ diff --git a/examples/fill_between.png b/examples/fill_between.png deleted file mode 100644 index a199423..0000000 Binary files a/examples/fill_between.png and /dev/null differ diff --git a/examples/fill_inbetween.cpp b/examples/fill_inbetween.cpp index 788d008..39de0c0 100644 --- a/examples/fill_inbetween.cpp +++ b/examples/fill_inbetween.cpp @@ -1,28 +1,32 @@ #define _USE_MATH_DEFINES -#include "../matplotlibcpp.h" +#include #include #include using namespace std; namespace plt = matplotlibcpp; +#include +#include + int main() { - // Prepare data. - int n = 5000; - std::vector x(n), y(n), z(n), w(n, 2); - for (int i = 0; i < n; ++i) { - x.at(i) = i * i; - y.at(i) = sin(2 * M_PI * i / 360.0); - z.at(i) = log(i); - } + // Prepare data. + int n = 5000; + xt::xtensor i = xt::linspace(0, (n-1), n); + std::array shape{static_cast(n)}; + xt::xtensor w(shape); + w.fill(2.0); + xt::xtensor x = xt::square(i); + xt::xtensor y = xt::sin(2 * xt::numeric_constants::PI * i / 360.0); + xt::xtensor z = xt::log(i); - // Prepare keywords to pass to PolyCollection. See - // https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.fill_between.html - std::map keywords; - keywords["alpha"] = "0.4"; - keywords["color"] = "grey"; - keywords["hatch"] = "-"; + // Prepare keywords to pass to PolyCollection. See + // https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.fill_between.html + std::map keywords; + keywords["alpha"] = "0.4"; + keywords["color"] = "grey"; + keywords["hatch"] = "-"; - plt::fill_between(x, y, z, keywords); - plt::show(); + plt::fill_between(x, y+2, z, keywords); + plt::show(); } diff --git a/examples/minimal.cpp b/examples/minimal.cpp index fbe1e1c..2e80b9e 100644 --- a/examples/minimal.cpp +++ b/examples/minimal.cpp @@ -1,7 +1,10 @@ -#include "../matplotlibcpp.h" +#include namespace plt = matplotlibcpp; +#include +#include + int main() { plt::plot({1,3,2,4}); plt::show(); diff --git a/examples/minimal.png b/examples/minimal.png deleted file mode 100644 index 0f6cf37..0000000 Binary files a/examples/minimal.png and /dev/null differ diff --git a/examples/modern.cpp b/examples/modern.cpp index a8aa0c7..e55f836 100644 --- a/examples/modern.cpp +++ b/examples/modern.cpp @@ -1,10 +1,14 @@ #define _USE_MATH_DEFINES #include -#include "../matplotlibcpp.h" +#include using namespace std; namespace plt = matplotlibcpp; +#include +#include +#include + int main() { // plot(y) - the x-coordinates are implicitly set to [0,1,...,n) @@ -12,17 +16,15 @@ int main() // Prepare data for parametric plot. int n = 5000; // number of data points - vector x(n),y(n); - for(int i=0; i i = xt::linspace(0, (n-1), n); + xt::xtensor t = 2 * xt::numeric_constants::PI * i / n; + xt::xtensor x = 16*xt::sin(t)*xt::sin(t)*xt::sin(t); + xt::xtensor y = 13*xt::cos(t) - 5*xt::cos(2*t) - 2*xt::cos(3*t) - xt::cos(4*t); // plot() takes an arbitrary number of (x,y,format)-triples. // x must be iterable (that is, anything providing begin(x) and end(x)), // 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::plot(x, y, "r-", x, [](double d) { return 12.5+abs(sin(d)); }, "k-"); // show plots diff --git a/examples/modern.png b/examples/modern.png deleted file mode 100644 index 0c51555..0000000 Binary files a/examples/modern.png and /dev/null differ diff --git a/examples/nonblock.cpp b/examples/nonblock.cpp index 327d96c..ab6a72d 100644 --- a/examples/nonblock.cpp +++ b/examples/nonblock.cpp @@ -1,23 +1,24 @@ #define _USE_MATH_DEFINES +#include #include -#include "../matplotlibcpp.h" - -namespace plt = matplotlibcpp; +#include +#include +#include -using namespace matplotlibcpp; -using namespace std; +namespace plt = matplotlibcpp; int main() { // Prepare data. int n = 5000; - std::vector x(n), y(n), z(n), w(n,2); - for(int i=0; i i = xt::linspace(0, (n-1), n); + std::array shape{static_cast(n)}; + xt::xtensor w(shape); + w.fill(2.0); + xt::xtensor x = xt::square(i); + xt::xtensor y = xt::sin(2 * xt::numeric_constants::PI * i / 360.0); + xt::xtensor z = xt::log(i); // Plot line from given x and y data. Color is selected automatically. plt::subplot(2,2,1); @@ -41,6 +42,6 @@ int main() plt::show(false); - cout << "matplotlibcpp::show() is working in an non-blocking mode" << endl; - getchar(); + std::cout << "matplotlibcpp::show() is working in an non-blocking mode" << std::endl; + std::getchar(); } diff --git a/examples/quiver.cpp b/examples/quiver.cpp index ea3c3ec..c55d561 100644 --- a/examples/quiver.cpp +++ b/examples/quiver.cpp @@ -1,20 +1,26 @@ -#include "../matplotlibcpp.h" +#include + +#include +#include namespace plt = matplotlibcpp; int main() { // u and v are respectively the x and y components of the arrows we're plotting - std::vector x, y, u, v; + std::array shape{121}; + xt::xtensor x(shape), y(shape), u(shape), v(shape); + std::size_t idx = 0; for (int i = -5; i <= 5; i++) { for (int j = -5; j <= 5; j++) { - x.push_back(i); - u.push_back(-i); - y.push_back(j); - v.push_back(-j); + x[idx] = (i); + u[idx] = (-i); + y[idx] = (j); + v[idx] = (-j); + ++idx; } } - plt::quiver(x, y, u, v); + plt::quiver(x+2, y+2, u+3, v+1); plt::show(); -} \ No newline at end of file +} diff --git a/examples/quiver.png b/examples/quiver.png deleted file mode 100644 index 9d7be1e..0000000 Binary files a/examples/quiver.png and /dev/null differ diff --git a/examples/subplot.cpp b/examples/subplot.cpp index bee322e..f39b3e6 100644 --- a/examples/subplot.cpp +++ b/examples/subplot.cpp @@ -1,6 +1,9 @@ #define _USE_MATH_DEFINES #include -#include "../matplotlibcpp.h" +#include + +#include +#include using namespace std; namespace plt = matplotlibcpp; @@ -9,19 +12,20 @@ int main() { // Prepare data int n = 500; - std::vector x(n), y(n), z(n), w(n,2); - for(int i=0; i i = xt::linspace(0, (n-1), n); + std::array shape{static_cast(n)}; + xt::xtensor w(shape); + w.fill(2.0); + xt::xtensor x = xt::square(i); + xt::xtensor y = xt::sin(2 * xt::numeric_constants::PI * i / 360.0); + xt::xtensor z = xt::log(i); // Set the "super title" plt::suptitle("My plot"); plt::subplot(1, 2, 1); - plt::plot(x, y, "r-"); + plt::plot(x, y+2, "r-"); plt::subplot(1, 2, 2); - plt::plot(x, z, "k-"); + plt::plot(x-50000, z-2, "k-"); // Add some text to the plot plt::text(100, 90, "Hello!"); diff --git a/examples/surface.cpp b/examples/surface.cpp index 4865f06..20170f9 100644 --- a/examples/surface.cpp +++ b/examples/surface.cpp @@ -1,6 +1,10 @@ -#include "../matplotlibcpp.h" - +#define _USE_MATH_DEFINES +#include #include +#include + +#include +#include namespace plt = matplotlibcpp; @@ -19,6 +23,16 @@ int main() z.push_back(z_row); } - plt::plot_surface(x, y, z); + std::array shape{x.size(), x[0].size()}; + xt::xtensor xx(shape), xy(shape), xz(shape); + + for (std::size_t i = 0; i < x.size(); ++i) { + xt::view(xx, i, xt::all()) = xt::adapt(x[i]); + xt::view(xy, i, xt::all()) = xt::adapt(y[i]); + xt::view(xz, i, xt::all()) = xt::adapt(z[i]); + } + + //plt::plot_surface(xx, xy, xz); + plt::plot_surface(xx+2, xy+5, xz); plt::show(); } diff --git a/examples/surface.png b/examples/surface.png deleted file mode 100644 index 6fc5fc7..0000000 Binary files a/examples/surface.png and /dev/null differ diff --git a/examples/types.cpp b/examples/types.cpp new file mode 100644 index 0000000..2a01b46 --- /dev/null +++ b/examples/types.cpp @@ -0,0 +1,59 @@ +#define _USE_MATH_DEFINES +#include +#include +#include + +#include + +#include +#include + +namespace plt = matplotlibcpp; + +std::vector get_yv(std::size_t len) { + std::vector res(len); + for (std::size_t i = 0; i < len; ++i) { + res[i] = i*i + 2; + } + return res; +} + +template +auto get_fy(E&& e) { + auto res = e + 2.0; + return std::move(res); +} + +int main(int argc, char **argv) { + std::size_t len = 10; + + /***** vectors *****/ + std::vector xv(len), yv(len); + for (std::size_t i = 0; i < len; ++i) { + xv[i] = i; + yv[i] = i*i; + } + + plt::named_plot("vector - lvalue", xv, yv); + plt::named_plot("vector - rvalue", xv, get_yv(len)); + + /***** xtensors *****/ + xt::xtensor xx = xt::linspace(0, len-1, len); + xt::xtensor xy = xt::square(xx) + 4; + + plt::named_plot("xtensor - lvalue", xx, xy, "--"); + plt::named_plot("xtensor - rvalue", xx, xy+1, "--"); + + /***** xfunction*****/ + auto fx = xt::linspace(0, len-1, len); + auto fy = xt::square(xx) + 6; + + plt::named_plot("xfunction - lvalue", fx, fy, "-."); + plt::named_plot("xfunction - rvalue", fx, get_fy(fy), "--"); + + plt::legend(); + plt::show(); + + return 0; +} + diff --git a/examples/update.cpp b/examples/update.cpp index 64f4906..bd9767e 100644 --- a/examples/update.cpp +++ b/examples/update.cpp @@ -1,12 +1,16 @@ #define _USE_MATH_DEFINES #include -#include "../matplotlibcpp.h" +#include #include +#include +#include + namespace plt = matplotlibcpp; +template void update_window(const double x, const double y, const double t, - std::vector &xt, std::vector &yt) + E1& xt, E2& yt) { const double target_length = 300; const double half_win = (target_length/(2.*sqrt(1.+t*t))); @@ -21,26 +25,24 @@ void update_window(const double x, const double y, const double t, int main() { size_t n = 1000; - std::vector x, y; const double w = 0.05; const double a = n/2; - for (size_t i=0; i x = xt::linspace(0, n-1, n); + xt::xtensor y = a * xt::sin(w * x); - std::vector xt(2), yt(2); + xt::xtensor xt{0,0}; + xt::xtensor yt{0,0}; plt::title("Tangent of a sine curve"); - plt::xlim(x.front(), x.back()); - plt::ylim(-a, a); - plt::axis("equal"); - // Plot sin once and for all. plt::named_plot("sin", x, y); + plt::xlim(x[0], x[n-1]); + plt::ylim(-a, a); + plt::axis("equal"); + // Prepare plotting the tangent. plt::Plot plot("tangent"); diff --git a/examples/xkcd.cpp b/examples/xkcd.cpp index fa41cfc..478e6e2 100644 --- a/examples/xkcd.cpp +++ b/examples/xkcd.cpp @@ -1,18 +1,13 @@ #define _USE_MATH_DEFINES #include -#include "../matplotlibcpp.h" +#include #include namespace plt = matplotlibcpp; int main() { - std::vector t(1000); - std::vector x(t.size()); - - for(size_t i = 0; i < t.size(); i++) { - t[i] = i / 100.0; - x[i] = sin(2.0 * M_PI * 1.0 * t[i]); - } + xt::xtensor t = xt::linspace(0, 10, 1000); + xt::xtensor x = xt::sin(2.0 * xt::numeric_constants::PI * t); plt::xkcd(); plt::plot(t, x); diff --git a/examples/xkcd.png b/examples/xkcd.png deleted file mode 100644 index c285e3d..0000000 Binary files a/examples/xkcd.png and /dev/null differ diff --git a/include/matplotlibcpp.h b/include/matplotlibcpp.h new file mode 100644 index 0000000..7ecfd50 --- /dev/null +++ b/include/matplotlibcpp.h @@ -0,0 +1,2094 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include // requires c++11 support +#include + +#include + +#ifndef WITHOUT_NUMPY +# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +# include +#endif // WITHOUT_NUMPY + +#if PY_MAJOR_VERSION >= 3 +# define PyString_FromString PyUnicode_FromString +# define PyInt_FromLong PyLong_FromLong +# define PyString_FromString PyUnicode_FromString +#endif + +#include +#include +#include +#include + +namespace matplotlibcpp { +namespace detail { + + static std::string s_backend; + + struct _interpreter { + PyObject *s_python_function_imshow; + PyObject *s_python_function_show; + PyObject *s_python_function_close; + PyObject *s_python_function_draw; + PyObject *s_python_function_pause; + PyObject *s_python_function_save; + PyObject *s_python_function_figure; + PyObject *s_python_function_fignum_exists; + PyObject *s_python_function_plot; + PyObject *s_python_function_quiver; + PyObject *s_python_function_semilogx; + PyObject *s_python_function_semilogy; + PyObject *s_python_function_loglog; + PyObject *s_python_function_fill; + PyObject *s_python_function_fill_between; + PyObject *s_python_function_hist; + PyObject *s_python_function_scatter; + PyObject *s_python_function_subplot; + PyObject *s_python_function_legend; + PyObject *s_python_function_xlim; + PyObject *s_python_function_ion; + PyObject *s_python_function_ginput; + PyObject *s_python_function_ylim; + PyObject *s_python_function_title; + PyObject *s_python_function_axis; + PyObject *s_python_function_xlabel; + PyObject *s_python_function_ylabel; + PyObject *s_python_function_xticks; + PyObject *s_python_function_yticks; + PyObject *s_python_function_grid; + PyObject *s_python_function_clf; + PyObject *s_python_function_errorbar; + PyObject *s_python_function_annotate; + PyObject *s_python_function_tight_layout; + PyObject *s_python_colormap; + PyObject *s_python_empty_tuple; + PyObject *s_python_function_stem; + PyObject *s_python_function_xkcd; + PyObject *s_python_function_text; + PyObject *s_python_function_suptitle; + PyObject *s_python_function_bar; + PyObject *s_python_function_subplots_adjust; + + + /* 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 + */ + + static _interpreter& get() { + static _interpreter ctx; + return ctx; + } + + private: + +#ifndef WITHOUT_NUMPY +# if PY_MAJOR_VERSION >= 3 + + void *import_numpy() { + import_array(); // initialize C-API + return NULL; + } + +# else + + void import_numpy() { + import_array(); // initialize C-API + } + +# endif +#endif + + _interpreter() { + + // optional but recommended +#if PY_MAJOR_VERSION >= 3 + wchar_t name[] = L"plotting"; +#else + char name[] = "plotting"; +#endif + Py_SetProgramName(name); + Py_Initialize(); + +#ifndef WITHOUT_NUMPY + import_numpy(); // initialize numpy C-API +#endif + + PyObject* matplotlibname = PyString_FromString("matplotlib"); + PyObject* pyplotname = PyString_FromString("matplotlib.pyplot"); + PyObject* cmname = PyString_FromString("matplotlib.cm"); + PyObject* pylabname = PyString_FromString("pylab"); + if (!pyplotname || !pylabname || !matplotlibname || !cmname) { + throw std::runtime_error("couldnt create string"); + } + + PyObject* matplotlib = PyImport_Import(matplotlibname); + Py_DECREF(matplotlibname); + if (!matplotlib) { + PyErr_Print(); + throw std::runtime_error("Error loading module matplotlib!"); + } + + // matplotlib.use() must be called *before* pylab, matplotlib.pyplot, + // or matplotlib.backends is imported for the first time + if (!s_backend.empty()) { + 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!"); } + + s_python_colormap = PyImport_Import(cmname); + Py_DECREF(cmname); + if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); } + + PyObject* pylabmod = PyImport_Import(pylabname); + Py_DECREF(pylabname); + if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); } + + s_python_function_imshow = PyObject_GetAttrString(pymod, "imshow"); + s_python_function_show = PyObject_GetAttrString(pymod, "show"); + s_python_function_close = PyObject_GetAttrString(pymod, "close"); + s_python_function_draw = PyObject_GetAttrString(pymod, "draw"); + s_python_function_pause = PyObject_GetAttrString(pymod, "pause"); + s_python_function_figure = PyObject_GetAttrString(pymod, "figure"); + s_python_function_fignum_exists = PyObject_GetAttrString(pymod, "fignum_exists"); + s_python_function_plot = PyObject_GetAttrString(pymod, "plot"); + s_python_function_quiver = PyObject_GetAttrString(pymod, "quiver"); + s_python_function_semilogx = PyObject_GetAttrString(pymod, "semilogx"); + s_python_function_semilogy = PyObject_GetAttrString(pymod, "semilogy"); + s_python_function_loglog = PyObject_GetAttrString(pymod, "loglog"); + s_python_function_fill = PyObject_GetAttrString(pymod, "fill"); + s_python_function_fill_between = PyObject_GetAttrString(pymod, "fill_between"); + s_python_function_hist = PyObject_GetAttrString(pymod,"hist"); + s_python_function_scatter = PyObject_GetAttrString(pymod,"scatter"); + s_python_function_subplot = PyObject_GetAttrString(pymod, "subplot"); + s_python_function_legend = PyObject_GetAttrString(pymod, "legend"); + s_python_function_ylim = PyObject_GetAttrString(pymod, "ylim"); + s_python_function_title = PyObject_GetAttrString(pymod, "title"); + s_python_function_axis = PyObject_GetAttrString(pymod, "axis"); + s_python_function_xlabel = PyObject_GetAttrString(pymod, "xlabel"); + s_python_function_ylabel = PyObject_GetAttrString(pymod, "ylabel"); + s_python_function_xticks = PyObject_GetAttrString(pymod, "xticks"); + s_python_function_yticks = PyObject_GetAttrString(pymod, "yticks"); + s_python_function_grid = PyObject_GetAttrString(pymod, "grid"); + s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim"); + s_python_function_ion = PyObject_GetAttrString(pymod, "ion"); + s_python_function_ginput = PyObject_GetAttrString(pymod, "ginput"); + s_python_function_save = PyObject_GetAttrString(pylabmod, "savefig"); + s_python_function_annotate = PyObject_GetAttrString(pymod,"annotate"); + s_python_function_clf = PyObject_GetAttrString(pymod, "clf"); + s_python_function_errorbar = PyObject_GetAttrString(pymod, "errorbar"); + s_python_function_tight_layout = PyObject_GetAttrString(pymod, "tight_layout"); + s_python_function_stem = PyObject_GetAttrString(pymod, "stem"); + s_python_function_xkcd = PyObject_GetAttrString(pymod, "xkcd"); + s_python_function_text = PyObject_GetAttrString(pymod, "text"); + s_python_function_suptitle = PyObject_GetAttrString(pymod, "suptitle"); + s_python_function_bar = PyObject_GetAttrString(pymod,"bar"); + s_python_function_subplots_adjust = PyObject_GetAttrString(pymod,"subplots_adjust"); + + if ( !s_python_function_show + || !s_python_function_imshow + || !s_python_function_close + || !s_python_function_draw + || !s_python_function_pause + || !s_python_function_figure + || !s_python_function_fignum_exists + || !s_python_function_plot + || !s_python_function_quiver + || !s_python_function_semilogx + || !s_python_function_semilogy + || !s_python_function_loglog + || !s_python_function_fill + || !s_python_function_fill_between + || !s_python_function_subplot + || !s_python_function_legend + || !s_python_function_ylim + || !s_python_function_title + || !s_python_function_axis + || !s_python_function_xlabel + || !s_python_function_ylabel + || !s_python_function_grid + || !s_python_function_xlim + || !s_python_function_ion + || !s_python_function_ginput + || !s_python_function_save + || !s_python_function_clf + || !s_python_function_annotate + || !s_python_function_errorbar + || !s_python_function_errorbar + || !s_python_function_tight_layout + || !s_python_function_stem + || !s_python_function_xkcd + || !s_python_function_text + || !s_python_function_suptitle + || !s_python_function_bar + || !s_python_function_subplots_adjust + ) { throw std::runtime_error("Couldn't find required function!"); } + + if ( !PyFunction_Check(s_python_function_show) + || !PyFunction_Check(s_python_function_imshow) + || !PyFunction_Check(s_python_function_close) + || !PyFunction_Check(s_python_function_draw) + || !PyFunction_Check(s_python_function_pause) + || !PyFunction_Check(s_python_function_figure) + || !PyFunction_Check(s_python_function_fignum_exists) + || !PyFunction_Check(s_python_function_plot) + || !PyFunction_Check(s_python_function_quiver) + || !PyFunction_Check(s_python_function_semilogx) + || !PyFunction_Check(s_python_function_semilogy) + || !PyFunction_Check(s_python_function_loglog) + || !PyFunction_Check(s_python_function_fill) + || !PyFunction_Check(s_python_function_fill_between) + || !PyFunction_Check(s_python_function_subplot) + || !PyFunction_Check(s_python_function_legend) + || !PyFunction_Check(s_python_function_annotate) + || !PyFunction_Check(s_python_function_ylim) + || !PyFunction_Check(s_python_function_title) + || !PyFunction_Check(s_python_function_axis) + || !PyFunction_Check(s_python_function_xlabel) + || !PyFunction_Check(s_python_function_ylabel) + || !PyFunction_Check(s_python_function_grid) + || !PyFunction_Check(s_python_function_xlim) + || !PyFunction_Check(s_python_function_ion) + || !PyFunction_Check(s_python_function_ginput) + || !PyFunction_Check(s_python_function_save) + || !PyFunction_Check(s_python_function_clf) + || !PyFunction_Check(s_python_function_tight_layout) + || !PyFunction_Check(s_python_function_errorbar) + || !PyFunction_Check(s_python_function_stem) + || !PyFunction_Check(s_python_function_xkcd) + || !PyFunction_Check(s_python_function_text) + || !PyFunction_Check(s_python_function_suptitle) + || !PyFunction_Check(s_python_function_bar) + || !PyFunction_Check(s_python_function_subplots_adjust) + ) { throw std::runtime_error("Python object is unexpectedly not a PyFunction."); } + + s_python_empty_tuple = PyTuple_New(0); + } + + ~_interpreter() { + Py_Finalize(); + } + }; + +} // end namespace detail + + // must be called before the first regular call to matplotlib to have any effect + inline void backend(const std::string& name) { + detail::s_backend = name; + } + + + inline bool annotate(std::string annotation, double x, double y) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject * xy = PyTuple_New(2); + PyObject * str = PyString_FromString(annotation.c_str()); + + PyTuple_SetItem(xy,0,PyFloat_FromDouble(x)); + PyTuple_SetItem(xy,1,PyFloat_FromDouble(y)); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "xy", xy); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, str); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_annotate, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + +#ifndef WITHOUT_NUMPY + // Type selector for numpy array conversion + template struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default + template <> struct select_npy_type { const static NPY_TYPES type = NPY_DOUBLE; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_FLOAT; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_BOOL; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT8; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_SHORT; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT8; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_USHORT; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_ULONG; }; + template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; +#endif + +#ifdef WITHOUT_NUMPY + // vector implementation + template + PyObject* get_array_impl(const std::vector& v) { + PyObject* list = PyList_New(v.size()); + for(size_t i = 0; i < v.size(); ++i) { + PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i))); + } + return list; + } + + // lvalue xexpression implementation + template + PyObject* get_array_impl(const xt::xexpression& e) { + const E& de = e.derived_cast(); + // Implementation for evaluated lvalue using de + + PyObject* list; + if (de.dimension() == 1) { + list = PyList_New(de.size()); + for(size_t i = 0; i < de.size(); ++i) { + PyList_SetItem(list, i, PyFloat_FromDouble(de.at(i))); + } + } else { + std::size_t num_rows = de.shape()[0]; + std::size_t num_cols = de.shape()[1]; + list = PyList_New(num_rows); + for(size_t i = 0; i < num_rows; ++i) { + PyObject* row = PyList_New(num_cols); + PyList_SetItem(list, i, row); + for(size_t j = 0; j < num_cols; ++j) { + PyList_SetItem(row, j, PyFloat_FromDouble(de.at(i,j))); + } + } + } + return list; + } + +#else // With numpy + // vector implementation + template + PyObject* get_array_impl(const std::vector& v) { + detail::_interpreter::get(); + NPY_TYPES type = select_npy_type::type; + if (type == NPY_NOTYPE) { + std::vector vd(v.size()); + npy_intp vsize = v.size(); + std::copy(v.begin(), v.end(), vd.begin()); + PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void*)(vd.data())); + return varray; + } + + npy_intp vsize = v.size(); + PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data())); + return varray; + } + + // lvalue xexpression implementation + template + PyObject* get_array_impl(const xt::xexpression& e) { + const E& de = e.derived_cast(); + // Implementation for evaluated lvalue using de + assert(de.dimension() <= 2); + detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work + + if (de.dimension() == 1) { + NPY_TYPES type = select_npy_type::value_type>::type; + if (type == NPY_NOTYPE) { + std::vector vd(de.size()); + npy_intp vsize = de.size(); + std::copy(de.begin(), de.end(), vd.begin()); + PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void*)(vd.data())); + return varray; + } + + npy_intp vsize = de.size(); + PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(de.data())); + return varray; + } else { + if (de.size() < 1) throw std::runtime_error("get_2d_array v too small"); + + npy_intp vsize[2] = {static_cast(de.shape()[0]), + static_cast(de.shape()[1])}; + + PyArrayObject *varray = + (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE); + + double *vd_begin = static_cast(PyArray_DATA(varray)); + + for (const std::size_t v_row_num : xt::arange(de.shape()[0])) { + auto v_row = xt::view(de, v_row_num); + if (v_row.size() != static_cast(vsize[1])) + throw std::runtime_error("Missmatched array size"); + std::copy(v_row.begin(), v_row.end(), vd_begin); + vd_begin += vsize[1]; + } + + return reinterpret_cast(varray); + } + } + +#endif // WITHOUT_NUMPY + + // rvalue xexpression implementation + template + PyObject* get_array_impl(xt::xexpression&& e) { + E de = e.derived_cast(); + + PyObject* list; + if (de.dimension() == 1) { + list = PyList_New(de.size()); + for(size_t i = 0; i < de.size(); ++i) { + PyList_SetItem(list, i, PyFloat_FromDouble(de.at(i))); + } + } else { + std::size_t num_rows = de.shape()[0]; + std::size_t num_cols = de.shape()[1]; + list = PyList_New(num_rows); + for(size_t i = 0; i < num_rows; ++i) { + PyObject* row = PyList_New(num_cols); + PyList_SetItem(list, i, row); + for(size_t j = 0; j < num_cols; ++j) { + PyList_SetItem(row, j, PyFloat_FromDouble(de.at(i,j))); + } + } + } + return list; + } + + // xtensor objects + template )> + PyObject* get_array(E&& e) { + return get_array_impl(xt::eval(std::forward(e))); + } + + // vectors + template >)> + PyObject* get_array(E&& e) { + return get_array_impl(std::forward(e)); + } + + + template + bool plot(E1&& x, E2&& y, const std::map& keywords) { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + // using numpy arrays + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + // construct positional args + PyObject* args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_plot, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + template + bool imshow(E&& im, const std::map &keywords = + std::map()) { + assert(im.dimension() == 2); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + // using numpy arrays + PyObject* imarray = get_array(std::forward(im)); + + // construct positional args + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, imarray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_imshow, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + +#ifndef WITHOUT_NUMPY + template + void plot_surface(E1&& x, E2&& y, E3&& z, + const std::map &keywords = + std::map()) { + // Passing a list of lists as the z argument results in python throwing: + // `AttributeError: 'list' object has no attribute 'ndim'` + static_assert(std::is_lvalue_reference::value, + "z argument to surface is required to be an lvalue"); + + // 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. + + // interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + 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()); + + // using numpy arrays + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + PyObject* zarray = get_array(std::forward(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(); + PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1)); + PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1)); + + PyObject *python_colormap_coolwarm = PyObject_GetAttrString( + detail::_interpreter::get().s_python_colormap, "coolwarm"); + + PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm); + + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + 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); + + 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 *plot_surface = PyObject_GetAttrString(axis, "plot_surface"); + if (!plot_surface) throw std::runtime_error("No surface"); + Py_INCREF(plot_surface); + PyObject *res = PyObject_Call(plot_surface, args, kwargs); + if (!res) throw std::runtime_error("failed surface"); + Py_DECREF(plot_surface); + + Py_DECREF(axis); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + } +#endif // WITHOUT_NUMPY + + + template + bool stem(E1&& x, E2&& y, const std::map& keywords) { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + // using numpy arrays + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + // construct positional args + PyObject* args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_stem, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool fill(E1&& x, E2&& y, const std::map& keywords) { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + // using numpy arrays + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + // construct positional args + PyObject* args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_fill, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool fill_between(E1&& x, E2&& y1, E3&& y2, const std::map& keywords) { + assert(x.size() == y1.size()); + assert(x.size() == y2.size()); + detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work + + // using numpy arrays + PyObject* xarray = get_array(std::forward(x)); + PyObject* y1array = get_array(std::forward(y1)); + PyObject* y2array = get_array(std::forward(y2)); + + // construct positional args + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, y1array); + PyTuple_SetItem(args, 2, y2array); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_fill_between, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool hist(E&& y, long bins=10, std::string color="b", double alpha=1.0, bool cumulative=false) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* yarray = get_array(std::forward(y)); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); + PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); + PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); + PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False); + + PyObject* plot_args = PyTuple_New(1); + + PyTuple_SetItem(plot_args, 0, yarray); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool scatter(E1&& x, E2&& y, const double s=1.0) { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); + + 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 bar(E&& y, std::string ec = "black", std::string ls = "-", double lw = 1.0, + const std::map& keywords = {}) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* yarray = get_array(std::forward(y)); + PyObject* xarray = get_array(xt::arange::value_type>(y.size())); + + 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)); + + 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_bar, 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 = {}) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyFloat_FromDouble(it->second)); + } + + PyObject* plot_args = PyTuple_New(0); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool named_hist(std::string label, E&& y, long bins=10, std::string color="b", double alpha=1.0) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* yarray = get_array(std::forward(y)); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str())); + PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); + PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); + PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); + + PyObject* plot_args = PyTuple_New(1); + PyTuple_SetItem(plot_args, 0, yarray); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) Py_DECREF(res); + + return res; + } + + + // enable_if used to fix overload resolution on variadic call to plot (see below) when the + // last argument is a callable, such as a lambda + template::value>> + bool plot(E1&& x, E2&& y, const std::string& s = "") { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_plot, plot_args); + + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool quiver(E1&& x, E2&& y, E3&& u, E4&& w, + const std::map& keywords = {}) { + assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + PyObject* uarray = get_array(std::forward(u)); + PyObject* warray = get_array(std::forward(w)); + + PyObject* plot_args = PyTuple_New(4); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, uarray); + PyTuple_SetItem(plot_args, 3, warray); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool stem(E1&& x, E2&& y, const std::string& s = "") { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_stem, plot_args); + + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool semilogx(E1&& x, E2&& y, const std::string& s = "") { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_semilogx, plot_args); + + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool semilogy(E1&& x, E2&& y, const std::string& s = "") { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_semilogy, plot_args); + + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool loglog(E1&& x, E2&& y, const std::string& s = "") { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(s.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_loglog, plot_args); + + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool errorbar(E1&& x, E2&& y, E3&& yerr, + const std::map &keywords = {}) { + assert(x.size() == y.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + PyObject* yerrarray = get_array(std::forward(yerr)); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyDict_SetItemString(kwargs, "yerr", yerrarray); + + 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_errorbar, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (!res) throw std::runtime_error("Call to errorbar() failed."); + Py_DECREF(res); + + return res; + } + + + template + bool named_plot(const std::string& name, E&& y, const std::string& format = "") { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(2); + + PyTuple_SetItem(plot_args, 0, yarray); + PyTuple_SetItem(plot_args, 1, pystring); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool named_plot(const std::string& name, E1&& x, E2&& y, const std::string& format = "") { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool named_semilogx(const std::string& name, E1&& x, E2&& y, const std::string& format = "") { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool named_semilogy(const std::string& name, E1&& x, E2&& y, const std::string& format = "") { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool named_loglog(const std::string& name, E1&& x, E2&& y, const std::string& format = "") { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + + + template + bool plot(E&& y, const std::string& format = "") { + xt::xtensor x = xt::arange(y.size()); + return plot(std::move(x), std::forward(y), format); + } + + + template + bool stem(E&& y, const std::string& format = "") { + xt::xtensor x = xt::arange(y.size()); + return stem(std::move(x), std::forward(y), format); + } + + + template + void text(Numeric x, Numeric y, const std::string& s = "") { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(y)); + PyTuple_SetItem(args, 2, PyString_FromString(s.c_str())); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_text, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to text() failed."); + Py_DECREF(res); + } + + + inline long figure(long number = -1) { + PyObject *res; + if (number == -1) { + res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + assert(number > 0); + + // Make sure interpreter is initialised + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(number)); + res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, args); + Py_DECREF(args); + } + + if (!res) throw std::runtime_error("Call to figure() failed."); + + PyObject* num = PyObject_GetAttrString(res, "number"); + if (!num) throw std::runtime_error("Could not get number attribute of figure object"); + const long figureNumber = PyLong_AsLong(num); + + Py_DECREF(num); + Py_DECREF(res); + + return figureNumber; + } + + + inline bool fignum_exists(long number) { + // Make sure interpreter is initialised + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(number)); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_fignum_exists, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to fignum_exists() failed."); + bool ret = PyObject_IsTrue(res); + Py_DECREF(res); + + return ret; + } + + + inline void figure_size(size_t w, size_t h) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + const size_t dpi = 100; + PyObject* size = PyTuple_New(2); + PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi)); + PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi)); + + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "figsize", size); + PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi)); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple, kwargs); + + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to figure_size() failed."); + Py_DECREF(res); + } + + + inline void legend() { + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_legend, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to legend() failed."); + Py_DECREF(res); + } + + + template + void ylim(Numeric left, Numeric right) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(left)); + PyList_SetItem(list, 1, PyFloat_FromDouble(right)); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_ylim, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to ylim() failed."); + Py_DECREF(res); + } + + + template + void xlim(Numeric left, Numeric right) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(left)); + PyList_SetItem(list, 1, PyFloat_FromDouble(right)); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_xlim, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to xlim() failed."); + Py_DECREF(res); + } + + + inline std::array xlim() { + //interpreter needs to be initialized for the numpy commands to work + 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); + + std::array arr{PyFloat_AsDouble(left), PyFloat_AsDouble(right)}; + + if (!res) throw std::runtime_error("Call to xlim() failed."); + Py_DECREF(res); + + return arr; + } + + + inline std::array ylim() { + //interpreter needs to be initialized for the numpy commands to work + 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); + + std::array arr{PyFloat_AsDouble(left), PyFloat_AsDouble(right)}; + + if (!res) throw std::runtime_error("Call to ylim() failed."); + Py_DECREF(res); + + return arr; + } + + + template + inline void xticks(E&& ticks, const std::vector &labels = {}, + const std::map& keywords = {}) { + assert(labels.size() == 0 || ticks.size() == labels.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + // using numpy array + PyObject* ticksarray = get_array(std::forward(ticks)); + + PyObject* args; + if (labels.size() == 0) { + // construct positional args + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, ticksarray); + } else { + // make tuple of tick labels + PyObject* labelstuple = PyTuple_New(labels.size()); + for (size_t i = 0; i < labels.size(); i++) + PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); + + // construct positional args + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, ticksarray); + PyTuple_SetItem(args, 1, labelstuple); + } + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_xticks, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to xticks() failed"); + Py_DECREF(res); + } + + + template + inline void xticks(E&& ticks, const std::map& keywords) { + xticks(std::forward(ticks), {}, keywords); + } + + + template + inline void yticks(E&& ticks, const std::vector &labels = {}, + const std::map& keywords = {}) { + assert(labels.size() == 0 || ticks.size() == labels.size()); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + // using numpy array + PyObject* ticksarray = get_array(std::forward(ticks)); + + PyObject* args; + if (labels.size() == 0) { + // construct positional args + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, ticksarray); + } else { + // make tuple of tick labels + PyObject* labelstuple = PyTuple_New(labels.size()); + for (size_t i = 0; i < labels.size(); i++) + PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); + + // construct positional args + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, ticksarray); + PyTuple_SetItem(args, 1, labelstuple); + } + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_yticks, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to yticks() failed"); + Py_DECREF(res); + } + + + template + inline void yticks(E&& ticks, const std::map& keywords) { + yticks(std::forward(ticks), {}, keywords); + } + + + inline void subplot(long nrows, long ncols, long plot_number) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + // construct positional args + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_subplot, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to subplot() failed."); + Py_DECREF(res); + } + + + inline void title(const std::string &titlestr, + const std::map &keywords = {}) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* pytitlestr = PyString_FromString(titlestr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pytitlestr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_title, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to title() failed."); + Py_DECREF(res); + } + + + inline void suptitle(const std::string &suptitlestr, + const std::map &keywords = {}) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pysuptitlestr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_suptitle, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to suptitle() failed."); + Py_DECREF(res); + } + + + inline void axis(const std::string &axisstr) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* str = PyString_FromString(axisstr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, str); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_axis, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to title() failed."); + Py_DECREF(res); + } + + + inline void xlabel(const std::string &str, + const std::map &keywords = {}) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call + (detail::_interpreter::get().s_python_function_xlabel, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to xlabel() failed."); + Py_DECREF(res); + } + + + inline void ylabel(const std::string &str, + const std::map& keywords = {}) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + PyObject* kwargs = PyDict_New(); + for (auto it = keywords.cbegin(); it != keywords.cend(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call + (detail::_interpreter::get().s_python_function_ylabel, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to ylabel() failed."); + Py_DECREF(res); + } + + + inline void grid(bool flag) { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* pyflag = flag ? Py_True : Py_False; + Py_INCREF(pyflag); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyflag); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_grid, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to grid() failed."); + Py_DECREF(res); + } + + + inline void show(const bool block = true) { + PyObject* res; + if (block) { + res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_show, + detail::_interpreter::get().s_python_empty_tuple); + } else { + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "block", Py_False); + res = PyObject_Call( + detail::_interpreter::get().s_python_function_show, + detail::_interpreter::get().s_python_empty_tuple, kwargs); + Py_DECREF(kwargs); + } + + if (!res) throw std::runtime_error("Call to show() failed."); + Py_DECREF(res); + } + + + inline void close() { + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_close, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to close() failed."); + Py_DECREF(res); + } + + + inline void xkcd() { + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* res; + PyObject *kwargs = PyDict_New(); + + res = PyObject_Call( + detail::_interpreter::get().s_python_function_xkcd, + detail::_interpreter::get().s_python_empty_tuple, kwargs); + + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to show() failed."); + Py_DECREF(res); + } + + + inline void draw() { + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_draw, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to draw() failed."); + Py_DECREF(res); + } + + + template + inline void pause(Numeric interval) { + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval)); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_pause, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to pause() failed."); + Py_DECREF(res); + } + + + inline void save(const std::string& filename) { + PyObject* pyfilename = PyString_FromString(filename.c_str()); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyfilename); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_save, args); + + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to save() failed."); + Py_DECREF(res); + } + + + inline void clf() { + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_clf, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to clf() failed."); + Py_DECREF(res); + } + + + inline void ion() { + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_ion, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to ion() failed."); + Py_DECREF(res); + } + + + inline std::vector> ginput(const int numClicks = 1, + const std::map& keywords = {}) { + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks)); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(auto it = keywords.cbegin(); it != keywords.cend(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_ginput, args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(args); + if (!res) throw std::runtime_error("Call to ginput() failed."); + + const size_t len = PyList_Size(res); + std::vector> out; + out.reserve(len); + for (size_t i = 0; i < len; i++) { + PyObject *current = PyList_GetItem(res, i); + std::array position; + position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0)); + position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1)); + out.push_back(position); + } + + Py_DECREF(res); + + return out; + } + + + // Actually, is there any reason not to call this automatically for every plot? + inline void tight_layout() { + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_tight_layout, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) throw std::runtime_error("Call to tight_layout() failed."); + Py_DECREF(res); + } + + + // Support for variadic plot() and initializer lists: +namespace detail { + + template + using is_function = typename std::is_function>>::type; + + template + struct is_callable_impl; + + template + struct is_callable_impl { + typedef is_function type; + }; // a non-object is callable iff it is a function + + template + struct is_callable_impl { + struct Fallback { void operator()(); }; + struct Derived : T, Fallback { }; + + template struct Check; + + // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match + template + static std::true_type test( ... ); + + template + static std::false_type test( Check* ); + + public: + typedef decltype(test(nullptr)) type; + typedef decltype(&Fallback::operator()) dtype; + static constexpr bool value = type::value; + }; // an object is callable iff it defines operator() + + template + struct is_callable { + // dispatch to is_callable_impl or is_callable_impl + // depending on whether T is of class type or not + typedef typename is_callable_impl::value, T>::type type; + }; + + template + struct plot_impl { }; + + template<> + struct plot_impl { + template + bool operator()(IterableX&& x, IterableY&& y, const std::string& format) { + // 2-phase lookup for distance, begin, end + using std::distance; + using std::begin; + using std::end; + + auto xs = distance(begin(x), end(x)); + auto ys = distance(begin(y), end(y)); + assert(xs == ys && "x and y data must have the same number of elements!"); + + //interpreter needs to be initialized for the numpy commands to work + detail::_interpreter::get(); + + PyObject* xlist = PyList_New(xs); + PyObject* ylist = PyList_New(ys); + PyObject* pystring = PyString_FromString(format.c_str()); + + auto itx = x.begin(); + auto ity = y.begin(); + for(size_t i = 0; i < xs; ++i) { + PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++)); + PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++)); + } + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xlist); + PyTuple_SetItem(plot_args, 1, ylist); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_plot, plot_args); + + Py_DECREF(plot_args); + if (res) Py_DECREF(res); + + return res; + } + }; + + + template<> + struct plot_impl { + template + bool operator()(Iterable&& ticks, Callable&& f, const std::string& format) { + if (begin(ticks) == end(ticks)) return true; + + // We could use additional meta-programming to deduce the correct element type of y, + // but all values have to be convertible to double anyways + std::size_t size = std::distance(begin(ticks), end(ticks)); + std::array shape{size}; + xt::xtensor y(shape); + std::size_t idx = 0; + for (auto x : ticks) y[idx++] = f(x); + return plot_impl()(std::forward(ticks), std::move(y), format); + } + }; + +} // end namespace detail + + // recursion stop for the above + template + bool plot() { return true; } + + template + bool plot(E1&& a, E2&& b, const std::string& format, Args... args) { + + return detail::plot_impl< + std::integral_constant>::type, + xtl::negation> + >::value> + >()(std::forward(a), std::forward(b), format) && plot(std::forward(args)...); + } + + // + // This group of plot() functions is needed to support initializer lists, i.e. calling + // plot( {1,2,3,4} ) + /// + inline bool plot(std::vector x, std::vector y, const std::string& format = "") { + xt::xtensor x_adapt = xt::adapt(x); + xt::xtensor y_adapt = xt::adapt(y); + return plot(std::move(x_adapt), std::move(y_adapt), format); + } + + inline bool plot(std::vector y, const std::string& format = "") { + xt::xtensor y_adapt = xt::adapt(y); + return plot(std::move(y_adapt), format); + } + + + inline bool plot(std::vector x, std::vector y, + const std::map& keywords) { + xt::xtensor x_adapt = xt::adapt(x); + xt::xtensor y_adapt = xt::adapt(y); + return plot(std::move(x_adapt), std::move(y_adapt), keywords); + } + + + // + // This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting + // + class Plot { + public: + // default initialization with plot label, some data and format + template + Plot(const std::string& name, E1&& x, E2&& y, const std::string& format = "") { + assert(x.size() == y.size()); + + PyObject* kwargs = PyDict_New(); + if (name != "") + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_Call( + detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + + if (res) { + line= PyList_GetItem(res, 0); + + if (line) + set_data_fct = PyObject_GetAttrString(line,"set_data"); + else + Py_DECREF(line); + Py_DECREF(res); + } + } + + // shorter initialization with name or format only + // basically calls line, = plot([], []) + Plot(const std::string& name = "", const std::string& format = "") + : Plot(name, xt::xtensor(), xt::xtensor(), format) {} + + template + bool update(E1&& x, E2&& y) { + assert(x.size() == y.size()); + if (set_data_fct) { + PyObject* xarray = get_array(std::forward(x)); + PyObject* yarray = get_array(std::forward(y)); + + PyObject* plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject* res = PyObject_CallObject(set_data_fct, plot_args); + if (res) Py_DECREF(res); + return res; + } + return false; + } + + // clears the plot but keep it available + bool clear() { + return update(xt::xtensor(), xt::xtensor()); + } + + // definitely remove this line + void remove() { + if (line) { + auto remove_fct = PyObject_GetAttrString(line,"remove"); + PyObject* args = PyTuple_New(0); + PyObject* res = PyObject_CallObject(remove_fct, args); + if (res) Py_DECREF(res); + } + decref(); + } + + ~Plot() { + decref(); + } + private: + + void decref() { + if (line) + Py_DECREF(line); + if (set_data_fct) + Py_DECREF(set_data_fct); + } + + PyObject* line = nullptr; + PyObject* set_data_fct = nullptr; + }; +} // end namespace matplotlibcpp diff --git a/matplotlibcpp.h b/matplotlibcpp.h deleted file mode 100644 index 2797295..0000000 --- a/matplotlibcpp.h +++ /dev/null @@ -1,1811 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include // requires c++11 support -#include - -#include - -#ifndef WITHOUT_NUMPY -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -# include -#endif // WITHOUT_NUMPY - -#if PY_MAJOR_VERSION >= 3 -# define PyString_FromString PyUnicode_FromString -# define PyInt_FromLong PyLong_FromLong -# define PyString_FromString PyUnicode_FromString -#endif - - -namespace matplotlibcpp { -namespace detail { - -static std::string s_backend; - -struct _interpreter { - PyObject *s_python_function_show; - PyObject *s_python_function_close; - PyObject *s_python_function_draw; - PyObject *s_python_function_pause; - PyObject *s_python_function_save; - PyObject *s_python_function_figure; - PyObject *s_python_function_fignum_exists; - PyObject *s_python_function_plot; - PyObject *s_python_function_quiver; - PyObject *s_python_function_semilogx; - PyObject *s_python_function_semilogy; - PyObject *s_python_function_loglog; - PyObject *s_python_function_fill; - PyObject *s_python_function_fill_between; - PyObject *s_python_function_hist; - PyObject *s_python_function_scatter; - PyObject *s_python_function_subplot; - PyObject *s_python_function_legend; - PyObject *s_python_function_xlim; - PyObject *s_python_function_ion; - PyObject *s_python_function_ginput; - PyObject *s_python_function_ylim; - PyObject *s_python_function_title; - PyObject *s_python_function_axis; - PyObject *s_python_function_xlabel; - PyObject *s_python_function_ylabel; - PyObject *s_python_function_xticks; - PyObject *s_python_function_yticks; - PyObject *s_python_function_grid; - PyObject *s_python_function_clf; - PyObject *s_python_function_errorbar; - PyObject *s_python_function_annotate; - PyObject *s_python_function_tight_layout; - PyObject *s_python_colormap; - PyObject *s_python_empty_tuple; - PyObject *s_python_function_stem; - PyObject *s_python_function_xkcd; - PyObject *s_python_function_text; - PyObject *s_python_function_suptitle; - PyObject *s_python_function_bar; - PyObject *s_python_function_subplots_adjust; - - - /* 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 - */ - - static _interpreter& get() { - static _interpreter ctx; - return ctx; - } - -private: - -#ifndef WITHOUT_NUMPY -# if PY_MAJOR_VERSION >= 3 - - void *import_numpy() { - import_array(); // initialize C-API - return NULL; - } - -# else - - void import_numpy() { - import_array(); // initialize C-API - } - -# endif -#endif - - _interpreter() { - - // optional but recommended -#if PY_MAJOR_VERSION >= 3 - wchar_t name[] = L"plotting"; -#else - char name[] = "plotting"; -#endif - Py_SetProgramName(name); - Py_Initialize(); - -#ifndef WITHOUT_NUMPY - import_numpy(); // initialize numpy C-API -#endif - - PyObject* matplotlibname = PyString_FromString("matplotlib"); - PyObject* pyplotname = PyString_FromString("matplotlib.pyplot"); - PyObject* cmname = PyString_FromString("matplotlib.cm"); - PyObject* pylabname = PyString_FromString("pylab"); - if (!pyplotname || !pylabname || !matplotlibname || !cmname) { - throw std::runtime_error("couldnt create string"); - } - - PyObject* matplotlib = PyImport_Import(matplotlibname); - Py_DECREF(matplotlibname); - if (!matplotlib) { - PyErr_Print(); - throw std::runtime_error("Error loading module matplotlib!"); - } - - // matplotlib.use() must be called *before* pylab, matplotlib.pyplot, - // or matplotlib.backends is imported for the first time - if (!s_backend.empty()) { - 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!"); } - - s_python_colormap = PyImport_Import(cmname); - Py_DECREF(cmname); - if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); } - - PyObject* pylabmod = PyImport_Import(pylabname); - Py_DECREF(pylabname); - if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); } - - s_python_function_show = PyObject_GetAttrString(pymod, "show"); - s_python_function_close = PyObject_GetAttrString(pymod, "close"); - s_python_function_draw = PyObject_GetAttrString(pymod, "draw"); - s_python_function_pause = PyObject_GetAttrString(pymod, "pause"); - s_python_function_figure = PyObject_GetAttrString(pymod, "figure"); - s_python_function_fignum_exists = PyObject_GetAttrString(pymod, "fignum_exists"); - s_python_function_plot = PyObject_GetAttrString(pymod, "plot"); - s_python_function_quiver = PyObject_GetAttrString(pymod, "quiver"); - s_python_function_semilogx = PyObject_GetAttrString(pymod, "semilogx"); - s_python_function_semilogy = PyObject_GetAttrString(pymod, "semilogy"); - s_python_function_loglog = PyObject_GetAttrString(pymod, "loglog"); - s_python_function_fill = PyObject_GetAttrString(pymod, "fill"); - s_python_function_fill_between = PyObject_GetAttrString(pymod, "fill_between"); - s_python_function_hist = PyObject_GetAttrString(pymod,"hist"); - s_python_function_scatter = PyObject_GetAttrString(pymod,"scatter"); - s_python_function_subplot = PyObject_GetAttrString(pymod, "subplot"); - s_python_function_legend = PyObject_GetAttrString(pymod, "legend"); - s_python_function_ylim = PyObject_GetAttrString(pymod, "ylim"); - s_python_function_title = PyObject_GetAttrString(pymod, "title"); - s_python_function_axis = PyObject_GetAttrString(pymod, "axis"); - s_python_function_xlabel = PyObject_GetAttrString(pymod, "xlabel"); - s_python_function_ylabel = PyObject_GetAttrString(pymod, "ylabel"); - s_python_function_xticks = PyObject_GetAttrString(pymod, "xticks"); - s_python_function_yticks = PyObject_GetAttrString(pymod, "yticks"); - s_python_function_grid = PyObject_GetAttrString(pymod, "grid"); - s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim"); - s_python_function_ion = PyObject_GetAttrString(pymod, "ion"); - s_python_function_ginput = PyObject_GetAttrString(pymod, "ginput"); - s_python_function_save = PyObject_GetAttrString(pylabmod, "savefig"); - s_python_function_annotate = PyObject_GetAttrString(pymod,"annotate"); - s_python_function_clf = PyObject_GetAttrString(pymod, "clf"); - s_python_function_errorbar = PyObject_GetAttrString(pymod, "errorbar"); - s_python_function_tight_layout = PyObject_GetAttrString(pymod, "tight_layout"); - s_python_function_stem = PyObject_GetAttrString(pymod, "stem"); - s_python_function_xkcd = PyObject_GetAttrString(pymod, "xkcd"); - s_python_function_text = PyObject_GetAttrString(pymod, "text"); - s_python_function_suptitle = PyObject_GetAttrString(pymod, "suptitle"); - s_python_function_bar = PyObject_GetAttrString(pymod,"bar"); - s_python_function_subplots_adjust = PyObject_GetAttrString(pymod,"subplots_adjust"); - - if( !s_python_function_show - || !s_python_function_close - || !s_python_function_draw - || !s_python_function_pause - || !s_python_function_figure - || !s_python_function_fignum_exists - || !s_python_function_plot - || !s_python_function_quiver - || !s_python_function_semilogx - || !s_python_function_semilogy - || !s_python_function_loglog - || !s_python_function_fill - || !s_python_function_fill_between - || !s_python_function_subplot - || !s_python_function_legend - || !s_python_function_ylim - || !s_python_function_title - || !s_python_function_axis - || !s_python_function_xlabel - || !s_python_function_ylabel - || !s_python_function_grid - || !s_python_function_xlim - || !s_python_function_ion - || !s_python_function_ginput - || !s_python_function_save - || !s_python_function_clf - || !s_python_function_annotate - || !s_python_function_errorbar - || !s_python_function_errorbar - || !s_python_function_tight_layout - || !s_python_function_stem - || !s_python_function_xkcd - || !s_python_function_text - || !s_python_function_suptitle - || !s_python_function_bar - || !s_python_function_subplots_adjust - ) { throw std::runtime_error("Couldn't find required function!"); } - - if ( !PyFunction_Check(s_python_function_show) - || !PyFunction_Check(s_python_function_close) - || !PyFunction_Check(s_python_function_draw) - || !PyFunction_Check(s_python_function_pause) - || !PyFunction_Check(s_python_function_figure) - || !PyFunction_Check(s_python_function_fignum_exists) - || !PyFunction_Check(s_python_function_plot) - || !PyFunction_Check(s_python_function_quiver) - || !PyFunction_Check(s_python_function_semilogx) - || !PyFunction_Check(s_python_function_semilogy) - || !PyFunction_Check(s_python_function_loglog) - || !PyFunction_Check(s_python_function_fill) - || !PyFunction_Check(s_python_function_fill_between) - || !PyFunction_Check(s_python_function_subplot) - || !PyFunction_Check(s_python_function_legend) - || !PyFunction_Check(s_python_function_annotate) - || !PyFunction_Check(s_python_function_ylim) - || !PyFunction_Check(s_python_function_title) - || !PyFunction_Check(s_python_function_axis) - || !PyFunction_Check(s_python_function_xlabel) - || !PyFunction_Check(s_python_function_ylabel) - || !PyFunction_Check(s_python_function_grid) - || !PyFunction_Check(s_python_function_xlim) - || !PyFunction_Check(s_python_function_ion) - || !PyFunction_Check(s_python_function_ginput) - || !PyFunction_Check(s_python_function_save) - || !PyFunction_Check(s_python_function_clf) - || !PyFunction_Check(s_python_function_tight_layout) - || !PyFunction_Check(s_python_function_errorbar) - || !PyFunction_Check(s_python_function_stem) - || !PyFunction_Check(s_python_function_xkcd) - || !PyFunction_Check(s_python_function_text) - || !PyFunction_Check(s_python_function_suptitle) - || !PyFunction_Check(s_python_function_bar) - || !PyFunction_Check(s_python_function_subplots_adjust) - ) { throw std::runtime_error("Python object is unexpectedly not a PyFunction."); } - - s_python_empty_tuple = PyTuple_New(0); - } - - ~_interpreter() { - Py_Finalize(); - } -}; - -} // end namespace detail - -// must be called before the first regular call to matplotlib to have any effect -inline void backend(const std::string& name) -{ - detail::s_backend = name; -} - -inline bool annotate(std::string annotation, double x, double y) -{ - PyObject * xy = PyTuple_New(2); - PyObject * str = PyString_FromString(annotation.c_str()); - - PyTuple_SetItem(xy,0,PyFloat_FromDouble(x)); - PyTuple_SetItem(xy,1,PyFloat_FromDouble(y)); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "xy", xy); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, str); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - - if(res) Py_DECREF(res); - - return res; -} - -#ifndef WITHOUT_NUMPY -// Type selector for numpy array conversion -template struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default -template <> struct select_npy_type { const static NPY_TYPES type = NPY_DOUBLE; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_FLOAT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_BOOL; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT8; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_SHORT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT8; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_USHORT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_ULONG; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; - -template -PyObject* get_array(const std::vector& v) -{ - detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work - NPY_TYPES type = select_npy_type::type; - if (type == NPY_NOTYPE) - { - std::vector vd(v.size()); - npy_intp vsize = v.size(); - std::copy(v.begin(),v.end(),vd.begin()); - PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void*)(vd.data())); - return varray; - } - - npy_intp vsize = v.size(); - PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data())); - return varray; -} - -template -PyObject* get_2darray(const std::vector<::std::vector>& v) -{ - detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work - if (v.size() < 1) throw std::runtime_error("get_2d_array v too small"); - - npy_intp vsize[2] = {static_cast(v.size()), - static_cast(v[0].size())}; - - PyArrayObject *varray = - (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE); - - double *vd_begin = static_cast(PyArray_DATA(varray)); - - for (const ::std::vector &v_row : v) { - if (v_row.size() != static_cast(vsize[1])) - throw std::runtime_error("Missmatched array size"); - std::copy(v_row.begin(), v_row.end(), vd_begin); - vd_begin += vsize[1]; - } - - return reinterpret_cast(varray); -} - -#else // fallback if we don't have numpy: copy every element of the given vector - -template -PyObject* get_array(const std::vector& v) -{ - PyObject* list = PyList_New(v.size()); - for(size_t i = 0; i < v.size(); ++i) { - PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i))); - } - return list; -} - -#endif // WITHOUT_NUMPY - -template -bool plot(const std::vector &x, const std::vector &y, const std::map& keywords) -{ - assert(x.size() == y.size()); - - // using numpy arrays - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - - // 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_plot, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; -} - -template -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()) -{ - // 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()); - - // using numpy arrays - PyObject *xarray = get_2darray(x); - PyObject *yarray = get_2darray(y); - PyObject *zarray = 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(); - PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1)); - PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1)); - - 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 *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); - - PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface"); - if (!plot_surface) throw std::runtime_error("No surface"); - Py_INCREF(plot_surface); - PyObject *res = PyObject_Call(plot_surface, args, kwargs); - if (!res) throw std::runtime_error("failed surface"); - Py_DECREF(plot_surface); - - Py_DECREF(axis); - Py_DECREF(args); - Py_DECREF(kwargs); - if (res) Py_DECREF(res); -} - -template -bool stem(const std::vector &x, const std::vector &y, const std::map& keywords) -{ - assert(x.size() == y.size()); - - // using numpy arrays - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - - // 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_stem, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - if (res) - Py_DECREF(res); - - return res; -} - -template< typename Numeric > -bool fill(const std::vector& x, const std::vector& y, const std::map& keywords) -{ - assert(x.size() == y.size()); - - // using numpy arrays - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - - if (res) Py_DECREF(res); - - return res; -} - -template< typename Numeric > -bool fill_between(const std::vector& x, const std::vector& y1, const std::vector& y2, const std::map& keywords) -{ - assert(x.size() == y1.size()); - assert(x.size() == y2.size()); - - // using numpy arrays - PyObject* xarray = get_array(x); - PyObject* y1array = get_array(y1); - PyObject* y2array = get_array(y2); - - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, y1array); - PyTuple_SetItem(args, 2, y2array); - - // 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())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; -} - -template< typename Numeric> -bool hist(const std::vector& y, long bins=10,std::string color="b", - double alpha=1.0, bool cumulative=false) -{ - - PyObject* yarray = get_array(y); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); - PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); - PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); - PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False); - - PyObject* plot_args = PyTuple_New(1); - - PyTuple_SetItem(plot_args, 0, yarray); - - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, 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 double s=1.0) // The marker size in points**2 -{ - assert(x.size() == y.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); - - 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< typename Numeric> -bool bar(const std::vector& y, std::string ec = "black", std::string ls = "-", double lw = 1.0, - const std::map& keywords = {}) -{ - PyObject* yarray = get_array(y); - - std::vector x; - for (int i = 0; i < y.size(); i++) - x.push_back(i); - - PyObject* xarray = get_array(x); - - 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)); - - 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_bar, 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 = {}) -{ - - PyObject* kwargs = PyDict_New(); - for (std::map::const_iterator it = - keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyFloat_FromDouble(it->second)); - } - - - PyObject* plot_args = PyTuple_New(0); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; -} - -template< typename Numeric> -bool named_hist(std::string label,const std::vector& y, long bins=10, std::string color="b", double alpha=1.0) -{ - PyObject* yarray = get_array(y); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str())); - PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); - PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); - PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); - - - PyObject* plot_args = PyTuple_New(1); - PyTuple_SetItem(plot_args, 0, yarray); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; -} - -template -bool plot(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool quiver(const std::vector& x, const std::vector& y, const std::vector& u, const std::vector& w, const std::map& keywords = {}) -{ - assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - PyObject* uarray = get_array(u); - PyObject* warray = get_array(w); - - PyObject* plot_args = PyTuple_New(4); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, uarray); - PyTuple_SetItem(plot_args, 3, warray); - - // 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())); - } - - PyObject* res = PyObject_Call( - detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs); - - 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 = "") -{ - assert(x.size() == y.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_stem, plot_args); - - Py_DECREF(plot_args); - if (res) - Py_DECREF(res); - - return res; -} - -template -bool semilogx(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool semilogy(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogy, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool loglog(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_loglog, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool errorbar(const std::vector &x, const std::vector &y, const std::vector &yerr, const std::map &keywords = {}) -{ - assert(x.size() == y.size()); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - PyObject* yerrarray = get_array(yerr); - - // 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())); - } - - PyDict_SetItemString(kwargs, "yerr", yerrarray); - - 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_errorbar, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - - if (res) - Py_DECREF(res); - else - throw std::runtime_error("Call to errorbar() failed."); - - return res; -} - -template -bool named_plot(const std::string& name, const std::vector& y, const std::string& format = "") -{ - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(format.c_str()); - - PyObject* plot_args = PyTuple_New(2); - - PyTuple_SetItem(plot_args, 0, yarray); - PyTuple_SetItem(plot_args, 1, pystring); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); - - return res; -} - -template -bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(format.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); - - return res; -} - -template -bool named_semilogx(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(format.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); - - return res; -} - -template -bool named_semilogy(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(format.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); - - return res; -} - -template -bool named_loglog(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(format.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); - - return res; -} - -template -bool plot(const std::vector& y, const std::string& format = "") -{ - std::vector x(y.size()); - for(size_t i=0; i -bool stem(const std::vector& y, const std::string& format = "") -{ - std::vector x(y.size()); - for (size_t i = 0; i < x.size(); ++i) x.at(i) = i; - return stem(x, y, format); -} - -template -void text(Numeric x, Numeric y, const std::string& s = "") -{ - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(y)); - PyTuple_SetItem(args, 2, PyString_FromString(s.c_str())); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_text, args); - if(!res) throw std::runtime_error("Call to text() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - - -inline long figure(long number = -1) -{ - PyObject *res; - if (number == -1) - res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple); - else { - assert(number > 0); - - // Make sure interpreter is initialised - detail::_interpreter::get(); - - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyLong_FromLong(number)); - res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, args); - Py_DECREF(args); - } - - if(!res) throw std::runtime_error("Call to figure() failed."); - - PyObject* num = PyObject_GetAttrString(res, "number"); - if (!num) throw std::runtime_error("Could not get number attribute of figure object"); - const long figureNumber = PyLong_AsLong(num); - - Py_DECREF(num); - Py_DECREF(res); - - return figureNumber; -} - -inline bool fignum_exists(long number) -{ - // Make sure interpreter is initialised - detail::_interpreter::get(); - - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyLong_FromLong(number)); - PyObject *res = PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, args); - if(!res) throw std::runtime_error("Call to fignum_exists() failed."); - - bool ret = PyObject_IsTrue(res); - Py_DECREF(res); - Py_DECREF(args); - - return ret; -} - -inline void figure_size(size_t w, size_t h) -{ - const size_t dpi = 100; - PyObject* size = PyTuple_New(2); - PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi)); - PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi)); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "figsize", size); - PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi)); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple, kwargs); - - Py_DECREF(kwargs); - - if(!res) throw std::runtime_error("Call to figure_size() failed."); - Py_DECREF(res); -} - -inline void legend() -{ - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple); - if(!res) throw std::runtime_error("Call to legend() failed."); - - Py_DECREF(res); -} - -template -void ylim(Numeric left, Numeric right) -{ - PyObject* list = PyList_New(2); - PyList_SetItem(list, 0, PyFloat_FromDouble(left)); - PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, list); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); - if(!res) throw std::runtime_error("Call to ylim() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -template -void xlim(Numeric left, Numeric right) -{ - PyObject* list = PyList_New(2); - PyList_SetItem(list, 0, PyFloat_FromDouble(left)); - PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, list); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); - if(!res) throw std::runtime_error("Call to xlim() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - - -inline double* xlim() -{ - 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; -} - - -inline double* ylim() -{ - 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; -} - -template -inline void xticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) -{ - assert(labels.size() == 0 || ticks.size() == labels.size()); - - // using numpy array - PyObject* ticksarray = get_array(ticks); - - PyObject* args; - if(labels.size() == 0) { - // construct positional args - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, ticksarray); - } else { - // make tuple of tick labels - PyObject* labelstuple = PyTuple_New(labels.size()); - for (size_t i = 0; i < labels.size(); i++) - PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); - - // construct positional args - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, ticksarray); - PyTuple_SetItem(args, 1, labelstuple); - } - - // 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_xticks, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - if(!res) throw std::runtime_error("Call to xticks() failed"); - - Py_DECREF(res); -} - -template -inline void xticks(const std::vector &ticks, const std::map& keywords) -{ - xticks(ticks, {}, keywords); -} - -template -inline void yticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) -{ - assert(labels.size() == 0 || ticks.size() == labels.size()); - - // using numpy array - PyObject* ticksarray = get_array(ticks); - - PyObject* args; - if(labels.size() == 0) { - // construct positional args - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, ticksarray); - } else { - // make tuple of tick labels - PyObject* labelstuple = PyTuple_New(labels.size()); - for (size_t i = 0; i < labels.size(); i++) - PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); - - // construct positional args - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, ticksarray); - PyTuple_SetItem(args, 1, labelstuple); - } - - // 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_yticks, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - if(!res) throw std::runtime_error("Call to yticks() failed"); - - Py_DECREF(res); -} - -template -inline void yticks(const std::vector &ticks, const std::map& keywords) -{ - yticks(ticks, {}, keywords); -} - -inline void subplot(long nrows, long ncols, long plot_number) -{ - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args); - if(!res) throw std::runtime_error("Call to subplot() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void title(const std::string &titlestr, const std::map &keywords = {}) -{ - PyObject* pytitlestr = PyString_FromString(titlestr.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pytitlestr); - - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_title, args, kwargs); - if(!res) throw std::runtime_error("Call to title() failed."); - - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); -} - -inline void suptitle(const std::string &suptitlestr, const std::map &keywords = {}) -{ - PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pysuptitlestr); - - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_suptitle, args, kwargs); - if(!res) throw std::runtime_error("Call to suptitle() failed."); - - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); -} - -inline void axis(const std::string &axisstr) -{ - PyObject* str = PyString_FromString(axisstr.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, str); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args); - if(!res) throw std::runtime_error("Call to title() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void xlabel(const std::string &str, const std::map &keywords = {}) -{ - PyObject* pystr = PyString_FromString(str.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pystr); - - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xlabel, args, kwargs); - if(!res) throw std::runtime_error("Call to xlabel() failed."); - - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); -} - -inline void ylabel(const std::string &str, const std::map& keywords = {}) -{ - PyObject* pystr = PyString_FromString(str.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pystr); - - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_ylabel, args, kwargs); - if(!res) throw std::runtime_error("Call to ylabel() failed."); - - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); -} - -inline void grid(bool flag) -{ - PyObject* pyflag = flag ? Py_True : Py_False; - Py_INCREF(pyflag); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pyflag); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args); - if(!res) throw std::runtime_error("Call to grid() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void show(const bool block = true) -{ - PyObject* res; - if(block) - { - res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_show, - detail::_interpreter::get().s_python_empty_tuple); - } - else - { - PyObject *kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "block", Py_False); - res = PyObject_Call( detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple, kwargs); - Py_DECREF(kwargs); - } - - - if (!res) throw std::runtime_error("Call to show() failed."); - - Py_DECREF(res); -} - -inline void close() -{ - PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_close, - detail::_interpreter::get().s_python_empty_tuple); - - if (!res) throw std::runtime_error("Call to close() failed."); - - Py_DECREF(res); -} - -inline void xkcd() { - PyObject* res; - PyObject *kwargs = PyDict_New(); - - res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd, - detail::_interpreter::get().s_python_empty_tuple, kwargs); - - Py_DECREF(kwargs); - - if (!res) - throw std::runtime_error("Call to show() failed."); - - Py_DECREF(res); -} - -inline void draw() -{ - PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_draw, - detail::_interpreter::get().s_python_empty_tuple); - - if (!res) throw std::runtime_error("Call to draw() failed."); - - Py_DECREF(res); -} - -template -inline void pause(Numeric interval) -{ - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval)); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args); - if(!res) throw std::runtime_error("Call to pause() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void save(const std::string& filename) -{ - PyObject* pyfilename = PyString_FromString(filename.c_str()); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pyfilename); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args); - if (!res) throw std::runtime_error("Call to save() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void clf() { - PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_clf, - detail::_interpreter::get().s_python_empty_tuple); - - if (!res) throw std::runtime_error("Call to clf() failed."); - - Py_DECREF(res); -} - - inline void ion() { - PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_ion, - detail::_interpreter::get().s_python_empty_tuple); - - if (!res) throw std::runtime_error("Call to ion() failed."); - - Py_DECREF(res); -} - -inline std::vector> ginput(const int numClicks = 1, const std::map& keywords = {}) -{ - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks)); - - // 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())); - } - - PyObject* res = PyObject_Call( - detail::_interpreter::get().s_python_function_ginput, args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(args); - if (!res) throw std::runtime_error("Call to ginput() failed."); - - const size_t len = PyList_Size(res); - std::vector> out; - out.reserve(len); - for (size_t i = 0; i < len; i++) { - PyObject *current = PyList_GetItem(res, i); - std::array position; - position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0)); - position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1)); - out.push_back(position); - } - Py_DECREF(res); - - return out; -} - -// Actually, is there any reason not to call this automatically for every plot? -inline void tight_layout() { - PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_tight_layout, - detail::_interpreter::get().s_python_empty_tuple); - - if (!res) throw std::runtime_error("Call to tight_layout() failed."); - - Py_DECREF(res); -} - -// Support for variadic plot() and initializer lists: - -namespace detail { - -template -using is_function = typename std::is_function>>::type; - -template -struct is_callable_impl; - -template -struct is_callable_impl -{ - typedef is_function type; -}; // a non-object is callable iff it is a function - -template -struct is_callable_impl -{ - struct Fallback { void operator()(); }; - struct Derived : T, Fallback { }; - - template struct Check; - - template - static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match - - template - static std::false_type test( Check* ); - -public: - typedef decltype(test(nullptr)) type; - typedef decltype(&Fallback::operator()) dtype; - static constexpr bool value = type::value; -}; // an object is callable iff it defines operator() - -template -struct is_callable -{ - // dispatch to is_callable_impl or is_callable_impl depending on whether T is of class type or not - typedef typename is_callable_impl::value, T>::type type; -}; - -template -struct plot_impl { }; - -template<> -struct plot_impl -{ - template - bool operator()(const IterableX& x, const IterableY& y, const std::string& format) - { - // 2-phase lookup for distance, begin, end - using std::distance; - using std::begin; - using std::end; - - auto xs = distance(begin(x), end(x)); - auto ys = distance(begin(y), end(y)); - assert(xs == ys && "x and y data must have the same number of elements!"); - - PyObject* xlist = PyList_New(xs); - PyObject* ylist = PyList_New(ys); - PyObject* pystring = PyString_FromString(format.c_str()); - - auto itx = begin(x), ity = begin(y); - for(size_t i = 0; i < xs; ++i) { - PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++)); - PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++)); - } - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xlist); - PyTuple_SetItem(plot_args, 1, ylist); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; - } -}; - -template<> -struct plot_impl -{ - template - bool operator()(const Iterable& ticks, const Callable& f, const std::string& format) - { - if(begin(ticks) == end(ticks)) return true; - - // We could use additional meta-programming to deduce the correct element type of y, - // but all values have to be convertible to double anyways - std::vector y; - for(auto x : ticks) y.push_back(f(x)); - return plot_impl()(ticks,y,format); - } -}; - -} // end namespace detail - -// recursion stop for the above -template -bool plot() { return true; } - -template -bool plot(const A& a, const B& b, const std::string& format, Args... args) -{ - return detail::plot_impl::type>()(a,b,format) && plot(args...); -} - -/* - * This group of plot() functions is needed to support initializer lists, i.e. calling - * plot( {1,2,3,4} ) - */ -inline bool plot(const std::vector& x, const std::vector& y, const std::string& format = "") { - return plot(x,y,format); -} - -inline bool plot(const std::vector& y, const std::string& format = "") { - return plot(y,format); -} - -inline bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { - return plot(x,y,keywords); -} - -/* - * This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting - */ - -class Plot -{ -public: - // default initialization with plot label, some data and format - template - Plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { - - assert(x.size() == y.size()); - - PyObject* kwargs = PyDict_New(); - if(name != "") - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* pystring = PyString_FromString(format.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - - if(res) - { - line= PyList_GetItem(res, 0); - - if(line) - set_data_fct = PyObject_GetAttrString(line,"set_data"); - else - Py_DECREF(line); - Py_DECREF(res); - } - } - - // shorter initialization with name or format only - // basically calls line, = plot([], []) - Plot(const std::string& name = "", const std::string& format = "") - : Plot(name, std::vector(), std::vector(), format) {} - - template - bool update(const std::vector& x, const std::vector& y) { - assert(x.size() == y.size()); - if(set_data_fct) - { - PyObject* xarray = get_array(x); - PyObject* yarray = get_array(y); - - PyObject* plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - - PyObject* res = PyObject_CallObject(set_data_fct, plot_args); - if (res) Py_DECREF(res); - return res; - } - return false; - } - - // clears the plot but keep it available - bool clear() { - return update(std::vector(), std::vector()); - } - - // definitely remove this line - void remove() { - if(line) - { - auto remove_fct = PyObject_GetAttrString(line,"remove"); - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(remove_fct, args); - if (res) Py_DECREF(res); - } - decref(); - } - - ~Plot() { - decref(); - } -private: - - void decref() { - if(line) - Py_DECREF(line); - if(set_data_fct) - Py_DECREF(set_data_fct); - } - - - PyObject* line = nullptr; - PyObject* set_data_fct = nullptr; -}; - -} // end namespace matplotlibcpp diff --git a/matplotlibcppConfig.cmake.in b/matplotlibcppConfig.cmake.in new file mode 100644 index 0000000..7b84c88 --- /dev/null +++ b/matplotlibcppConfig.cmake.in @@ -0,0 +1,15 @@ +# matplotlibcpp cmake module +# This module sets the following variables in your project:: +# +# matplotlibcpp_FOUND - true if matplotlibcpp found on the system +# matplotlibcpp_INCLUDE_DIRS - the directory containing matplotlibcpp header +# matplotlibcpp_LIBRARY - empty + +@PACKAGE_INIT@ + +if(NOT TARGET @PROJECT_NAME@) + include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + get_target_property(@PROJECT_NAME@_INCLUDE_DIRS matplotlibcpp INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(@PROJECT_NAME@_LIBRARY matplotlibcpp INTERFACE_LINK_LIBRARIES) +endif() +