diff --git a/.gitignore b/.gitignore index 1c4a1b0..6c30f59 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,7 @@ # vim temp files *.sw* + +# CMake +build/ +install/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d6175a2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: minimal -dist: trusty -services: - - docker -script: - - make -C contrib docker_build diff --git a/CMakeLists.txt b/CMakeLists.txt index bb2decd..5ff53c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,133 +1,64 @@ -cmake_minimum_required(VERSION 3.8 FATAL_ERROR) -project(matplotlib_cpp LANGUAGES CXX) +# CMakeLists.txt +cmake_minimum_required(VERSION 3.15) +project(matplotlibcpp LANGUAGES CXX) -include(GNUInstallDirs) -set(PACKAGE_NAME matplotlib_cpp) -set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PACKAGE_NAME}/cmake) +# set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install) # comment this if you want to install in /usr/local +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") # neede for include(...) +set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE INTERNAL "No dev warnings") # bypass dev warnings -# Library target -add_library(matplotlib_cpp INTERFACE) -target_include_directories(matplotlib_cpp - INTERFACE - $ - $ -) -target_compile_features(matplotlib_cpp INTERFACE - cxx_std_11 -) -# TODO: Use `Development.Embed` component when requiring cmake >= 3.18 -find_package(Python3 COMPONENTS Interpreter Development REQUIRED) -target_link_libraries(matplotlib_cpp INTERFACE - Python3::Python - Python3::Module -) -find_package(Python3 COMPONENTS NumPy) -if(Python3_NumPy_FOUND) - target_link_libraries(matplotlib_cpp INTERFACE - Python3::NumPy - ) -else() - target_compile_definitions(matplotlib_cpp INTERFACE WITHOUT_NUMPY) -endif() -install( - TARGETS matplotlib_cpp - EXPORT install_targets -) - - -# Examples -add_executable(minimal examples/minimal.cpp) -target_link_libraries(minimal PRIVATE matplotlib_cpp) -set_target_properties(minimal PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(basic examples/basic.cpp) -target_link_libraries(basic PRIVATE matplotlib_cpp) -set_target_properties(basic PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(modern examples/modern.cpp) -target_link_libraries(modern PRIVATE matplotlib_cpp) -set_target_properties(modern PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(animation examples/animation.cpp) -target_link_libraries(animation PRIVATE matplotlib_cpp) -set_target_properties(animation PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(nonblock examples/nonblock.cpp) -target_link_libraries(nonblock PRIVATE matplotlib_cpp) -set_target_properties(nonblock PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(xkcd examples/xkcd.cpp) -target_link_libraries(xkcd PRIVATE matplotlib_cpp) -set_target_properties(xkcd PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +# -------------------------------------------------------------------------- # +# Build C++ library # +# -------------------------------------------------------------------------- # -add_executable(bar examples/bar.cpp) -target_link_libraries(bar PRIVATE matplotlib_cpp) -set_target_properties(bar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(LIBRARY_NAME matplotlibcpp_lib) -add_executable(fill_inbetween examples/fill_inbetween.cpp) -target_link_libraries(fill_inbetween PRIVATE matplotlib_cpp) -set_target_properties(fill_inbetween PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +# find python libraries +find_package(Python3 COMPONENTS Interpreter Development NumPy REQUIRED) +find_package(PythonLibs 3.0 REQUIRED) +include_directories(${PYTHON3_INCLUDE_DIRS} ${NumPy_INCLUDE_DIRS}) -add_executable(fill examples/fill.cpp) -target_link_libraries(fill PRIVATE matplotlib_cpp) -set_target_properties(fill PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(update examples/update.cpp) -target_link_libraries(update PRIVATE matplotlib_cpp) -set_target_properties(update PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(subplot2grid examples/subplot2grid.cpp) -target_link_libraries(subplot2grid PRIVATE matplotlib_cpp) -set_target_properties(subplot2grid PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -add_executable(lines3d examples/lines3d.cpp) -target_link_libraries(lines3d PRIVATE matplotlib_cpp) -set_target_properties(lines3d PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - -if(Python3_NumPy_FOUND) - add_executable(surface examples/surface.cpp) - target_link_libraries(surface PRIVATE matplotlib_cpp) - set_target_properties(surface PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +# add library +add_library(${LIBRARY_NAME} SHARED + src/matplotlibcpp.cpp +) - add_executable(colorbar examples/colorbar.cpp) - target_link_libraries(colorbar PRIVATE matplotlib_cpp) - set_target_properties(colorbar PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - add_executable(contour examples/contour.cpp) - target_link_libraries(contour PRIVATE matplotlib_cpp) - set_target_properties(contour PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +target_include_directories(${LIBRARY_NAME} PUBLIC + $ + $ + $ + $ +) - add_executable(spy examples/spy.cpp) - target_link_libraries(spy PRIVATE matplotlib_cpp) - set_target_properties(spy PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") -endif() +target_link_libraries(${LIBRARY_NAME} + ${PYTHON_LIBRARIES} + Python3::NumPy +) +set_target_properties( + ${LIBRARY_NAME} + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO +) -# Install headers -install(FILES - "${PROJECT_SOURCE_DIR}/matplotlibcpp.h" - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +# -------------------------------------------------------------------------- # +# Install # +# -------------------------------------------------------------------------- # +install(TARGETS ${LIBRARY_NAME} + EXPORT ${LIBRARY_NAME} + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) +install(EXPORT ${LIBRARY_NAME} + FILE ${LIBRARY_NAME}.cmake + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/cmake) -# Install targets file -install(EXPORT install_targets - FILE - ${PACKAGE_NAME}Targets.cmake - NAMESPACE - ${PACKAGE_NAME}:: - DESTINATION - ${INSTALL_CONFIGDIR} -) +install(FILES cmake/${LIBRARY_NAME}-config.cmake + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/cmake) +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_PREFIX}/include) -# Install matplotlib_cppConfig.cmake -include(CMakePackageConfigHelpers) -configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PACKAGE_NAME}Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake - INSTALL_DESTINATION ${INSTALL_CONFIGDIR} -) -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake - DESTINATION ${INSTALL_CONFIGDIR} -) +install(DIRECTORY src/ + DESTINATION ${CMAKE_INSTALL_PREFIX}/src) \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index a67f2b5..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Benno Evers - -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. diff --git a/LICENSE.matplotlib b/LICENSE.matplotlib deleted file mode 100644 index 1c1a66b..0000000 --- a/LICENSE.matplotlib +++ /dev/null @@ -1,47 +0,0 @@ -This library does not contain any files from the matplotlib project, nor -does it make any changes to it. On the other hand, the code contained herein -is perfectly useless without a separate installation of matplotlib. -I don't know enough about US copyright law to decide whether this implies -that this library "uses" or is "based on" matplotlib. -In any case, matplotlib comes with the following license: - -License agreement for matplotlib 1.4.3 -1. This LICENSE AGREEMENT is between the Matplotlib Development Team (“MDT”), - and the Individual or Organization (“Licensee”) accessing and otherwise - using matplotlib software in source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, MDT hereby grants - Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, - test, perform and/or display publicly, prepare derivative works, distribute, and - otherwise use matplotlib 1.4.3 alone or in any derivative version, provided, however, - that MDT’s License Agreement and MDT’s notice of copyright, i.e., - “Copyright (c) 2012-2013 Matplotlib Development Team; All Rights Reserved” are retained - in matplotlib 1.4.3 alone or in any derivative version prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on or incorporates - matplotlib 1.4.3 or any part thereof, and wants to make the derivative work available - to others as provided herein, then Licensee hereby agrees to include in any such work a - brief summary of the changes made to matplotlib 1.4.3. - -4. MDT is making matplotlib 1.4.3 available to Licensee on an “AS IS” basis. MDT MAKES NO - REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, - MDT MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS - FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 1.4.3 WILL NOT INFRINGE ANY - THIRD PARTY RIGHTS. - -5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 1.4.3 FOR ANY - INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, - DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB 1.4.3, OR ANY DERIVATIVE THEREOF, - EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material breach of - its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any relationship of - agency, partnership, or joint venture between MDT and Licensee. This License - Agreement does not grant permission to use MDT trademarks or trade name in a - trademark sense to endorse or promote products or services of Licensee, or any - third party. - -8. By copying, installing or otherwise using matplotlib 1.4.3, Licensee agrees to be - bound by the terms and conditions of this License Agreement. diff --git a/README.md b/README.md index 0f8479f..a8e88cd 100644 --- a/README.md +++ b/README.md @@ -1,294 +1,36 @@ -matplotlib-cpp -============== +# MATPLOTLIBCPP - FORKED BY ARRI -Welcome to matplotlib-cpp, possibly the simplest C++ plotting library. -It is built to resemble the plotting API used by Matlab and matplotlib. +This is a fork of the original [matplotlib-cpp](https://github.com/lava/matplotlib-cpp). Its characterized for being: +- minimal: Just the necessary files. +- library: Install it as a library for usage in other projects. +- extended 3D rendering: Copied from [this video](https://www.youtube.com/watch?v=NOZDyFmWDtw) +## Installation -Usage ------ -Complete minimal example: -```cpp -#include "matplotlibcpp.h" -namespace plt = matplotlibcpp; -int main() { - plt::plot({1,3,2,4}); - plt::show(); -} -``` - g++ minimal.cpp -std=c++11 -I/usr/include/python2.7 -lpython2.7 - -**Result:** - -![Minimal example](./examples/minimal.png) - -A more comprehensive example: -```cpp -#include "matplotlibcpp.h" -#include +Build and install the library with `install.sh` as follows: -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 -#include "matplotlibcpp.h" +## Usage -using namespace std; -namespace plt = matplotlibcpp; +If you have successfully built the project, you are ready to start using the installed library. By default matplotlibcpp will be installed as a shared library in `/path/to/matplotlibcpp/install/`, and thereby, it can easily be linked to other projects using CMakeLists.txt as follows: -int main() -{ - // Prepare data. - int n = 5000; // number of data points - vector x(n),y(n); - for(int i=0; i -#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]); - } - - plt::xkcd(); - plt::plot(t, x); - plt::title("AN ORDINARY SIN WAVE"); - plt::save("xkcd.png"); -} +For exemplary files that showcase how to use the library can be found in [the examples folder](examples/). You can run an example by running: +```bash +cd examples +bash run_examples.sh ``` - g++ xkcd.cpp -std=c++11 -I/usr/include/python2.7 -lpython2.7 - -**Result:** - -![xkcd example](./examples/xkcd.png) - -When working with vector fields, you might be interested in quiver plots: -```cpp -#include "../matplotlibcpp.h" - -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; - 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); - } - } - - plt::quiver(x, y, u, v); - plt::show(); -} -``` - g++ quiver.cpp -std=c++11 -I/usr/include/python2.7 -lpython2.7 - -**Result:** - -![quiver example](./examples/quiver.png) - -When working with 3d functions, you might be interested in 3d plots: -```cpp -#include "../matplotlibcpp.h" - -namespace plt = matplotlibcpp; - -int main() -{ - std::vector> x, y, z; - for (double i = -5; i <= 5; i += 0.25) { - std::vector x_row, y_row, z_row; - for (double j = -5; j <= 5; j += 0.25) { - x_row.push_back(i); - y_row.push_back(j); - z_row.push_back(::std::sin(::std::hypot(i, j))); - } - x.push_back(x_row); - y.push_back(y_row); - z.push_back(z_row); - } - - plt::plot_surface(x, y, z); - plt::show(); -} -``` - -**Result:** - -![surface example](./examples/surface.png) - -Installation ------------- - -matplotlib-cpp works by wrapping the popular python plotting library matplotlib. (matplotlib.org) -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 - -If, for some reason, you're unable to get a working installation of numpy on your system, -you can define the macro `WITHOUT_NUMPY` before including the header file to erase this -dependency. - -The C++-part of the library consists of the single header file `matplotlibcpp.h` which -can be placed anywhere. - -Since a python interpreter is opened internally, it is necessary to link -against `libpython` in order to user matplotlib-cpp. Most versions should -work, although python likes to randomly break compatibility from time to time -so some caution is advised when using the bleeding edge. - - -# CMake - -The C++ code is compatible to both python2 and python3. However, the `CMakeLists.txt` -file is currently set up to use python3 by default, so if python2 is required this -has to be changed manually. (a PR that adds a cmake option for this would be highly -welcomed) - -**NOTE**: By design (of python), only a single python interpreter can be created per -process. When using this library, *no other* library that is spawning a python -interpreter internally can be used. - -To compile the code without using cmake, the compiler invocation should look like -this: - - g++ example.cpp -I/usr/include/python2.7 -lpython2.7 - -This can also be used for linking against a custom build of python - - g++ example.cpp -I/usr/local/include/fancy-python4 -L/usr/local/lib -lfancy-python4 - -# Vcpkg - -You can download and install matplotlib-cpp using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: - - git clone https://github.com/Microsoft/vcpkg.git - cd vcpkg - ./bootstrap-vcpkg.sh - ./vcpkg integrate install - vcpkg install matplotlib-cpp - -The matplotlib-cpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. - - -# C++11 - -Currently, c++11 is required to build matplotlib-cpp. The last working commit that did -not have this requirement was `717e98e752260245407c5329846f5d62605eff08`. - -Note that support for c++98 was dropped more or less accidentally, so if you have to work -with an ancient compiler and still want to enjoy the latest additional features, I'd -probably merge a PR that restores support. - - - -Why? ----- -I initially started this library during my diploma thesis. The usual approach of -writing data from the c++ algorithm to a file and afterwards parsing and plotting -it in python using matplotlib proved insufficient: Keeping the algorithm -and plotting code in sync requires a lot of effort when the C++ code frequently and substantially -changes. Additionally, the python yaml parser was not able to cope with files that -exceed a few hundred megabytes in size. - -Therefore, I was looking for a C++ plotting library that was extremely easy to use -and to add into an existing codebase, preferably header-only. When I found -none, I decided to write one myself, which is basically a C++ wrapper around -matplotlib. As you can see from the above examples, plotting data and saving it -to an image file can be done as few as two lines of code. - -The general approach of providing a simple C++ API for utilizing python code -was later generalized and extracted into a separate, more powerful -library in another project of mine, [wrappy](http://www.github.com/lava/wrappy). - - -Todo/Issues/Wishlist --------------------- -* This library is not thread safe. Protect all concurrent access with a mutex. - Sadly, this is not easy to fix since it is not caused by the library itself but - by the python interpreter, which is itself not thread-safe. - -* It would be nice to have a more object-oriented design with a Plot class which would allow - multiple independent plots per program. - -* Right now, only a small subset of matplotlibs functionality is exposed. Stuff like xlabel()/ylabel() etc. should - be easy to add. - -* If you use Anaconda on Windows, you might need to set PYTHONHOME to Anaconda home directory and QT_QPA_PLATFORM_PLUGIN_PATH to %PYTHONHOME%Library/plugins/platforms. The latter is for especially when you get the error which says 'This application failed to start because it could not find or load the Qt platform plugin "windows" -in "".' - -* MacOS: `Unable to import matplotlib.pyplot`. Cause: In mac os image rendering back end of matplotlib (what-is-a-backend to render using the API of Cocoa by default). There is Qt4Agg and GTKAgg and as a back-end is not the default. Set the back end of macosx that is differ compare with other windows or linux os. -Solution is described [here](https://stackoverflow.com/questions/21784641/installation-issue-with-matplotlib-python?noredirect=1&lq=1), additional information can be found there too(see links in answers). diff --git a/cmake/matplotlib_cppConfig.cmake.in b/cmake/matplotlib_cppConfig.cmake.in deleted file mode 100644 index 86d25d0..0000000 --- a/cmake/matplotlib_cppConfig.cmake.in +++ /dev/null @@ -1,10 +0,0 @@ -get_filename_component(matplotlib_cpp_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -if(NOT TARGET matplotlib_cpp::matplotlib_cpp) - find_package(Python3 COMPONENTS Interpreter Development REQUIRED) - find_package(Python3 COMPONENTS NumPy) - include("${matplotlib_cpp_CMAKE_DIR}/matplotlib_cppTargets.cmake") - - get_target_property(matplotlib_cpp_INCLUDE_DIRS matplotlib_cpp::matplotlib_cpp INTERFACE_INCLUDE_DIRECTORIES) - -endif() diff --git a/cmake/matplotlibcpp_lib-config.cmake b/cmake/matplotlibcpp_lib-config.cmake new file mode 100644 index 0000000..3a32769 --- /dev/null +++ b/cmake/matplotlibcpp_lib-config.cmake @@ -0,0 +1,7 @@ +include(CMakeFindDependencyMacro) + +find_dependency(Python3 COMPONENTS Interpreter Development NumPy REQUIRED) +find_dependency(PythonLibs 3.0 REQUIRED) + +get_filename_component(SELF_DIR ${CMAKE_CURRENT_LIST_DIR} PATH) +include(${SELF_DIR}/cmake/matplotlibcpp_lib.cmake) \ No newline at end of file diff --git a/contrib/Dockerfile b/contrib/Dockerfile deleted file mode 100644 index 850466f..0000000 --- a/contrib/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM debian:10 AS builder -RUN apt-get update \ - && apt-get install --yes --no-install-recommends \ - g++ \ - libpython3-dev \ - make \ - python3 \ - python3-dev \ - python3-numpy - -ADD Makefile matplotlibcpp.h numpy_flags.py /opt/ -ADD examples/*.cpp /opt/examples/ -RUN cd /opt \ - && make PYTHON_BIN=python3 \ - && ls examples/build - -FROM debian:10 -RUN apt-get update \ - && apt-get install --yes --no-install-recommends \ - libpython3-dev \ - python3-matplotlib \ - python3-numpy - -COPY --from=builder /opt/examples/build /opt/ -RUN cd /opt \ - && ls \ - && ./basic diff --git a/contrib/Makefile b/contrib/Makefile deleted file mode 100644 index f659cd9..0000000 --- a/contrib/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -all: docker_build - -docker_build: - cd .. && \ - docker build . -f contrib/Dockerfile -t matplotlibcpp && \ - cd contrib diff --git a/contrib/README.md b/contrib/README.md deleted file mode 100644 index 0af8515..0000000 --- a/contrib/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# contrib/ - -This folder contains contributions that may be useful to users of this library, but -have a too specialized audience to become part of the main tree. - -In particular, things in here will have a higher rate of bit-rot, since -contributors are not required to and may be unable to check whether their -changes break any of them. - -## Windows support -Tested on the following environment -* Windows 10 - 64bit -* Anaconda 4.3 (64 bit) -* Python 3.6.0 -* CMake 3.9.4 -* Visual Studio 2017, 2015, 2013 - -### Configuring and Building Samples -1. Edit WinBuild.cmd for your environment(Line:5-7) - if NOT DEFINED MSVC_VERSION set MSVC_VERSION=[Your Visual Studio Version(12, 14, 15)] - if NOT DEFINED CMAKE_CONFIG set CMAKE_CONFIG=Release - if NOT DEFINED PYTHONHOME set PYTHONHOME=[Your Python Path] - -2. Run WinBuild.cmd to build -```cmd -> cd contrib -> WinBuild.cmd -``` -The `WinBuild.cmd` will set up temporal ENV variables and build binaries in (matplotlib root)/examples with the Release configuration. - -3. Find exe files in examples/build/Release -Note: platforms folder is necessary to make qt works. diff --git a/contrib/WinBuild.cmd b/contrib/WinBuild.cmd deleted file mode 100644 index 9dfd627..0000000 --- a/contrib/WinBuild.cmd +++ /dev/null @@ -1,61 +0,0 @@ -@echo off -@setlocal EnableDelayedExpansion - -REM ------Set Your Environment------------------------------- -if NOT DEFINED MSVC_VERSION set MSVC_VERSION=15 -if NOT DEFINED CMAKE_CONFIG set CMAKE_CONFIG=Release -if NOT DEFINED PYTHONHOME set PYTHONHOME=C:/Users/%username%/Anaconda3 -REM --------------------------------------------------------- - -set KEY_NAME="HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7" -set VALUE_NAME=15.0 - -if "%MSVC_VERSION%"=="14" ( - if "%processor_architecture%" == "AMD64" ( - set CMAKE_GENERATOR=Visual Studio 14 2015 Win64 - ) else ( - set CMAKE_GENERATOR=Visual Studio 14 2015 - ) -) else if "%MSVC_VERSION%"=="12" ( - if "%processor_architecture%" == "AMD64" ( - set CMAKE_GENERATOR=Visual Studio 12 2013 Win64 - ) else ( - set CMAKE_GENERATOR=Visual Studio 12 2013 - ) -) else if "%MSVC_VERSION%"=="15" ( - if "%processor_architecture%" == "AMD64" ( - set CMAKE_GENERATOR=Visual Studio 15 2017 Win64 - ) else ( - set CMAKE_GENERATOR=Visual Studio 15 2017 - ) -) -if "%MSVC_VERSION%"=="15" ( - for /F "usebackq tokens=1,2,*" %%A in (`REG QUERY %KEY_NAME% /v %VALUE_NAME%`) do ( - set batch_file=%%CVC\Auxiliary\Build\vcvarsall.bat - ) -) else ( - set batch_file=!VS%MSVC_VERSION%0COMNTOOLS!..\..\VC\vcvarsall.bat -) -call "%batch_file%" %processor_architecture% - -pushd .. -pushd examples -if NOT EXIST build mkdir build -pushd build - -cmake -G"!CMAKE_GENERATOR!" ^ - -DPYTHONHOME:STRING=%PYTHONHOME%^ - -DCMAKE_BUILD_TYPE:STRING=%CMAKE_CONFIG% ^ - %~dp0 -cmake --build . --config %CMAKE_CONFIG% - -pushd %CMAKE_CONFIG% -if not EXIST platforms mkdir platforms -if EXIST %PYTHONHOME%/Library/plugins/platforms/qwindows.dll ^ -cp %PYTHONHOME%/Library/plugins/platforms/qwindows.dll ./platforms/ -popd -REM move ./%CMAKE_CONFIG% ../ -popd -popd -popd -@endlocal diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index 3da8ad6..0000000 --- a/examples/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -animation -bar -basic -fill -fill_inbetween -imshow -minimal -modern -nonblock -quiver -subplot -surface -update -xkcd diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..44ebdb8 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.14) +project(matplotlibcpp_examples) + +# set path to motplit_lib installation( comment if installed in /usr/local) +set(CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/../install/lib/cmake) + +# to avoid PCL warnings +set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE INTERNAL "No dev warnings") # bypass PCL warnings + +# find motplit_lib package +find_package(matplotlibcpp_lib REQUIRED) + +# add executable and link libraries +add_executable(animated_3d animated_3d.cpp) +target_link_libraries(animated_3d matplotlibcpp_lib) + +add_executable(minimal minimal.cpp) +target_link_libraries(minimal matplotlibcpp_lib) \ No newline at end of file diff --git a/examples/animated_3d.cpp b/examples/animated_3d.cpp new file mode 100644 index 0000000..254b24b --- /dev/null +++ b/examples/animated_3d.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include "matplotlibcpp.h" +#include +#include + +namespace plt = matplotlibcpp; + +double fx(double x, double y) +{ + return -(pow(x, 2) + pow(y, 2)); +} + +double dfx(double x, double y) +{ + return -2.0*x; +} + +double dfy(double x, double y) +{ + return -2.0*y; +} + +double plane(double x, double y, double x0, double y0) +{ + return dfx(x0, y0)*(x - x0) + dfy(x0, y0)*(y - y0) + fx(x0, y0); +} + +std::map>> Tangent(double c, double x0, double y0) +{ + double m0 = x0 - c, m1 = x0 + c; + double n0 = y0 - c, n1 = y0 + c; + int o = 60; + + double dM = (m1 - m0)/(o - 1); + double dN = (n1 - n0)/(o - 1); + + std::map>> result; + std::vector tx, ty, tz; + + double rx, ry; + + for(int i = 0; i < o; ++i){ + tx.clear(); + ty.clear(); + tz.clear(); + rx = m0 + i*dM; + for(int j = 0; j < o; ++j){ + ry = n0 + j*dM; + tx.push_back(rx); + ty.push_back(ry); + tz.push_back(plane(rx, ry, x0, y0)); + } + result["x"].push_back(tx); + result["y"].push_back(ty); + result["z"].push_back(tz); + } + return result; +} + +int main() +{ + PyObject * ax = plt::chart(111); + plt::Clear3DChart(ax); + + int n = 60; + double t0 = -4.0, t1 = 4.0; + double dT = (t1 - t0)/(n - 1); + + std::vector> x, y, z; + std::vector tx, ty, tz; + + double rx, ry; + + for(int i = 0; i < n; ++i){ + tx.clear(); + ty.clear(); + tz.clear(); + rx = t0 + i*dT; + for(int j = 0; j < n; ++j){ + ry = t0 + j*dT; + tx.push_back(rx); + ty.push_back(ry); + tz.push_back(fx(rx, ry)); + } + x.push_back(tx); + y.push_back(ty); + z.push_back(tz); + } + + double Px = -3.5, Py = -3.5; + double learning = 0.15; + + std::map>> gd; + + for(int i = 0; i < 100; ++i){ + plt::Clear3DChart(ax); + gd = Tangent(2, Px, Py); + + plt::surface3D(ax, x, y, z, "red", 0.9); + plt::surface3D(ax, gd["x"], gd["y"], gd["z"], "green", 0.9); + + Px = Px + learning*dfx(Px, Py); + Py = Py + learning*dfy(Px, Py); + + plt::pause(0.5); + } + + + plt::show(); + + return 0; +} \ No newline at end of file diff --git a/examples/animation.cpp b/examples/animation.cpp deleted file mode 100644 index d979430..0000000 --- a/examples/animation.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#include "../matplotlibcpp.h" - -namespace plt = matplotlibcpp; - -int main() -{ - int n = 1000; - std::vector x, y, z; - - for(int i=0; i -#include -#include "../matplotlibcpp.h" -namespace plt = matplotlibcpp; - -int main(int argc, char **argv) { - std::vector test_data; - for (int i = 0; i < 20; i++) { - test_data.push_back(i); - } - - plt::bar(test_data); - plt::show(); - - return (0); -} 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 deleted file mode 100644 index 2dc34c7..0000000 --- a/examples/basic.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#include -#include "../matplotlibcpp.h" - -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 -#include -#include "../matplotlibcpp.h" - -using namespace std; -namespace plt = matplotlibcpp; - -int main() -{ - // Prepare data - int ncols = 500, nrows = 300; - std::vector z(ncols * nrows); - for (int j=0; j - -namespace plt = matplotlibcpp; - -int main() -{ - std::vector> x, y, z; - for (double i = -5; i <= 5; i += 0.25) { - std::vector x_row, y_row, z_row; - for (double j = -5; j <= 5; j += 0.25) { - x_row.push_back(i); - y_row.push_back(j); - z_row.push_back(::std::sin(::std::hypot(i, j))); - } - x.push_back(x_row); - y.push_back(y_row); - z.push_back(z_row); - } - - plt::contour(x, y, z); - plt::show(); -} diff --git a/examples/fill.cpp b/examples/fill.cpp deleted file mode 100644 index 6059b47..0000000 --- a/examples/fill.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#define _USE_MATH_DEFINES -#include "../matplotlibcpp.h" -#include - -using namespace std; -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); - - 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) ); - } - - x1.insert(x1.end(), x2.rbegin(), x2.rend()); - y1.insert(y1.end(), y2.rbegin(), y2.rend()); - - plt::fill(x1, y1, {}); - } - 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 deleted file mode 100644 index 788d008..0000000 --- a/examples/fill_inbetween.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#define _USE_MATH_DEFINES -#include "../matplotlibcpp.h" -#include -#include - -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 < n; ++i) { - x.at(i) = i * i; - y.at(i) = sin(2 * M_PI * i / 360.0); - z.at(i) = 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"] = "-"; - - plt::fill_between(x, y, z, keywords); - plt::show(); -} diff --git a/examples/imshow.cpp b/examples/imshow.cpp deleted file mode 100644 index b11661e..0000000 --- a/examples/imshow.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#include -#include "../matplotlibcpp.h" - -using namespace std; -namespace plt = matplotlibcpp; - -int main() -{ - // Prepare data - int ncols = 500, nrows = 300; - std::vector z(ncols * nrows); - for (int j=0; j - -namespace plt = matplotlibcpp; - -int main() -{ - std::vector x, y, z; - double theta, r; - double z_inc = 4.0/99.0; double theta_inc = (8.0 * M_PI)/99.0; - - for (double i = 0; i < 100; i += 1) { - theta = -4.0 * M_PI + theta_inc*i; - z.push_back(-2.0 + z_inc*i); - r = z[i]*z[i] + 1; - x.push_back(r * sin(theta)); - y.push_back(r * cos(theta)); - } - - std::map keywords; - keywords.insert(std::pair("label", "parametric curve") ); - - plt::plot3(x, y, z, keywords); - plt::xlabel("x label"); - plt::ylabel("y label"); - plt::set_zlabel("z label"); // set_zlabel rather than just zlabel, in accordance with the Axes3D method - plt::legend(); - plt::show(); -} diff --git a/examples/lines3d.png b/examples/lines3d.png deleted file mode 100644 index 7a0c478..0000000 Binary files a/examples/lines3d.png and /dev/null differ diff --git a/examples/minimal.cpp b/examples/minimal.cpp index fbe1e1c..bd5c4d4 100644 --- a/examples/minimal.cpp +++ b/examples/minimal.cpp @@ -1,8 +1,8 @@ -#include "../matplotlibcpp.h" - +// example.cpp +#include "matplotlibcpp.h" namespace plt = matplotlibcpp; int main() { - plt::plot({1,3,2,4}); - plt::show(); -} + plt::plot({1, 3, 2, 4}); + plt::show(); +} \ No newline at end of file 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 deleted file mode 100644 index 871ef2b..0000000 --- a/examples/modern.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#include "../matplotlibcpp.h" - -using namespace std; -namespace plt = matplotlibcpp; - -int main() -{ - // plot(y) - the x-coordinates are implicitly set to [0,1,...,n) - //plt::plot({1,2,3,4}); - - // Prepare data for parametric plot. - int n = 5000; // number of data points - vector x(n),y(n); - for(int i=0; i -#include "../matplotlibcpp.h" - -namespace plt = matplotlibcpp; - - -using namespace matplotlibcpp; -using namespace std; - -int main() -{ - // Prepare data. - int n = 5000; - std::vector x(n), y(n), z(n), w(n,2); - for(int i=0; i x, y, u, v; - 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); - } - } - - plt::quiver(x, y, u, v); - 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/run_examples.sh b/examples/run_examples.sh new file mode 100644 index 0000000..c01cba6 --- /dev/null +++ b/examples/run_examples.sh @@ -0,0 +1,17 @@ +# Check if the build directory exists +if [ -d "build" ]; then + echo "Removing existing build directory..." + rm -rf build +fi + +# Create a new build directory and navigate into it +mkdir build && cd build + +# Run CMake and make based on the selected build type +cmake -DPython3_EXECUTABLE=$(which python) .. + +make + +# Run the executable +./minimal +./animated_3d \ No newline at end of file diff --git a/examples/spy.cpp b/examples/spy.cpp deleted file mode 100644 index 6027a48..0000000 --- a/examples/spy.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "../matplotlibcpp.h" - -#include -#include - -namespace plt = matplotlibcpp; - -int main() -{ - const int n = 20; - std::vector> matrix; - - for (int i = 0; i < n; ++i) { - std::vector row; - for (int j = 0; j < n; ++j) { - if (i == j) - row.push_back(-2); - else if (j == i - 1 || j == i + 1) - row.push_back(1); - else - row.push_back(0); - } - matrix.push_back(row); - } - - plt::spy(matrix, 5, {{"marker", "o"}}); - plt::show(); - - return 0; -} diff --git a/examples/subplot.cpp b/examples/subplot.cpp deleted file mode 100644 index bee322e..0000000 --- a/examples/subplot.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#include "../matplotlibcpp.h" - -using namespace std; -namespace plt = matplotlibcpp; - -int main() -{ - // Prepare data - int n = 500; - std::vector x(n), y(n), z(n), w(n,2); - for(int i=0; i -#include "../matplotlibcpp.h" - -using namespace std; -namespace plt = matplotlibcpp; - -int main() -{ - // Prepare data - int n = 500; - std::vector x(n), u(n), v(n), w(n); - for(int i=0; i - -namespace plt = matplotlibcpp; - -int main() -{ - std::vector> x, y, z; - for (double i = -5; i <= 5; i += 0.25) { - std::vector x_row, y_row, z_row; - for (double j = -5; j <= 5; j += 0.25) { - x_row.push_back(i); - y_row.push_back(j); - z_row.push_back(::std::sin(::std::hypot(i, j))); - } - x.push_back(x_row); - y.push_back(y_row); - z.push_back(z_row); - } - - plt::plot_surface(x, y, z); - 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/update.cpp b/examples/update.cpp deleted file mode 100644 index 64f4906..0000000 --- a/examples/update.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#include "../matplotlibcpp.h" -#include - -namespace plt = matplotlibcpp; - -void update_window(const double x, const double y, const double t, - std::vector &xt, std::vector &yt) -{ - const double target_length = 300; - const double half_win = (target_length/(2.*sqrt(1.+t*t))); - - xt[0] = x - half_win; - xt[1] = x + half_win; - yt[0] = y - half_win*t; - yt[1] = y + half_win*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 xt(2), yt(2); - - 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); - - // Prepare plotting the tangent. - plt::Plot plot("tangent"); - - plt::legend(); - - for (size_t i=0; i -#include "../matplotlibcpp.h" -#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]); - } - - plt::xkcd(); - plt::plot(t, x); - plt::title("AN ORDINARY SIN WAVE"); - plt::show(); -} - 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..81aead5 --- /dev/null +++ b/include/matplotlibcpp.h @@ -0,0 +1,3319 @@ +#pragma once + +// Python headers must be included before any system headers, since +// they define _POSIX_C_SOURCE +#include + +#include +#include +#include // requires c++11 support +#include +#include +#include +#include +#include +#include // std::stod +#include + +#ifndef WITHOUT_NUMPY +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include + +#ifdef WITH_OPENCV +#include +#endif // WITH_OPENCV + +/* + * A bunch of constants were removed in OpenCV 4 in favour of enum classes, so + * define the ones we need here. + */ +#if CV_MAJOR_VERSION > 3 +#define CV_BGR2RGB cv::COLOR_BGR2RGB +#define CV_BGRA2RGBA cv::COLOR_BGRA2RGBA +#endif +#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 *pymod; + PyObject *s_python_function_arrow; + 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_contour; + 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_imshow; + PyObject *s_python_function_scatter; + PyObject *s_python_function_boxplot; + PyObject *s_python_function_subplot; + PyObject *s_python_function_subplot2grid; + 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_axhline; + PyObject *s_python_function_axvline; + PyObject *s_python_function_axvspan; + PyObject *s_python_function_xlabel; + PyObject *s_python_function_ylabel; + PyObject *s_python_function_gca; + PyObject *s_python_function_xticks; + PyObject *s_python_function_yticks; + PyObject *s_python_function_margins; + PyObject *s_python_function_tick_params; + PyObject *s_python_function_grid; + PyObject *s_python_function_cla; + 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_barh; + PyObject *s_python_function_colorbar; + PyObject *s_python_function_subplots_adjust; + PyObject *s_python_function_rcparams; + PyObject *s_python_function_spy; + + /* For now, _interpreter is implemented as a singleton since its currently not + possible to have multiple independent embedded python interpreters without + patching the python source code or starting a separate process for each. + [1] Furthermore, many python objects expect that they are destructed in the + same thread as they were constructed. [2] So for advanced usage, a `kill()` + function is provided so that library users can manually ensure that the + interpreter is constructed and destroyed within the same thread. + + 1: + http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program + 2: https://github.com/lava/matplotlib-cpp/pull/202#issue-436220256 + */ + + static _interpreter &get() { return interkeeper(false); } + + static _interpreter &kill() { return interkeeper(true); } + + // Stores the actual singleton object referenced by `get()` and `kill()`. + static _interpreter &interkeeper(bool should_kill) { + static _interpreter ctx; + if (should_kill) + ctx.~_interpreter(); + return ctx; + } + + PyObject *safe_import(PyObject *module, std::string fname) { + PyObject *fn = PyObject_GetAttrString(module, fname.c_str()); + + if (!fn) + throw std::runtime_error( + std::string("Couldn't find required function: ") + fname); + + if (!PyFunction_Check(fn)) + throw std::runtime_error( + fname + std::string(" is unexpectedly not a PyFunction.")); + + return fn; + } + +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(); + + wchar_t const *dummy_args[] = { + L"Python", + NULL}; // const is needed because literals must not be modified + wchar_t const **argv = dummy_args; + int argc = sizeof(dummy_args) / sizeof(dummy_args[0]) - 1; + +#if PY_MAJOR_VERSION >= 3 + PySys_SetArgv(argc, const_cast(argv)); +#else + PySys_SetArgv(argc, (char **)(argv)); +#endif + +#ifndef WITHOUT_NUMPY + import_numpy(); // initialize numpy C-API +#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()); + } + + pymod = PyImport_Import(pyplotname); + Py_DECREF(pyplotname); + if (!pymod) { + throw std::runtime_error("Error loading module matplotlib.pyplot!"); + } + + PyObject *mpl_toolkitsmod; + PyObject *axis3dmod; + 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!"); + } + } + + 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_arrow = safe_import(pymod, "arrow"); + s_python_function_show = safe_import(pymod, "show"); + s_python_function_close = safe_import(pymod, "close"); + s_python_function_draw = safe_import(pymod, "draw"); + s_python_function_pause = safe_import(pymod, "pause"); + s_python_function_figure = safe_import(pymod, "figure"); + s_python_function_fignum_exists = safe_import(pymod, "fignum_exists"); + s_python_function_plot = safe_import(pymod, "plot"); + s_python_function_quiver = safe_import(pymod, "quiver"); + s_python_function_contour = safe_import(pymod, "contour"); + s_python_function_semilogx = safe_import(pymod, "semilogx"); + s_python_function_semilogy = safe_import(pymod, "semilogy"); + s_python_function_loglog = safe_import(pymod, "loglog"); + s_python_function_fill = safe_import(pymod, "fill"); + s_python_function_fill_between = safe_import(pymod, "fill_between"); + s_python_function_hist = safe_import(pymod, "hist"); + s_python_function_scatter = safe_import(pymod, "scatter"); + s_python_function_boxplot = safe_import(pymod, "boxplot"); + s_python_function_subplot = safe_import(pymod, "subplot"); + s_python_function_subplot2grid = safe_import(pymod, "subplot2grid"); + s_python_function_legend = safe_import(pymod, "legend"); + s_python_function_xlim = safe_import(pymod, "xlim"); + s_python_function_ylim = safe_import(pymod, "ylim"); + s_python_function_title = safe_import(pymod, "title"); + s_python_function_axis = safe_import(pymod, "axis"); + s_python_function_axhline = safe_import(pymod, "axhline"); + s_python_function_axvline = safe_import(pymod, "axvline"); + s_python_function_axvspan = safe_import(pymod, "axvspan"); + s_python_function_xlabel = safe_import(pymod, "xlabel"); + s_python_function_ylabel = safe_import(pymod, "ylabel"); + s_python_function_gca = safe_import(pymod, "gca"); + s_python_function_xticks = safe_import(pymod, "xticks"); + s_python_function_yticks = safe_import(pymod, "yticks"); + s_python_function_margins = safe_import(pymod, "margins"); + s_python_function_tick_params = safe_import(pymod, "tick_params"); + s_python_function_grid = safe_import(pymod, "grid"); + s_python_function_ion = safe_import(pymod, "ion"); + s_python_function_ginput = safe_import(pymod, "ginput"); + s_python_function_save = safe_import(pylabmod, "savefig"); + s_python_function_annotate = safe_import(pymod, "annotate"); + s_python_function_cla = safe_import(pymod, "cla"); + s_python_function_clf = safe_import(pymod, "clf"); + s_python_function_errorbar = safe_import(pymod, "errorbar"); + s_python_function_tight_layout = safe_import(pymod, "tight_layout"); + s_python_function_stem = safe_import(pymod, "stem"); + s_python_function_xkcd = safe_import(pymod, "xkcd"); + s_python_function_text = safe_import(pymod, "text"); + s_python_function_suptitle = safe_import(pymod, "suptitle"); + s_python_function_bar = safe_import(pymod, "bar"); + s_python_function_barh = safe_import(pymod, "barh"); + s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); + s_python_function_subplots_adjust = safe_import(pymod, "subplots_adjust"); + s_python_function_rcparams = PyObject_GetAttrString(pymod, "rcParams"); + s_python_function_spy = PyObject_GetAttrString(pymod, "spy"); +#ifndef WITHOUT_NUMPY + s_python_function_imshow = safe_import(pymod, "imshow"); +#endif + s_python_empty_tuple = PyTuple_New(0); + } + + ~_interpreter() { Py_Finalize(); } +}; + +} // end namespace detail + +/// Select the backend +/// +/// **NOTE:** This must be called before the first plot command to have +/// any effect. +/// +/// Mainly useful to select the non-interactive 'Agg' backend when running +/// matplotlibcpp in headless mode, for example on a machine with no display. +/// +/// See also: +/// https://matplotlib.org/2.0.2/api/matplotlib_configuration_api.html#matplotlib.use +inline void backend(const std::string &name) { detail::s_backend = name; } + +inline bool annotate(std::string annotation, double x, double y) { + 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; +} + +PyObject *chart(int place) { + PyObject *drawObject = + PyObject_GetAttrString(detail::_interpreter::get().pymod, "subplot"); + PyObject *args = PyTuple_New(1); + PyObject *kwargs = PyDict_New(); + PyTuple_SetItem(args, 0, PyLong_FromLong(place)); + PyDict_SetItemString(kwargs, "projection", PyUnicode_FromString("3d")); + PyObject *thePlot = PyObject_Call(drawObject, args, kwargs); + return thePlot; +} + +inline void Clear3DChart(PyObject *ax) { + PyObject *eraser = PyObject_GetAttrString(ax, "cla"); + PyObject *args = PyTuple_New(0); + PyObject_CallObject(eraser, args); +} + +namespace detail { + +#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; +}; + +// Sanity checks; comment them out or change the numpy type below if you're +// compiling on a platform where they don't apply +static_assert(sizeof(long long) == 8); +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_INT64; +}; +static_assert(sizeof(unsigned long long) == 8); +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_UINT64; +}; + +template PyObject *get_array(const std::vector &v) { + npy_intp vsize = v.size(); + NPY_TYPES type = select_npy_type::type; + if (type == NPY_NOTYPE) { + size_t memsize = v.size() * sizeof(double); + double *dp = static_cast(::malloc(memsize)); + for (size_t i = 0; i < v.size(); ++i) + dp[i] = v[i]; + PyObject *varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, dp); + PyArray_UpdateFlags(reinterpret_cast(varray), + NPY_ARRAY_OWNDATA); + return varray; + } + + PyObject *varray = + PyArray_SimpleNewFromData(1, &vsize, type, (void *)(v.data())); + return varray; +} + +template +PyObject *get_2darray(const std::vector<::std::vector> &v) { + 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 + +// sometimes, for labels and such, we need string arrays +inline PyObject *get_array(const std::vector &strings) { + PyObject *list = PyList_New(strings.size()); + for (std::size_t i = 0; i < strings.size(); ++i) { + PyList_SetItem(list, i, PyString_FromString(strings[i].c_str())); + } + return list; +} + +// not all matplotlib need 2d arrays, some prefer lists of lists +template +PyObject *get_listlist(const std::vector> &ll) { + PyObject *listlist = PyList_New(ll.size()); + for (std::size_t i = 0; i < ll.size(); ++i) { + PyList_SetItem(listlist, i, get_array(ll[i])); + } + return listlist; +} + +} // namespace detail + +/// Plot a line through the given x and y data points.. +/// +/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html +template +bool plot(const std::vector &x, const std::vector &y, + const std::map &keywords) { + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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; +} + +// TODO - it should be possible to make this work by implementing +// a non-numpy alternative for `detail::get_2darray()`. +#ifndef WITHOUT_NUMPY +template +void surface3D(PyObject *ax, const std::vector<::std::vector> &x, + const std::vector<::std::vector> &y, + const std::vector<::std::vector> &z, std::string color, + double linewidth) { + PyObject *X = detail::get_2darray(x); + PyObject *Y = detail::get_2darray(y); + PyObject *Z = detail::get_2darray(z); + + PyObject *args = PyTuple_New(3); + PyObject *kwargs = PyDict_New(); + PyTuple_SetItem(args, 0, X); + PyTuple_SetItem(args, 1, Y); + PyTuple_SetItem(args, 2, Z); + PyDict_SetItemString(kwargs, "color", PyUnicode_FromString(color.c_str())); + PyDict_SetItemString(kwargs, "linewidth", PyLong_FromLong(linewidth)); + + PyObject *thePlot = PyObject_GetAttrString(ax, "plot_surface"); + PyObject_Call(thePlot, args, kwargs); +} + +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(), + const long fig_number = 0) { + detail::_interpreter::get(); + + // 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 = detail::get_2darray(x); + PyObject *yarray = detail::get_2darray(y); + PyObject *zarray = detail::get_2darray(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + 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) { + if (it->first == "linewidth" || it->first == "alpha") { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyFloat_FromDouble(std::stod(it->second))); + } else { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + } + + PyObject *fig_args = PyTuple_New(1); + PyObject *fig = nullptr; + PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); + PyObject *fig_exists = PyObject_CallObject( + detail::_interpreter::get().s_python_function_fignum_exists, fig_args); + if (!PyObject_IsTrue(fig_exists)) { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, fig_args); + } + Py_DECREF(fig_exists); + if (!fig) + throw std::runtime_error("Call to figure() failed."); + + PyObject *gca_kwargs = PyDict_New(); + PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); + + PyObject *gca = PyObject_GetAttrString(fig, "gca"); + if (!gca) + throw std::runtime_error("No gca"); + Py_INCREF(gca); + PyObject *axis = PyObject_Call( + gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); + + if (!axis) + throw std::runtime_error("No axis"); + Py_INCREF(axis); + + Py_DECREF(gca); + Py_DECREF(gca_kwargs); + + PyObject *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 +void contour(const std::vector<::std::vector> &x, + const std::vector<::std::vector> &y, + const std::vector<::std::vector> &z, + const std::map &keywords = {}) { + detail::_interpreter::get(); + + // using numpy arrays + PyObject *xarray = detail::get_2darray(x); + PyObject *yarray = detail::get_2darray(y); + PyObject *zarray = detail::get_2darray(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + + PyObject *python_colormap_coolwarm = PyObject_GetAttrString( + detail::_interpreter::get().s_python_colormap, "coolwarm"); + + PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_contour, args, kwargs); + if (!res) + throw std::runtime_error("failed contour"); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); +} + +template +void spy(const std::vector<::std::vector> &x, + const double markersize = -1, // -1 for default matplotlib size + const std::map &keywords = {}) { + detail::_interpreter::get(); + + PyObject *xarray = detail::get_2darray(x); + + PyObject *kwargs = PyDict_New(); + if (markersize != -1) { + PyDict_SetItemString(kwargs, "markersize", PyFloat_FromDouble(markersize)); + } + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *plot_args = PyTuple_New(1); + PyTuple_SetItem(plot_args, 0, xarray); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_spy, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); +} +#endif // WITHOUT_NUMPY + +template +void axis_equal(const std::vector &x, const std::vector &y, + const std::vector &z, const long fig_number = 0) { + + // get axis + PyObject *fig_args = PyTuple_New(1); + PyObject *fig = nullptr; + PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); + PyObject *fig_exists = PyObject_CallObject( + detail::_interpreter::get().s_python_function_fignum_exists, fig_args); + if (!PyObject_IsTrue(fig_exists)) { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, fig_args); + } + if (!fig) + throw std::runtime_error("Call to figure() failed."); + + PyObject *gca_kwargs = PyDict_New(); + 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"); + + // get axis limit objects + PyObject *xlim = PyObject_GetAttrString(axis, "set_xlim"); + PyObject *ylim = PyObject_GetAttrString(axis, "set_ylim"); + PyObject *zlim = PyObject_GetAttrString(axis, "set_zlim"); + + // calculate limits + double minX = *std::min_element(x.begin(), x.end()); + double maxX = *std::max_element(x.begin(), x.end()); + double minY = *std::min_element(y.begin(), y.end()); + double maxY = *std::max_element(y.begin(), y.end()); + double minZ = *std::min_element(z.begin(), z.end()); + double maxZ = *std::max_element(z.begin(), z.end()); + double max_range = + std::max({(maxX - minX), (maxY - minY), (maxZ - minZ)}) / 2.0; + double mid_x = (x.back() + x.front()) * 0.5; + double mid_y = (y.back() + y.front()) * 0.5; + double mid_z = (z.back() + z.front()) * 0.5; + + // set limits + PyObject *xlim_args = PyTuple_New(2); + PyObject *ylim_args = PyTuple_New(2); + PyObject *zlim_args = PyTuple_New(2); + PyTuple_SetItem(xlim_args, 0, PyFloat_FromDouble(mid_x - 1.2 * max_range)); + PyTuple_SetItem(xlim_args, 1, PyFloat_FromDouble(mid_x + 1.2 * max_range)); + PyTuple_SetItem(ylim_args, 0, PyFloat_FromDouble(mid_y - 1.2 * max_range)); + PyTuple_SetItem(ylim_args, 1, PyFloat_FromDouble(mid_y + 1.2 * max_range)); + PyTuple_SetItem(zlim_args, 0, PyFloat_FromDouble(mid_z - 1.2 * max_range)); + PyTuple_SetItem(zlim_args, 1, PyFloat_FromDouble(mid_z + 1.2 * max_range)); + + PyObject_CallObject(xlim, xlim_args); + PyObject_CallObject(ylim, ylim_args); + PyObject_CallObject(zlim, zlim_args); +} + +template +void plot3(const std::vector &x, const std::vector &y, + const std::vector &z, + const std::map &keywords = + std::map(), + const long fig_number = 0) { + detail::_interpreter::get(); + + // Same as with plot_surface: We lazily load the modules here the first time + // this function is called because I'm not sure that we can assume "matplotlib + // installed" implies "mpl_toolkits installed" on all platforms, and we don't + // want to require it for people who don't need 3d plots. + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + detail::_interpreter::get(); + + PyObject *mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject *axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { + throw std::runtime_error("couldnt create string"); + } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { + throw std::runtime_error("Error loading module mpl_toolkits!"); + } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { + throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); + } + } + + assert(x.size() == y.size()); + assert(y.size() == z.size()); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *zarray = detail::get_array(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *fig_args = PyTuple_New(1); + PyObject *fig = nullptr; + PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); + PyObject *fig_exists = PyObject_CallObject( + detail::_interpreter::get().s_python_function_fignum_exists, fig_args); + if (!PyObject_IsTrue(fig_exists)) { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, fig_args); + } + if (!fig) + throw std::runtime_error("Call to figure() failed."); + + PyObject *gca_kwargs = PyDict_New(); + PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); + + PyObject *gca = PyObject_GetAttrString(fig, "gca"); + if (!gca) + throw std::runtime_error("No gca"); + Py_INCREF(gca); + PyObject *axis = PyObject_Call( + gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); + + if (!axis) + throw std::runtime_error("No axis"); + Py_INCREF(axis); + + Py_DECREF(gca); + Py_DECREF(gca_kwargs); + + PyObject *plot3 = PyObject_GetAttrString(axis, "plot"); + if (!plot3) + throw std::runtime_error("No 3D line plot"); + Py_INCREF(plot3); + PyObject *res = PyObject_Call(plot3, args, kwargs); + if (!res) + throw std::runtime_error("Failed 3D line plot"); + Py_DECREF(plot3); + + Py_DECREF(axis); + Py_DECREF(args); + Py_DECREF(kwargs); + 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()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 +bool fill(const std::vector &x, const std::vector &y, + const std::map &keywords) { + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 +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()); + + detail::_interpreter::get(); + + // using numpy arrays + PyObject *xarray = detail::get_array(x); + PyObject *y1array = detail::get_array(y1); + PyObject *y2array = detail::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 +bool arrow(Numeric x, Numeric y, Numeric end_x, Numeric end_y, + const std::string &fc = "r", const std::string ec = "k", + Numeric head_length = 0.25, Numeric head_width = 0.1625) { + PyObject *obj_x = PyFloat_FromDouble(x); + PyObject *obj_y = PyFloat_FromDouble(y); + PyObject *obj_end_x = PyFloat_FromDouble(end_x); + PyObject *obj_end_y = PyFloat_FromDouble(end_y); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "fc", PyString_FromString(fc.c_str())); + PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); + PyDict_SetItemString(kwargs, "head_width", PyFloat_FromDouble(head_width)); + PyDict_SetItemString(kwargs, "head_length", PyFloat_FromDouble(head_length)); + + PyObject *plot_args = PyTuple_New(4); + PyTuple_SetItem(plot_args, 0, obj_x); + PyTuple_SetItem(plot_args, 1, obj_y); + PyTuple_SetItem(plot_args, 2, obj_end_x); + PyTuple_SetItem(plot_args, 3, obj_end_y); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_arrow, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); + + return res; +} + +template +bool hist(const std::vector &y, long bins = 10, + std::string color = "b", double alpha = 1.0, + bool cumulative = false) { + detail::_interpreter::get(); + + PyObject *yarray = detail::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; +} + +#ifndef WITHOUT_NUMPY +namespace detail { + +inline void imshow(void *ptr, const NPY_TYPES type, const int rows, + const int columns, const int colors, + const std::map &keywords, + PyObject **out) { + assert(type == NPY_UINT8 || type == NPY_FLOAT); + assert(colors == 1 || colors == 3 || colors == 4); + + detail::_interpreter::get(); + + // construct args + npy_intp dims[3] = {rows, columns, colors}; + PyObject *args = PyTuple_New(1); + PyTuple_SetItem( + args, 0, PyArray_SimpleNewFromData(colors == 1 ? 2 : 3, dims, type, ptr)); + + // 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_imshow, args, kwargs); + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) + throw std::runtime_error("Call to imshow() failed"); + if (out) + *out = res; + else + Py_DECREF(res); +} + +} // namespace detail + +inline void imshow(const unsigned char *ptr, const int rows, const int columns, + const int colors, + const std::map &keywords = {}, + PyObject **out = nullptr) { + detail::imshow((void *)ptr, NPY_UINT8, rows, columns, colors, keywords, out); +} + +inline void imshow(const float *ptr, const int rows, const int columns, + const int colors, + const std::map &keywords = {}, + PyObject **out = nullptr) { + detail::imshow((void *)ptr, NPY_FLOAT, rows, columns, colors, keywords, out); +} + +#ifdef WITH_OPENCV +void imshow(const cv::Mat &image, + const std::map &keywords = {}) { + // Convert underlying type of matrix, if needed + cv::Mat image2; + NPY_TYPES npy_type = NPY_UINT8; + switch (image.type() & CV_MAT_DEPTH_MASK) { + case CV_8U: + image2 = image; + break; + case CV_32F: + image2 = image; + npy_type = NPY_FLOAT; + break; + default: + image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels())); + } + + // If color image, convert from BGR to RGB + switch (image2.channels()) { + case 3: + cv::cvtColor(image2, image2, CV_BGR2RGB); + break; + case 4: + cv::cvtColor(image2, image2, CV_BGRA2RGBA); + } + + detail::imshow(image2.data, npy_type, image2.rows, image2.cols, + image2.channels(), keywords); +} +#endif // WITH_OPENCV +#endif // WITHOUT_NUMPY + +template +bool scatter(const std::vector &x, const std::vector &y, + const double s = 1.0, // The marker size in points**2 + const std::map &keywords = {}) { + detail::_interpreter::get(); + + assert(x.size() == y.size()); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); + for (const auto &it : keywords) { + PyDict_SetItemString(kwargs, it.first.c_str(), + PyString_FromString(it.second.c_str())); + } + + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); + + return res; +} + +template +bool scatter_colored(const std::vector &x, + const std::vector &y, + const std::vector &colors, + const double s = 1.0, // The marker size in points**2 + const std::map &keywords = {}) { + detail::_interpreter::get(); + + assert(x.size() == y.size()); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *colors_array = detail::get_array(colors); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); + PyDict_SetItemString(kwargs, "c", colors_array); + + for (const auto &it : keywords) { + PyDict_SetItemString(kwargs, it.first.c_str(), + PyString_FromString(it.second.c_str())); + } + + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); + + return res; +} + +template +bool scatter(const std::vector &x, const std::vector &y, + const std::vector &z, + const double s = 1.0, // The marker size in points**2 + const std::map &keywords = {}, + const long fig_number = 0) { + detail::_interpreter::get(); + + // Same as with plot_surface: We lazily load the modules here the first time + // this function is called because I'm not sure that we can assume "matplotlib + // installed" implies "mpl_toolkits installed" on all platforms, and we don't + // want to require it for people who don't need 3d plots. + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + detail::_interpreter::get(); + + PyObject *mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject *axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { + throw std::runtime_error("couldnt create string"); + } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { + throw std::runtime_error("Error loading module mpl_toolkits!"); + } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { + throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); + } + } + + assert(x.size() == y.size()); + assert(y.size() == z.size()); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *zarray = detail::get_array(z); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); + PyTuple_SetItem(args, 2, zarray); + + // Build up the kw args. + PyObject *kwargs = PyDict_New(); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + PyObject *fig_args = PyTuple_New(1); + PyObject *fig = nullptr; + PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); + PyObject *fig_exists = PyObject_CallObject( + detail::_interpreter::get().s_python_function_fignum_exists, fig_args); + if (!PyObject_IsTrue(fig_exists)) { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + } else { + fig = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, fig_args); + } + Py_DECREF(fig_exists); + if (!fig) + throw std::runtime_error("Call to figure() failed."); + + PyObject *gca_kwargs = PyDict_New(); + PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); + + PyObject *gca = PyObject_GetAttrString(fig, "gca"); + if (!gca) + throw std::runtime_error("No gca"); + Py_INCREF(gca); + PyObject *axis = PyObject_Call( + gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); + + if (!axis) + throw std::runtime_error("No axis"); + Py_INCREF(axis); + + Py_DECREF(gca); + Py_DECREF(gca_kwargs); + + PyObject *plot3 = PyObject_GetAttrString(axis, "scatter"); + if (!plot3) + throw std::runtime_error("No 3D line plot"); + Py_INCREF(plot3); + PyObject *res = PyObject_Call(plot3, args, kwargs); + if (!res) + throw std::runtime_error("Failed 3D line plot"); + Py_DECREF(plot3); + + Py_DECREF(axis); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(fig); + if (res) + Py_DECREF(res); + return res; +} + +template +bool boxplot(const std::vector> &data, + const std::vector &labels = {}, + const std::map &keywords = {}) { + detail::_interpreter::get(); + + PyObject *listlist = detail::get_listlist(data); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, listlist); + + PyObject *kwargs = PyDict_New(); + + // kwargs needs the labels, if there are (the correct number of) labels + if (!labels.empty() && labels.size() == data.size()) { + PyDict_SetItemString(kwargs, "labels", detail::get_array(labels)); + } + + // take care of the remaining keywords + for (const auto &it : keywords) { + PyDict_SetItemString(kwargs, it.first.c_str(), + PyString_FromString(it.second.c_str())); + } + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_boxplot, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if (res) + Py_DECREF(res); + + return res; +} + +template +bool boxplot(const std::vector &data, + const std::map &keywords = {}) { + detail::_interpreter::get(); + + PyObject *vector = detail::get_array(data); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, vector); + + PyObject *kwargs = PyDict_New(); + for (const auto &it : keywords) { + PyDict_SetItemString(kwargs, it.first.c_str(), + PyString_FromString(it.second.c_str())); + } + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_boxplot, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if (res) + Py_DECREF(res); + + return res; +} + +template +bool bar(const std::vector &x, const std::vector &y, + std::string ec = "black", std::string ls = "-", double lw = 1.0, + const std::map &keywords = {}) { + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + + PyObject *kwargs = PyDict_New(); + + PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); + PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str())); + PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw)); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_bar, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); + + return res; +} + +template +bool bar(const std::vector &y, std::string ec = "black", + std::string ls = "-", double lw = 1.0, + const std::map &keywords = {}) { + using T = typename std::remove_reference::type::value_type; + + detail::_interpreter::get(); + + std::vector x; + for (std::size_t i = 0; i < y.size(); i++) { + x.push_back(i); + } + + return bar(x, y, ec, ls, lw, keywords); +} + +template +bool barh(const std::vector &x, const std::vector &y, + std::string ec = "black", std::string ls = "-", double lw = 1.0, + const std::map &keywords = {}) { + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + + PyObject *kwargs = PyDict_New(); + + PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); + PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str())); + PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw)); + + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_barh, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); + + return res; +} + +inline bool +subplots_adjust(const std::map &keywords = {}) { + detail::_interpreter::get(); + + 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 +bool named_hist(std::string label, const std::vector &y, + long bins = 10, std::string color = "b", double alpha = 1.0) { + detail::_interpreter::get(); + + PyObject *yarray = detail::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()); + + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 contour(const std::vector &x, const std::vector &y, + const std::vector &z, + const std::map &keywords = {}) { + assert(x.size() == y.size() && x.size() == z.size()); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *zarray = detail::get_array(z); + + PyObject *plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, zarray); + + // 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_contour, plot_args, kwargs); + + Py_DECREF(kwargs); + 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()); + + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *uarray = detail::get_array(u); + PyObject *warray = detail::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 quiver(const std::vector &x, const std::vector &y, + const std::vector &z, const std::vector &u, + const std::vector &w, const std::vector &v, + const std::map &keywords = {}) { + // set up 3d axes stuff + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + detail::_interpreter::get(); + + PyObject *mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject *axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { + throw std::runtime_error("couldnt create string"); + } + + mpl_toolkitsmod = PyImport_Import(mpl_toolkits); + Py_DECREF(mpl_toolkits); + if (!mpl_toolkitsmod) { + throw std::runtime_error("Error loading module mpl_toolkits!"); + } + + axis3dmod = PyImport_Import(axis3d); + Py_DECREF(axis3d); + if (!axis3dmod) { + throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); + } + } + + // assert sizes match up + assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size() && + x.size() == z.size() && x.size() == v.size() && u.size() == v.size()); + + // set up parameters + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *zarray = detail::get_array(z); + PyObject *uarray = detail::get_array(u); + PyObject *warray = detail::get_array(w); + PyObject *varray = detail::get_array(v); + + PyObject *plot_args = PyTuple_New(6); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, zarray); + PyTuple_SetItem(plot_args, 3, uarray); + PyTuple_SetItem(plot_args, 4, warray); + PyTuple_SetItem(plot_args, 5, varray); + + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + // get figure gca to enable 3d projection + PyObject *fig = + PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + if (!fig) + throw std::runtime_error("Call to figure() failed."); + + PyObject *gca_kwargs = PyDict_New(); + PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); + + PyObject *gca = PyObject_GetAttrString(fig, "gca"); + if (!gca) + throw std::runtime_error("No gca"); + Py_INCREF(gca); + PyObject *axis = PyObject_Call( + gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); + + if (!axis) + throw std::runtime_error("No axis"); + Py_INCREF(axis); + Py_DECREF(gca); + Py_DECREF(gca_kwargs); + + // plot our boys bravely, plot them strongly, plot them with a wink and clap + PyObject *plot3 = PyObject_GetAttrString(axis, "quiver"); + if (!plot3) + throw std::runtime_error("No 3D line plot"); + Py_INCREF(plot3); + PyObject *res = PyObject_Call(plot3, plot_args, kwargs); + if (!res) + throw std::runtime_error("Failed 3D plot"); + Py_DECREF(plot3); + Py_DECREF(axis); + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) + Py_DECREF(res); + + return res; +} + +template +bool stem(const std::vector &x, const std::vector &y, + const std::string &s = "") { + assert(x.size() == y.size()); + + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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()); + + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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()); + + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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()); + + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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()); + + detail::_interpreter::get(); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::get_array(y); + PyObject *yerrarray = detail::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 = "") { + detail::_interpreter::get(); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject *yarray = detail::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 = "") { + detail::_interpreter::get(); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 = "") { + detail::_interpreter::get(); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 = "") { + detail::_interpreter::get(); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 = "") { + detail::_interpreter::get(); + + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 < x.size(); ++i) + x.at(i) = i; + return plot(x, y, format); +} + +template +bool plot(const std::vector &y, + const std::map &keywords) { + std::vector x(y.size()); + for (size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + return plot(x, y, keywords); +} + +template +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 = "") { + 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); + if (!res) + throw std::runtime_error("Call to text() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void colorbar(PyObject *mappable = NULL, + const std::map &keywords = {}) { + if (mappable == NULL) + throw std::runtime_error("Must call colorbar with PyObject* returned from " + "an image, contour, surface, etc."); + + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, mappable); + + 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 *res = PyObject_Call( + detail::_interpreter::get().s_python_function_colorbar, args, kwargs); + if (!res) + throw std::runtime_error("Call to colorbar() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline long figure(long number = -1) { + detail::_interpreter::get(); + + 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) { + 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) { + 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() { + detail::_interpreter::get(); + + 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); +} + +inline void legend(const std::map &keywords) { + detail::_interpreter::get(); + + // 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_legend, + detail::_interpreter::get().s_python_empty_tuple, kwargs); + if (!res) + throw std::runtime_error("Call to legend() failed."); + + Py_DECREF(kwargs); + Py_DECREF(res); +} + +template inline void set_aspect(Numeric ratio) { + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(ratio)); + PyObject *kwargs = PyDict_New(); + + PyObject *ax = + PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, + detail::_interpreter::get().s_python_empty_tuple); + if (!ax) + throw std::runtime_error("Call to gca() failed."); + Py_INCREF(ax); + + PyObject *set_aspect = PyObject_GetAttrString(ax, "set_aspect"); + if (!set_aspect) + throw std::runtime_error("Attribute set_aspect not found."); + Py_INCREF(set_aspect); + + PyObject *res = PyObject_Call(set_aspect, args, kwargs); + if (!res) + throw std::runtime_error("Call to set_aspect() failed."); + Py_DECREF(set_aspect); + + Py_DECREF(ax); + Py_DECREF(args); + Py_DECREF(kwargs); +} + +inline void set_aspect_equal() { + // expect ratio == "equal". Leaving error handling to matplotlib. + detail::_interpreter::get(); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyString_FromString("equal")); + PyObject *kwargs = PyDict_New(); + + PyObject *ax = + PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, + detail::_interpreter::get().s_python_empty_tuple); + if (!ax) + throw std::runtime_error("Call to gca() failed."); + Py_INCREF(ax); + + PyObject *set_aspect = PyObject_GetAttrString(ax, "set_aspect"); + if (!set_aspect) + throw std::runtime_error("Attribute set_aspect not found."); + Py_INCREF(set_aspect); + + PyObject *res = PyObject_Call(set_aspect, args, kwargs); + if (!res) + throw std::runtime_error("Call to set_aspect() failed."); + Py_DECREF(set_aspect); + + Py_DECREF(ax); + Py_DECREF(args); + Py_DECREF(kwargs); +} + +template void ylim(Numeric left, Numeric right) { + 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); + if (!res) + throw std::runtime_error("Call to ylim() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +template void xlim(Numeric left, Numeric right) { + 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); + if (!res) + throw std::runtime_error("Call to xlim() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline std::array xlim() { + PyObject *args = PyTuple_New(0); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_xlim, args); + + if (!res) + throw std::runtime_error("Call to xlim() failed."); + + Py_DECREF(res); + + PyObject *left = PyTuple_GetItem(res, 0); + PyObject *right = PyTuple_GetItem(res, 1); + return {PyFloat_AsDouble(left), PyFloat_AsDouble(right)}; +} + +inline std::array ylim() { + PyObject *args = PyTuple_New(0); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_ylim, args); + + if (!res) + throw std::runtime_error("Call to ylim() failed."); + + Py_DECREF(res); + + PyObject *left = PyTuple_GetItem(res, 0); + PyObject *right = PyTuple_GetItem(res, 1); + return {PyFloat_AsDouble(left), PyFloat_AsDouble(right)}; +} + +template +inline void xticks(const std::vector &ticks, + const std::vector &labels = {}, + const std::map &keywords = {}) { + assert(labels.size() == 0 || ticks.size() == labels.size()); + + detail::_interpreter::get(); + + // using numpy array + PyObject *ticksarray = detail::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()); + + detail::_interpreter::get(); + + // using numpy array + PyObject *ticksarray = detail::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); +} + +template inline void margins(Numeric margin) { + // construct positional args + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin)); + + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_margins, args); + if (!res) + throw std::runtime_error("Call to margins() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +template +inline void margins(Numeric margin_x, Numeric margin_y) { + // construct positional args + PyObject *args = PyTuple_New(2); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin_x)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(margin_y)); + + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_margins, args); + if (!res) + throw std::runtime_error("Call to margins() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void tick_params(const std::map &keywords, + const std::string axis = "both") { + detail::_interpreter::get(); + + // construct positional args + PyObject *args; + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyString_FromString(axis.c_str())); + + // 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_tick_params, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) + throw std::runtime_error("Call to tick_params() failed"); + + Py_DECREF(res); +} + +inline void subplot(long nrows, long ncols, long plot_number) { + detail::_interpreter::get(); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); + 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 subplot2grid(long nrows, long ncols, long rowid = 0, long colid = 0, + long rowspan = 1, long colspan = 1) { + detail::_interpreter::get(); + + PyObject *shape = PyTuple_New(2); + PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows)); + PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols)); + + PyObject *loc = PyTuple_New(2); + PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid)); + PyTuple_SetItem(loc, 1, PyLong_FromLong(colid)); + + PyObject *args = PyTuple_New(4); + PyTuple_SetItem(args, 0, shape); + PyTuple_SetItem(args, 1, loc); + PyTuple_SetItem(args, 2, PyLong_FromLong(rowspan)); + PyTuple_SetItem(args, 3, PyLong_FromLong(colspan)); + + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_subplot2grid, args); + if (!res) + throw std::runtime_error("Call to subplot2grid() failed."); + + Py_DECREF(shape); + Py_DECREF(loc); + Py_DECREF(args); + Py_DECREF(res); +} + +inline void title(const std::string &titlestr, + const std::map &keywords = {}) { + 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.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 = {}) { + 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.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) { + 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); + if (!res) + throw std::runtime_error("Call to title() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void axhline(double y, double xmin = 0., double xmax = 1., + const std::map &keywords = + std::map()) { + detail::_interpreter::get(); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(y)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmin)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(xmax)); + + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_axhline, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if (res) + Py_DECREF(res); +} + +inline void axvline(double x, double ymin = 0., double ymax = 1., + const std::map &keywords = + std::map()) { + detail::_interpreter::get(); + + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(ymin)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymax)); + + // 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_axvline, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if (res) + Py_DECREF(res); +} + +inline void axvspan(double xmin, double xmax, double ymin = 0., + double ymax = 1., + const std::map &keywords = + std::map()) { + // construct positional args + PyObject *args = PyTuple_New(4); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(xmin)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmax)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymin)); + PyTuple_SetItem(args, 3, PyFloat_FromDouble(ymax)); + + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + if (it->first == "linewidth" || it->first == "alpha") { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyFloat_FromDouble(std::stod(it->second))); + } else { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + } + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_axvspan, args, kwargs); + Py_DECREF(args); + Py_DECREF(kwargs); + + if (res) + Py_DECREF(res); +} + +inline void xlabel(const std::string &str, + const std::map &keywords = {}) { + 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.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 = {}) { + 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.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 +set_zlabel(const std::string &str, + const std::map &keywords = {}) { + detail::_interpreter::get(); + + // Same as with plot_surface: We lazily load the modules here the first time + // this function is called because I'm not sure that we can assume "matplotlib + // installed" implies "mpl_toolkits installed" on all platforms, and we don't + // want to require it for people who don't need 3d plots. + static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; + if (!mpl_toolkitsmod) { + 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!"); + } + } + + 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 *ax = + PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, + detail::_interpreter::get().s_python_empty_tuple); + if (!ax) + throw std::runtime_error("Call to gca() failed."); + Py_INCREF(ax); + + PyObject *zlabel = PyObject_GetAttrString(ax, "set_zlabel"); + if (!zlabel) + throw std::runtime_error("Attribute set_zlabel not found."); + Py_INCREF(zlabel); + + PyObject *res = PyObject_Call(zlabel, args, kwargs); + if (!res) + throw std::runtime_error("Call to set_zlabel() failed."); + Py_DECREF(zlabel); + + Py_DECREF(ax); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); +} + +inline void grid(bool flag) { + 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); + if (!res) + throw std::runtime_error("Call to grid() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void show(const bool block = true) { + detail::_interpreter::get(); + + 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() { + detail::_interpreter::get(); + + 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() { + 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() { + detail::_interpreter::get(); + + 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) { + detail::_interpreter::get(); + + 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, const int dpi = 0) { + detail::_interpreter::get(); + + PyObject *pyfilename = PyString_FromString(filename.c_str()); + + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyfilename); + + PyObject *kwargs = PyDict_New(); + + if (dpi > 0) { + PyDict_SetItemString(kwargs, "dpi", PyLong_FromLong(dpi)); + } + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_save, args, kwargs); + if (!res) + throw std::runtime_error("Call to save() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline void rcparams(const std::map &keywords = {}) { + detail::_interpreter::get(); + PyObject *args = PyTuple_New(0); + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + if ("text.usetex" == it->first) + PyDict_SetItemString(kwargs, it->first.c_str(), + PyLong_FromLong(std::stoi(it->second.c_str()))); + else + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } + + PyObject *update = PyObject_GetAttrString( + detail::_interpreter::get().s_python_function_rcparams, "update"); + PyObject *res = PyObject_Call(update, args, kwargs); + if (!res) + throw std::runtime_error("Call to rcParams.update() failed."); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(update); + Py_DECREF(res); +} + +inline void clf() { + detail::_interpreter::get(); + + 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 cla() { + detail::_interpreter::get(); + + PyObject *res = + PyObject_CallObject(detail::_interpreter::get().s_python_function_cla, + detail::_interpreter::get().s_python_empty_tuple); + + if (!res) + throw std::runtime_error("Call to cla() failed."); + + Py_DECREF(res); +} + +inline void ion() { + detail::_interpreter::get(); + + 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 = {}) { + detail::_interpreter::get(); + + 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() { + detail::_interpreter::get(); + + 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< + std::remove_pointer>>::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) { + detail::_interpreter::get(); + + // 2-phase lookup for distance, begin, end + using std::begin; + using std::distance; + 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 = "") { + detail::_interpreter::get(); + + assert(x.size() == y.size()); + + PyObject *kwargs = PyDict_New(); + if (name != "") + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject *xarray = detail::get_array(x); + PyObject *yarray = detail::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 = detail::get_array(x); + PyObject *yarray = detail::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 \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..bde6bad --- /dev/null +++ b/install.sh @@ -0,0 +1,29 @@ +# Set default install prefix +install_prefix="$PWD/install" + +# Check if an argument is provided +if [ -n "$1" ]; then + install_prefix="$1" + echo "Installing to custom directory: $install_prefix" +fi + + +# Check if the build directory exists +if [ -d "build" ]; then + echo "Removing existing build directory..." + rm -rf build +fi + +# Create a new build directory and navigate into it +mkdir build && cd build + +# Run CMake and make based on the selected build type +cmake -DPython3_EXECUTABLE=$(which python) -DCMAKE_INSTALL_PREFIX="$install_prefix" .. +make + +# Install the executable +make install + +# # Run the executable +# ./minimal +# ./animated_3d \ No newline at end of file diff --git a/matplotlibcpp.h b/matplotlibcpp.h deleted file mode 100644 index d95d46a..0000000 --- a/matplotlibcpp.h +++ /dev/null @@ -1,2986 +0,0 @@ -#pragma once - -// Python headers must be included before any system headers, since -// they define _POSIX_C_SOURCE -#include - -#include -#include -#include -#include -#include -#include -#include -#include // requires c++11 support -#include -#include // std::stod - -#ifndef WITHOUT_NUMPY -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -# include - -# ifdef WITH_OPENCV -# include -# endif // WITH_OPENCV - -/* - * A bunch of constants were removed in OpenCV 4 in favour of enum classes, so - * define the ones we need here. - */ -# if CV_MAJOR_VERSION > 3 -# define CV_BGR2RGB cv::COLOR_BGR2RGB -# define CV_BGRA2RGBA cv::COLOR_BGRA2RGBA -# endif -#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_arrow; - 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_contour; - 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_imshow; - PyObject *s_python_function_scatter; - PyObject *s_python_function_boxplot; - PyObject *s_python_function_subplot; - PyObject *s_python_function_subplot2grid; - 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_axhline; - PyObject *s_python_function_axvline; - PyObject *s_python_function_axvspan; - PyObject *s_python_function_xlabel; - PyObject *s_python_function_ylabel; - PyObject *s_python_function_gca; - PyObject *s_python_function_xticks; - PyObject *s_python_function_yticks; - PyObject* s_python_function_margins; - PyObject *s_python_function_tick_params; - PyObject *s_python_function_grid; - PyObject* s_python_function_cla; - 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_barh; - PyObject *s_python_function_colorbar; - PyObject *s_python_function_subplots_adjust; - PyObject *s_python_function_rcparams; - PyObject *s_python_function_spy; - - /* For now, _interpreter is implemented as a singleton since its currently not possible to have - multiple independent embedded python interpreters without patching the python source code - or starting a separate process for each. [1] - Furthermore, many python objects expect that they are destructed in the same thread as they - were constructed. [2] So for advanced usage, a `kill()` function is provided so that library - users can manually ensure that the interpreter is constructed and destroyed within the - same thread. - - 1: http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program - 2: https://github.com/lava/matplotlib-cpp/pull/202#issue-436220256 - */ - - static _interpreter& get() { - return interkeeper(false); - } - - static _interpreter& kill() { - return interkeeper(true); - } - - // Stores the actual singleton object referenced by `get()` and `kill()`. - static _interpreter& interkeeper(bool should_kill) { - static _interpreter ctx; - if (should_kill) - ctx.~_interpreter(); - return ctx; - } - - PyObject* safe_import(PyObject* module, std::string fname) { - PyObject* fn = PyObject_GetAttrString(module, fname.c_str()); - - if (!fn) - throw std::runtime_error(std::string("Couldn't find required function: ") + fname); - - if (!PyFunction_Check(fn)) - throw std::runtime_error(fname + std::string(" is unexpectedly not a PyFunction.")); - - return fn; - } - -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(); - - wchar_t const *dummy_args[] = {L"Python", NULL}; // const is needed because literals must not be modified - wchar_t const **argv = dummy_args; - int argc = sizeof(dummy_args)/sizeof(dummy_args[0])-1; - -#if PY_MAJOR_VERSION >= 3 - PySys_SetArgv(argc, const_cast(argv)); -#else - PySys_SetArgv(argc, (char **)(argv)); -#endif - -#ifndef WITHOUT_NUMPY - import_numpy(); // initialize numpy C-API -#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_arrow = safe_import(pymod, "arrow"); - s_python_function_show = safe_import(pymod, "show"); - s_python_function_close = safe_import(pymod, "close"); - s_python_function_draw = safe_import(pymod, "draw"); - s_python_function_pause = safe_import(pymod, "pause"); - s_python_function_figure = safe_import(pymod, "figure"); - s_python_function_fignum_exists = safe_import(pymod, "fignum_exists"); - s_python_function_plot = safe_import(pymod, "plot"); - s_python_function_quiver = safe_import(pymod, "quiver"); - s_python_function_contour = safe_import(pymod, "contour"); - s_python_function_semilogx = safe_import(pymod, "semilogx"); - s_python_function_semilogy = safe_import(pymod, "semilogy"); - s_python_function_loglog = safe_import(pymod, "loglog"); - s_python_function_fill = safe_import(pymod, "fill"); - s_python_function_fill_between = safe_import(pymod, "fill_between"); - s_python_function_hist = safe_import(pymod,"hist"); - s_python_function_scatter = safe_import(pymod,"scatter"); - s_python_function_boxplot = safe_import(pymod,"boxplot"); - s_python_function_subplot = safe_import(pymod, "subplot"); - s_python_function_subplot2grid = safe_import(pymod, "subplot2grid"); - s_python_function_legend = safe_import(pymod, "legend"); - s_python_function_xlim = safe_import(pymod, "xlim"); - s_python_function_ylim = safe_import(pymod, "ylim"); - s_python_function_title = safe_import(pymod, "title"); - s_python_function_axis = safe_import(pymod, "axis"); - s_python_function_axhline = safe_import(pymod, "axhline"); - s_python_function_axvline = safe_import(pymod, "axvline"); - s_python_function_axvspan = safe_import(pymod, "axvspan"); - s_python_function_xlabel = safe_import(pymod, "xlabel"); - s_python_function_ylabel = safe_import(pymod, "ylabel"); - s_python_function_gca = safe_import(pymod, "gca"); - s_python_function_xticks = safe_import(pymod, "xticks"); - s_python_function_yticks = safe_import(pymod, "yticks"); - s_python_function_margins = safe_import(pymod, "margins"); - s_python_function_tick_params = safe_import(pymod, "tick_params"); - s_python_function_grid = safe_import(pymod, "grid"); - s_python_function_ion = safe_import(pymod, "ion"); - s_python_function_ginput = safe_import(pymod, "ginput"); - s_python_function_save = safe_import(pylabmod, "savefig"); - s_python_function_annotate = safe_import(pymod,"annotate"); - s_python_function_cla = safe_import(pymod, "cla"); - s_python_function_clf = safe_import(pymod, "clf"); - s_python_function_errorbar = safe_import(pymod, "errorbar"); - s_python_function_tight_layout = safe_import(pymod, "tight_layout"); - s_python_function_stem = safe_import(pymod, "stem"); - s_python_function_xkcd = safe_import(pymod, "xkcd"); - s_python_function_text = safe_import(pymod, "text"); - s_python_function_suptitle = safe_import(pymod, "suptitle"); - s_python_function_bar = safe_import(pymod,"bar"); - s_python_function_barh = safe_import(pymod, "barh"); - s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); - s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust"); - s_python_function_rcparams = PyObject_GetAttrString(pymod, "rcParams"); - s_python_function_spy = PyObject_GetAttrString(pymod, "spy"); -#ifndef WITHOUT_NUMPY - s_python_function_imshow = safe_import(pymod, "imshow"); -#endif - s_python_empty_tuple = PyTuple_New(0); - } - - ~_interpreter() { - Py_Finalize(); - } -}; - -} // end namespace detail - -/// Select the backend -/// -/// **NOTE:** This must be called before the first plot command to have -/// any effect. -/// -/// Mainly useful to select the non-interactive 'Agg' backend when running -/// matplotlibcpp in headless mode, for example on a machine with no display. -/// -/// See also: https://matplotlib.org/2.0.2/api/matplotlib_configuration_api.html#matplotlib.use -inline void backend(const std::string& name) -{ - detail::s_backend = name; -} - -inline bool annotate(std::string annotation, double x, double y) -{ - 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; -} - -namespace detail { - -#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; }; - -// Sanity checks; comment them out or change the numpy type below if you're compiling on -// a platform where they don't apply -static_assert(sizeof(long long) == 8); -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; -static_assert(sizeof(unsigned long long) == 8); -template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; - -template -PyObject* get_array(const std::vector& v) -{ - npy_intp vsize = v.size(); - NPY_TYPES type = select_npy_type::type; - if (type == NPY_NOTYPE) { - size_t memsize = v.size()*sizeof(double); - double* dp = static_cast(::malloc(memsize)); - for (size_t i=0; i(varray), NPY_ARRAY_OWNDATA); - return varray; - } - - PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data())); - return varray; -} - - -template -PyObject* get_2darray(const std::vector<::std::vector>& v) -{ - 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 - -// sometimes, for labels and such, we need string arrays -inline PyObject * get_array(const std::vector& strings) -{ - PyObject* list = PyList_New(strings.size()); - for (std::size_t i = 0; i < strings.size(); ++i) { - PyList_SetItem(list, i, PyString_FromString(strings[i].c_str())); - } - return list; -} - -// not all matplotlib need 2d arrays, some prefer lists of lists -template -PyObject* get_listlist(const std::vector>& ll) -{ - PyObject* listlist = PyList_New(ll.size()); - for (std::size_t i = 0; i < ll.size(); ++i) { - PyList_SetItem(listlist, i, get_array(ll[i])); - } - return listlist; -} - -} // namespace detail - -/// Plot a line through the given x and y data points.. -/// -/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html -template -bool plot(const std::vector &x, const std::vector &y, const std::map& keywords) -{ - assert(x.size() == y.size()); - - detail::_interpreter::get(); - - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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; -} - -// TODO - it should be possible to make this work by implementing -// a non-numpy alternative for `detail::get_2darray()`. -#ifndef WITHOUT_NUMPY -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(), - const long fig_number=0) -{ - detail::_interpreter::get(); - - // 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 = detail::get_2darray(x); - PyObject *yarray = detail::get_2darray(y); - PyObject *zarray = detail::get_2darray(z); - - // construct positional args - PyObject *args = PyTuple_New(3); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - PyTuple_SetItem(args, 2, zarray); - - // Build up the kw args. - PyObject *kwargs = PyDict_New(); - 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) { - if (it->first == "linewidth" || it->first == "alpha") { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyFloat_FromDouble(std::stod(it->second))); - } else { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); - } - } - - PyObject *fig_args = PyTuple_New(1); - PyObject* fig = nullptr; - PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); - PyObject *fig_exists = - PyObject_CallObject( - detail::_interpreter::get().s_python_function_fignum_exists, fig_args); - if (!PyObject_IsTrue(fig_exists)) { - fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple); - } else { - fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - fig_args); - } - Py_DECREF(fig_exists); - if (!fig) throw std::runtime_error("Call to figure() failed."); - - PyObject *gca_kwargs = PyDict_New(); - PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); - - PyObject *gca = PyObject_GetAttrString(fig, "gca"); - if (!gca) throw std::runtime_error("No gca"); - Py_INCREF(gca); - PyObject *axis = PyObject_Call( - gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); - - if (!axis) throw std::runtime_error("No axis"); - Py_INCREF(axis); - - Py_DECREF(gca); - Py_DECREF(gca_kwargs); - - PyObject *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 -void contour(const std::vector<::std::vector> &x, - const std::vector<::std::vector> &y, - const std::vector<::std::vector> &z, - const std::map &keywords = {}) -{ - detail::_interpreter::get(); - - // using numpy arrays - PyObject *xarray = detail::get_2darray(x); - PyObject *yarray = detail::get_2darray(y); - PyObject *zarray = detail::get_2darray(z); - - // construct positional args - PyObject *args = PyTuple_New(3); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - PyTuple_SetItem(args, 2, zarray); - - // Build up the kw args. - PyObject *kwargs = PyDict_New(); - - PyObject *python_colormap_coolwarm = PyObject_GetAttrString( - detail::_interpreter::get().s_python_colormap, "coolwarm"); - - PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm); - - for (std::map::const_iterator it = keywords.begin(); - it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); - } - - PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_contour, args, kwargs); - if (!res) - throw std::runtime_error("failed contour"); - - Py_DECREF(args); - Py_DECREF(kwargs); - if (res) Py_DECREF(res); -} - -template -void spy(const std::vector<::std::vector> &x, - const double markersize = -1, // -1 for default matplotlib size - const std::map &keywords = {}) -{ - detail::_interpreter::get(); - - PyObject *xarray = detail::get_2darray(x); - - PyObject *kwargs = PyDict_New(); - if (markersize != -1) { - PyDict_SetItemString(kwargs, "markersize", PyFloat_FromDouble(markersize)); - } - for (std::map::const_iterator it = keywords.begin(); - it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); - } - - PyObject *plot_args = PyTuple_New(1); - PyTuple_SetItem(plot_args, 0, xarray); - - PyObject *res = PyObject_Call( - detail::_interpreter::get().s_python_function_spy, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if (res) Py_DECREF(res); -} -#endif // WITHOUT_NUMPY - -template -void plot3(const std::vector &x, - const std::vector &y, - const std::vector &z, - const std::map &keywords = - std::map(), - const long fig_number=0) -{ - detail::_interpreter::get(); - - // Same as with plot_surface: We lazily load the modules here the first time - // this function is called because I'm not sure that we can assume "matplotlib - // installed" implies "mpl_toolkits installed" on all platforms, and we don't - // want to require it for people who don't need 3d plots. - static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; - if (!mpl_toolkitsmod) { - detail::_interpreter::get(); - - PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); - PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); - if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } - - mpl_toolkitsmod = PyImport_Import(mpl_toolkits); - Py_DECREF(mpl_toolkits); - if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } - - axis3dmod = PyImport_Import(axis3d); - Py_DECREF(axis3d); - if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } - } - - assert(x.size() == y.size()); - assert(y.size() == z.size()); - - PyObject *xarray = detail::get_array(x); - PyObject *yarray = detail::get_array(y); - PyObject *zarray = detail::get_array(z); - - // construct positional args - PyObject *args = PyTuple_New(3); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - PyTuple_SetItem(args, 2, zarray); - - // Build up the kw args. - PyObject *kwargs = PyDict_New(); - - for (std::map::const_iterator it = keywords.begin(); - it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); - } - - PyObject *fig_args = PyTuple_New(1); - PyObject* fig = nullptr; - PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); - PyObject *fig_exists = - PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, fig_args); - if (!PyObject_IsTrue(fig_exists)) { - fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple); - } else { - fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - fig_args); - } - if (!fig) throw std::runtime_error("Call to figure() failed."); - - PyObject *gca_kwargs = PyDict_New(); - PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); - - PyObject *gca = PyObject_GetAttrString(fig, "gca"); - if (!gca) throw std::runtime_error("No gca"); - Py_INCREF(gca); - PyObject *axis = PyObject_Call( - gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); - - if (!axis) throw std::runtime_error("No axis"); - Py_INCREF(axis); - - Py_DECREF(gca); - Py_DECREF(gca_kwargs); - - PyObject *plot3 = PyObject_GetAttrString(axis, "plot"); - if (!plot3) throw std::runtime_error("No 3D line plot"); - Py_INCREF(plot3); - PyObject *res = PyObject_Call(plot3, args, kwargs); - if (!res) throw std::runtime_error("Failed 3D line plot"); - Py_DECREF(plot3); - - Py_DECREF(axis); - Py_DECREF(args); - Py_DECREF(kwargs); - 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()); - - detail::_interpreter::get(); - - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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()); - - detail::_interpreter::get(); - - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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()); - - detail::_interpreter::get(); - - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* y1array = detail::get_array(y1); - PyObject* y2array = detail::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 -bool arrow(Numeric x, Numeric y, Numeric end_x, Numeric end_y, const std::string& fc = "r", - const std::string ec = "k", Numeric head_length = 0.25, Numeric head_width = 0.1625) { - PyObject* obj_x = PyFloat_FromDouble(x); - PyObject* obj_y = PyFloat_FromDouble(y); - PyObject* obj_end_x = PyFloat_FromDouble(end_x); - PyObject* obj_end_y = PyFloat_FromDouble(end_y); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "fc", PyString_FromString(fc.c_str())); - PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); - PyDict_SetItemString(kwargs, "head_width", PyFloat_FromDouble(head_width)); - PyDict_SetItemString(kwargs, "head_length", PyFloat_FromDouble(head_length)); - - PyObject* plot_args = PyTuple_New(4); - PyTuple_SetItem(plot_args, 0, obj_x); - PyTuple_SetItem(plot_args, 1, obj_y); - PyTuple_SetItem(plot_args, 2, obj_end_x); - PyTuple_SetItem(plot_args, 3, obj_end_y); - - PyObject* res = - PyObject_Call(detail::_interpreter::get().s_python_function_arrow, plot_args, kwargs); - - Py_DECREF(plot_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) -{ - detail::_interpreter::get(); - - PyObject* yarray = detail::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; -} - -#ifndef WITHOUT_NUMPY -namespace detail { - -inline void imshow(void *ptr, const NPY_TYPES type, const int rows, const int columns, const int colors, const std::map &keywords, PyObject** out) -{ - assert(type == NPY_UINT8 || type == NPY_FLOAT); - assert(colors == 1 || colors == 3 || colors == 4); - - detail::_interpreter::get(); - - // construct args - npy_intp dims[3] = { rows, columns, colors }; - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyArray_SimpleNewFromData(colors == 1 ? 2 : 3, dims, type, ptr)); - - // 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_imshow, args, kwargs); - Py_DECREF(args); - Py_DECREF(kwargs); - if (!res) - throw std::runtime_error("Call to imshow() failed"); - if (out) - *out = res; - else - Py_DECREF(res); -} - -} // namespace detail - -inline void imshow(const unsigned char *ptr, const int rows, const int columns, const int colors, const std::map &keywords = {}, PyObject** out = nullptr) -{ - detail::imshow((void *) ptr, NPY_UINT8, rows, columns, colors, keywords, out); -} - -inline void imshow(const float *ptr, const int rows, const int columns, const int colors, const std::map &keywords = {}, PyObject** out = nullptr) -{ - detail::imshow((void *) ptr, NPY_FLOAT, rows, columns, colors, keywords, out); -} - -#ifdef WITH_OPENCV -void imshow(const cv::Mat &image, const std::map &keywords = {}) -{ - // Convert underlying type of matrix, if needed - cv::Mat image2; - NPY_TYPES npy_type = NPY_UINT8; - switch (image.type() & CV_MAT_DEPTH_MASK) { - case CV_8U: - image2 = image; - break; - case CV_32F: - image2 = image; - npy_type = NPY_FLOAT; - break; - default: - image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels())); - } - - // If color image, convert from BGR to RGB - switch (image2.channels()) { - case 3: - cv::cvtColor(image2, image2, CV_BGR2RGB); - break; - case 4: - cv::cvtColor(image2, image2, CV_BGRA2RGBA); - } - - detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords); -} -#endif // WITH_OPENCV -#endif // WITHOUT_NUMPY - -template -bool scatter(const std::vector& x, - const std::vector& y, - const double s=1.0, // The marker size in points**2 - const std::map & keywords = {}) -{ - detail::_interpreter::get(); - - assert(x.size() == y.size()); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); - for (const auto& it : keywords) - { - PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); - } - - PyObject* plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; -} - -template - bool scatter_colored(const std::vector& x, - const std::vector& y, - const std::vector& colors, - const double s=1.0, // The marker size in points**2 - const std::map & keywords = {}) - { - detail::_interpreter::get(); - - assert(x.size() == y.size()); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - PyObject* colors_array = detail::get_array(colors); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); - PyDict_SetItemString(kwargs, "c", colors_array); - - for (const auto& it : keywords) - { - PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); - } - - PyObject* plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; - } - - -template -bool scatter(const std::vector& x, - const std::vector& y, - const std::vector& z, - const double s=1.0, // The marker size in points**2 - const std::map & keywords = {}, - const long fig_number=0) { - detail::_interpreter::get(); - - // Same as with plot_surface: We lazily load the modules here the first time - // this function is called because I'm not sure that we can assume "matplotlib - // installed" implies "mpl_toolkits installed" on all platforms, and we don't - // want to require it for people who don't need 3d plots. - static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; - if (!mpl_toolkitsmod) { - detail::_interpreter::get(); - - PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); - PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); - if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } - - mpl_toolkitsmod = PyImport_Import(mpl_toolkits); - Py_DECREF(mpl_toolkits); - if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } - - axis3dmod = PyImport_Import(axis3d); - Py_DECREF(axis3d); - if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } - } - - assert(x.size() == y.size()); - assert(y.size() == z.size()); - - PyObject *xarray = detail::get_array(x); - PyObject *yarray = detail::get_array(y); - PyObject *zarray = detail::get_array(z); - - // construct positional args - PyObject *args = PyTuple_New(3); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - PyTuple_SetItem(args, 2, zarray); - - // Build up the kw args. - PyObject *kwargs = PyDict_New(); - - for (std::map::const_iterator it = keywords.begin(); - it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); - } - PyObject *fig_args = PyTuple_New(1); - PyObject* fig = nullptr; - PyTuple_SetItem(fig_args, 0, PyLong_FromLong(fig_number)); - PyObject *fig_exists = - PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, fig_args); - if (!PyObject_IsTrue(fig_exists)) { - fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple); - } else { - fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - fig_args); - } - Py_DECREF(fig_exists); - if (!fig) throw std::runtime_error("Call to figure() failed."); - - PyObject *gca_kwargs = PyDict_New(); - PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); - - PyObject *gca = PyObject_GetAttrString(fig, "gca"); - if (!gca) throw std::runtime_error("No gca"); - Py_INCREF(gca); - PyObject *axis = PyObject_Call( - gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); - - if (!axis) throw std::runtime_error("No axis"); - Py_INCREF(axis); - - Py_DECREF(gca); - Py_DECREF(gca_kwargs); - - PyObject *plot3 = PyObject_GetAttrString(axis, "scatter"); - if (!plot3) throw std::runtime_error("No 3D line plot"); - Py_INCREF(plot3); - PyObject *res = PyObject_Call(plot3, args, kwargs); - if (!res) throw std::runtime_error("Failed 3D line plot"); - Py_DECREF(plot3); - - Py_DECREF(axis); - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(fig); - if (res) Py_DECREF(res); - return res; - -} - -template -bool boxplot(const std::vector>& data, - const std::vector& labels = {}, - const std::map & keywords = {}) -{ - detail::_interpreter::get(); - - PyObject* listlist = detail::get_listlist(data); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, listlist); - - PyObject* kwargs = PyDict_New(); - - // kwargs needs the labels, if there are (the correct number of) labels - if (!labels.empty() && labels.size() == data.size()) { - PyDict_SetItemString(kwargs, "labels", detail::get_array(labels)); - } - - // take care of the remaining keywords - for (const auto& it : keywords) - { - PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - - if(res) Py_DECREF(res); - - return res; -} - -template -bool boxplot(const std::vector& data, - const std::map & keywords = {}) -{ - detail::_interpreter::get(); - - PyObject* vector = detail::get_array(data); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, vector); - - PyObject* kwargs = PyDict_New(); - for (const auto& it : keywords) - { - PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - - if(res) Py_DECREF(res); - - return res; -} - -template -bool bar(const std::vector & x, - const std::vector & y, - std::string ec = "black", - std::string ls = "-", - double lw = 1.0, - const std::map & keywords = {}) -{ - detail::_interpreter::get(); - - PyObject * xarray = detail::get_array(x); - PyObject * yarray = detail::get_array(y); - - PyObject * kwargs = PyDict_New(); - - PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); - PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str())); - PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw)); - - for (std::map::const_iterator it = - keywords.begin(); - it != keywords.end(); - ++it) { - PyDict_SetItemString( - kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject * plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - - PyObject * res = PyObject_Call( - detail::_interpreter::get().s_python_function_bar, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if (res) Py_DECREF(res); - - return res; -} - -template -bool bar(const std::vector & y, - std::string ec = "black", - std::string ls = "-", - double lw = 1.0, - const std::map & keywords = {}) -{ - using T = typename std::remove_reference::type::value_type; - - detail::_interpreter::get(); - - std::vector x; - for (std::size_t i = 0; i < y.size(); i++) { x.push_back(i); } - - return bar(x, y, ec, ls, lw, keywords); -} - - -template -bool barh(const std::vector &x, const std::vector &y, std::string ec = "black", std::string ls = "-", double lw = 1.0, const std::map &keywords = { }) { - PyObject *xarray = detail::get_array(x); - PyObject *yarray = detail::get_array(y); - - PyObject *kwargs = PyDict_New(); - - PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); - PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str())); - PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw)); - - for (std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject *plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - - PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_barh, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if (res) Py_DECREF(res); - - return res; -} - - -inline bool subplots_adjust(const std::map& keywords = {}) -{ - detail::_interpreter::get(); - - 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) -{ - detail::_interpreter::get(); - - PyObject* yarray = detail::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()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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 contour(const std::vector& x, const std::vector& y, - const std::vector& z, - const std::map& keywords = {}) { - assert(x.size() == y.size() && x.size() == z.size()); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - PyObject* zarray = detail::get_array(z); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, zarray); - - // 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_contour, plot_args, kwargs); - - Py_DECREF(kwargs); - 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()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - PyObject* uarray = detail::get_array(u); - PyObject* warray = detail::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 quiver(const std::vector& x, const std::vector& y, const std::vector& z, const std::vector& u, const std::vector& w, const std::vector& v, const std::map& keywords = {}) -{ - //set up 3d axes stuff - static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; - if (!mpl_toolkitsmod) { - detail::_interpreter::get(); - - PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); - PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); - if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } - - mpl_toolkitsmod = PyImport_Import(mpl_toolkits); - Py_DECREF(mpl_toolkits); - if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } - - axis3dmod = PyImport_Import(axis3d); - Py_DECREF(axis3d); - if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } - } - - //assert sizes match up - assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size() && x.size() == z.size() && x.size() == v.size() && u.size() == v.size()); - - //set up parameters - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - PyObject* zarray = detail::get_array(z); - PyObject* uarray = detail::get_array(u); - PyObject* warray = detail::get_array(w); - PyObject* varray = detail::get_array(v); - - PyObject* plot_args = PyTuple_New(6); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, zarray); - PyTuple_SetItem(plot_args, 3, uarray); - PyTuple_SetItem(plot_args, 4, warray); - PyTuple_SetItem(plot_args, 5, varray); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - //get figure gca to enable 3d projection - PyObject *fig = - PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple); - if (!fig) throw std::runtime_error("Call to figure() failed."); - - PyObject *gca_kwargs = PyDict_New(); - PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); - - PyObject *gca = PyObject_GetAttrString(fig, "gca"); - if (!gca) throw std::runtime_error("No gca"); - Py_INCREF(gca); - PyObject *axis = PyObject_Call( - gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); - - if (!axis) throw std::runtime_error("No axis"); - Py_INCREF(axis); - Py_DECREF(gca); - Py_DECREF(gca_kwargs); - - //plot our boys bravely, plot them strongly, plot them with a wink and clap - PyObject *plot3 = PyObject_GetAttrString(axis, "quiver"); - if (!plot3) throw std::runtime_error("No 3D line plot"); - Py_INCREF(plot3); - PyObject* res = PyObject_Call( - plot3, plot_args, kwargs); - if (!res) throw std::runtime_error("Failed 3D plot"); - Py_DECREF(plot3); - Py_DECREF(axis); - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) - Py_DECREF(res); - - return res; -} - -template -bool stem(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - PyObject* yerrarray = detail::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 = "") -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* yarray = detail::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 = "") -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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 = "") -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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 = "") -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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 = "") -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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 plot(const std::vector& y, const std::map& keywords) -{ - 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 = "") -{ - 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); - if(!res) throw std::runtime_error("Call to text() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void colorbar(PyObject* mappable = NULL, const std::map& keywords = {}) -{ - if (mappable == NULL) - throw std::runtime_error("Must call colorbar with PyObject* returned from an image, contour, surface, etc."); - - detail::_interpreter::get(); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, mappable); - - 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* res = PyObject_Call(detail::_interpreter::get().s_python_function_colorbar, args, kwargs); - if(!res) throw std::runtime_error("Call to colorbar() failed."); - - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); -} - - -inline long figure(long number = -1) -{ - detail::_interpreter::get(); - - 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) -{ - 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) -{ - 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() -{ - detail::_interpreter::get(); - - 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); -} - -inline void legend(const std::map& keywords) -{ - detail::_interpreter::get(); - - // 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_legend, detail::_interpreter::get().s_python_empty_tuple, kwargs); - if(!res) throw std::runtime_error("Call to legend() failed."); - - Py_DECREF(kwargs); - Py_DECREF(res); -} - -template -inline void set_aspect(Numeric ratio) -{ - detail::_interpreter::get(); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(ratio)); - PyObject* kwargs = PyDict_New(); - - PyObject *ax = - PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, - detail::_interpreter::get().s_python_empty_tuple); - if (!ax) throw std::runtime_error("Call to gca() failed."); - Py_INCREF(ax); - - PyObject *set_aspect = PyObject_GetAttrString(ax, "set_aspect"); - if (!set_aspect) throw std::runtime_error("Attribute set_aspect not found."); - Py_INCREF(set_aspect); - - PyObject *res = PyObject_Call(set_aspect, args, kwargs); - if (!res) throw std::runtime_error("Call to set_aspect() failed."); - Py_DECREF(set_aspect); - - Py_DECREF(ax); - Py_DECREF(args); - Py_DECREF(kwargs); -} - -inline void set_aspect_equal() -{ - // expect ratio == "equal". Leaving error handling to matplotlib. - detail::_interpreter::get(); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString("equal")); - PyObject* kwargs = PyDict_New(); - - PyObject *ax = - PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, - detail::_interpreter::get().s_python_empty_tuple); - if (!ax) throw std::runtime_error("Call to gca() failed."); - Py_INCREF(ax); - - PyObject *set_aspect = PyObject_GetAttrString(ax, "set_aspect"); - if (!set_aspect) throw std::runtime_error("Attribute set_aspect not found."); - Py_INCREF(set_aspect); - - PyObject *res = PyObject_Call(set_aspect, args, kwargs); - if (!res) throw std::runtime_error("Call to set_aspect() failed."); - Py_DECREF(set_aspect); - - Py_DECREF(ax); - Py_DECREF(args); - Py_DECREF(kwargs); -} - -template -void ylim(Numeric left, Numeric right) -{ - 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); - if(!res) throw std::runtime_error("Call to ylim() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -template -void xlim(Numeric left, Numeric right) -{ - 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); - if(!res) throw std::runtime_error("Call to xlim() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - - -inline std::array xlim() -{ - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); - - if(!res) throw std::runtime_error("Call to xlim() failed."); - - Py_DECREF(res); - - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); - return { PyFloat_AsDouble(left), PyFloat_AsDouble(right) }; -} - - -inline std::array ylim() -{ - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); - - if(!res) throw std::runtime_error("Call to ylim() failed."); - - Py_DECREF(res); - - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); - return { PyFloat_AsDouble(left), PyFloat_AsDouble(right) }; -} - -template -inline void xticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) -{ - assert(labels.size() == 0 || ticks.size() == labels.size()); - - detail::_interpreter::get(); - - // using numpy array - PyObject* ticksarray = detail::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()); - - detail::_interpreter::get(); - - // using numpy array - PyObject* ticksarray = detail::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); -} - -template inline void margins(Numeric margin) -{ - // construct positional args - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin)); - - PyObject* res = - PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args); - if (!res) - throw std::runtime_error("Call to margins() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -template inline void margins(Numeric margin_x, Numeric margin_y) -{ - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin_x)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(margin_y)); - - PyObject* res = - PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args); - if (!res) - throw std::runtime_error("Call to margins() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - - -inline void tick_params(const std::map& keywords, const std::string axis = "both") -{ - detail::_interpreter::get(); - - // construct positional args - PyObject* args; - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(axis.c_str())); - - // 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_tick_params, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - if (!res) throw std::runtime_error("Call to tick_params() failed"); - - Py_DECREF(res); -} - -inline void subplot(long nrows, long ncols, long plot_number) -{ - detail::_interpreter::get(); - - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); - 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 subplot2grid(long nrows, long ncols, long rowid=0, long colid=0, long rowspan=1, long colspan=1) -{ - detail::_interpreter::get(); - - PyObject* shape = PyTuple_New(2); - PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows)); - PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols)); - - PyObject* loc = PyTuple_New(2); - PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid)); - PyTuple_SetItem(loc, 1, PyLong_FromLong(colid)); - - PyObject* args = PyTuple_New(4); - PyTuple_SetItem(args, 0, shape); - PyTuple_SetItem(args, 1, loc); - PyTuple_SetItem(args, 2, PyLong_FromLong(rowspan)); - PyTuple_SetItem(args, 3, PyLong_FromLong(colspan)); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot2grid, args); - if(!res) throw std::runtime_error("Call to subplot2grid() failed."); - - Py_DECREF(shape); - Py_DECREF(loc); - Py_DECREF(args); - Py_DECREF(res); -} - -inline void title(const std::string &titlestr, const std::map &keywords = {}) -{ - 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.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 = {}) -{ - 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.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) -{ - 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); - if(!res) throw std::runtime_error("Call to title() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void axhline(double y, double xmin = 0., double xmax = 1., const std::map& keywords = std::map()) -{ - detail::_interpreter::get(); - - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(y)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmin)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(xmax)); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axhline, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - - if(res) Py_DECREF(res); -} - -inline void axvline(double x, double ymin = 0., double ymax = 1., const std::map& keywords = std::map()) -{ - detail::_interpreter::get(); - - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(ymin)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymax)); - - // 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_axvline, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - - if(res) Py_DECREF(res); -} - -inline void axvspan(double xmin, double xmax, double ymin = 0., double ymax = 1., const std::map& keywords = std::map()) -{ - // construct positional args - PyObject* args = PyTuple_New(4); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(xmin)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmax)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymin)); - PyTuple_SetItem(args, 3, PyFloat_FromDouble(ymax)); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - if (it->first == "linewidth" || it->first == "alpha") { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyFloat_FromDouble(std::stod(it->second))); - } else { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); - } - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvspan, args, kwargs); - Py_DECREF(args); - Py_DECREF(kwargs); - - if(res) Py_DECREF(res); -} - -inline void xlabel(const std::string &str, const std::map &keywords = {}) -{ - 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.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 = {}) -{ - 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.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 set_zlabel(const std::string &str, const std::map& keywords = {}) -{ - detail::_interpreter::get(); - - // Same as with plot_surface: We lazily load the modules here the first time - // this function is called because I'm not sure that we can assume "matplotlib - // installed" implies "mpl_toolkits installed" on all platforms, and we don't - // want to require it for people who don't need 3d plots. - static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; - if (!mpl_toolkitsmod) { - 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!"); } - } - - 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 *ax = - PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, - detail::_interpreter::get().s_python_empty_tuple); - if (!ax) throw std::runtime_error("Call to gca() failed."); - Py_INCREF(ax); - - PyObject *zlabel = PyObject_GetAttrString(ax, "set_zlabel"); - if (!zlabel) throw std::runtime_error("Attribute set_zlabel not found."); - Py_INCREF(zlabel); - - PyObject *res = PyObject_Call(zlabel, args, kwargs); - if (!res) throw std::runtime_error("Call to set_zlabel() failed."); - Py_DECREF(zlabel); - - Py_DECREF(ax); - Py_DECREF(args); - Py_DECREF(kwargs); - if (res) Py_DECREF(res); -} - -inline void grid(bool flag) -{ - 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); - if(!res) throw std::runtime_error("Call to grid() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} - -inline void show(const bool block = true) -{ - detail::_interpreter::get(); - - 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() -{ - detail::_interpreter::get(); - - 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() { - 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() -{ - detail::_interpreter::get(); - - 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) -{ - detail::_interpreter::get(); - - 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, const int dpi=0) -{ - detail::_interpreter::get(); - - PyObject* pyfilename = PyString_FromString(filename.c_str()); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pyfilename); - - PyObject* kwargs = PyDict_New(); - - if(dpi > 0) - { - PyDict_SetItemString(kwargs, "dpi", PyLong_FromLong(dpi)); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_save, args, kwargs); - if (!res) throw std::runtime_error("Call to save() failed."); - - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); -} - -inline void rcparams(const std::map& keywords = {}) { - detail::_interpreter::get(); - PyObject* args = PyTuple_New(0); - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - if ("text.usetex" == it->first) - PyDict_SetItemString(kwargs, it->first.c_str(), PyLong_FromLong(std::stoi(it->second.c_str()))); - else PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } - - PyObject * update = PyObject_GetAttrString(detail::_interpreter::get().s_python_function_rcparams, "update"); - PyObject * res = PyObject_Call(update, args, kwargs); - if(!res) throw std::runtime_error("Call to rcParams.update() failed."); - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(update); - Py_DECREF(res); -} - -inline void clf() { - detail::_interpreter::get(); - - 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 cla() { - detail::_interpreter::get(); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_cla, - detail::_interpreter::get().s_python_empty_tuple); - - if (!res) - throw std::runtime_error("Call to cla() failed."); - - Py_DECREF(res); -} - -inline void ion() { - detail::_interpreter::get(); - - 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 = {}) -{ - detail::_interpreter::get(); - - 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() { - detail::_interpreter::get(); - - 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) - { - detail::_interpreter::get(); - - // 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 = "") { - detail::_interpreter::get(); - - assert(x.size() == y.size()); - - PyObject* kwargs = PyDict_New(); - if(name != "") - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::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 = detail::get_array(x); - PyObject* yarray = detail::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/src/matplotlibcpp.cpp b/src/matplotlibcpp.cpp new file mode 100644 index 0000000..e7914e1 --- /dev/null +++ b/src/matplotlibcpp.cpp @@ -0,0 +1 @@ +#include "matplotlibcpp.h" \ No newline at end of file