From c73a8001264aeda4d26a5e7a65113f0a88073e3e Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Fri, 2 Mar 2018 01:17:51 -0500 Subject: [PATCH 1/9] Big cleanup/refactor (works with Python 3) + move to building with cmake --- CMakeLists.txt | 62 +++ Makefile | 22 -- cmake/Config.cmake.in | 17 + cmake/GenerateAndInstallConfig.cmake | 70 ++++ contrib/CMakeLists.txt | 21 - contrib/README.md | 19 - contrib/WinBuild.cmd | 46 --- examples/animation.cpp | 4 +- examples/basic.cpp | 6 +- examples/fill_inbetween.cpp | 3 +- examples/minimal.cpp | 3 +- examples/modern.cpp | 16 +- examples/nonblock.cpp | 5 +- examples/xkcd.cpp | 3 +- .../matplotlibcpp/matplotlibcpp.h | 358 ++++++++---------- package.xml | 19 + 16 files changed, 357 insertions(+), 317 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 cmake/Config.cmake.in create mode 100644 cmake/GenerateAndInstallConfig.cmake delete mode 100644 contrib/CMakeLists.txt delete mode 100644 contrib/README.md delete mode 100644 contrib/WinBuild.cmd rename matplotlibcpp.h => include/matplotlibcpp/matplotlibcpp.h (71%) create mode 100644 package.xml diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..9504ff51 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.0) + +project(matplotlibcpp VERSION 0.1.0 LANGUAGES CXX) + +find_package(PythonLibs REQUIRED) + +add_library(${PROJECT_NAME} INTERFACE) +target_include_directories(${PROJECT_NAME} INTERFACE + $) +target_include_directories(${PROJECT_NAME} INTERFACE ${PYTHON_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} INTERFACE ${PYTHON_LIBRARIES}) + +install(TARGETS ${PROJECT_NAME} EXPORT "${PROJECT_NAME}Targets" + LIBRARY DESTINATION "lib" + ARCHIVE DESTINATION "lib" + RUNTIME DESTINATION "bin" + INCLUDES DESTINATION "include" + ) + +install(DIRECTORY "include/${PROJECT_NAME}" + DESTINATION "include" + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" + PATTERN ".svn" EXCLUDE + ) + +include(cmake/GenerateAndInstallConfig.cmake) +generate_and_install_config_file( + INCLUDE_DIRS "include" + LIBRARIES + DEPS PythonLibs + DEPS_INCLUDE_DIRS PYTHON + DEPS_LIBRARIES PYTHON + ) + +# Examples +option(MATPLOTLIBCPP_EXAMPLES "Build matplotlib examples" OFF) + +if(MATPLOTLIBCPP_EXAMPLES) + set(CMAKE_CXX_STANDARD 11) + + add_executable(animation "examples/animation.cpp") + target_link_libraries(animation matplotlibcpp) + + add_executable(basic "examples/basic.cpp") + target_link_libraries(basic matplotlibcpp) + + add_executable(fill_inbetween "examples/fill_inbetween.cpp") + target_link_libraries(fill_inbetween matplotlibcpp) + + add_executable(minimal "examples/minimal.cpp") + target_link_libraries(minimal matplotlibcpp) + target_compile_definitions(minimal PRIVATE "-DWITHOUT_NUMPY") + + add_executable(modern "examples/modern.cpp") + target_link_libraries(modern matplotlibcpp) + + add_executable(nonblock "examples/nonblock.cpp") + target_link_libraries(nonblock matplotlibcpp) + + add_executable(xkcd "examples/xkcd.cpp") + target_link_libraries(xkcd matplotlibcpp) +endif(MATPLOTLIBCPP_EXAMPLES) diff --git a/Makefile b/Makefile deleted file mode 100644 index e75d1343..00000000 --- a/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -examples: minimal basic modern animation nonblock xkcd - -minimal: examples/minimal.cpp matplotlibcpp.h - cd examples && g++ -DWITHOUT_NUMPY minimal.cpp -I/usr/include/python2.7 -lpython2.7 -o minimal -std=c++11 - -basic: examples/basic.cpp matplotlibcpp.h - cd examples && g++ basic.cpp -I/usr/include/python2.7 -lpython2.7 -o basic - -modern: examples/modern.cpp matplotlibcpp.h - cd examples && g++ modern.cpp -I/usr/include/python2.7 -lpython2.7 -o modern -std=c++11 - -animation: examples/animation.cpp matplotlibcpp.h - cd examples && g++ animation.cpp -I/usr/include/python2.7 -lpython2.7 -o animation -std=c++11 - -nonblock: examples/nonblock.cpp matplotlibcpp.h - cd examples && g++ nonblock.cpp -I/usr/include/python2.7 -lpython2.7 -o nonblock -std=c++11 - -xkcd: examples/xkcd.cpp matplotlibcpp.h - cd examples && g++ xkcd.cpp -I/usr/include/python2.7 -lpython2.7 -o xkcd -std=c++11 - -clean: - rm -f examples/{minimal,basic,modern,animation,nonblock,xkcd} diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in new file mode 100644 index 00000000..94d42cd5 --- /dev/null +++ b/cmake/Config.cmake.in @@ -0,0 +1,17 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +@PROJECT_DEPS@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + + +# Legacy variables + +set(@PROJECT_NAME@_INCLUDE_DIRS "@PROJECT_INCLUDE_DIRS@") +# message(STATUS "@PROJECT_NAME@_INCLUDE_DIRS: ${@PROJECT_NAME@_INCLUDE_DIRS}") + +set(@PROJECT_NAME@_LIBRARIES "@PROJECT_LIBRARIES@") +# message(STATUS "@PROJECT_NAME@_LIBRARIES: ${@PROJECT_NAME@_LIBRARIES}") + +set(@PROJECT_NAME@_FOUND TRUE) diff --git a/cmake/GenerateAndInstallConfig.cmake b/cmake/GenerateAndInstallConfig.cmake new file mode 100644 index 00000000..75e3aff9 --- /dev/null +++ b/cmake/GenerateAndInstallConfig.cmake @@ -0,0 +1,70 @@ +function(generate_and_install_config_file) + cmake_parse_arguments(config "" "" "INCLUDE_DIRS;LIBRARIES;DEPS;DEPS_INCLUDE_DIRS;DEPS_LIBRARIES" ${ARGN}) + + # Configuration (https://github.com/forexample/package-example) + set(config_install_dir "share/${PROJECT_NAME}/cmake") + set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") + set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") + set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") + + include(CMakePackageConfigHelpers) + write_basic_package_version_file("${version_config}" + COMPATIBILITY SameMajorVersion + ) + + foreach(dep ${config_DEPS}) + set(PROJECT_DEPS "${PROJECT_DEPS}\nfind_dependency(${dep})") + endforeach() + + foreach(dir ${config_INCLUDE_DIRS}) + if(IS_ABSOLUTE ${dir} AND EXISTS ${dir}) + set(CUR_DIR ${dir}) + else() + set(CUR_DIR "\${PACKAGE_PREFIX_DIR}/${dir}") + endif() + list(APPEND PROJECT_INCLUDE_DIRS ${CUR_DIR}) + set(CUR_DIR) + endforeach() + foreach(dep ${config_DEPS_INCLUDE_DIRS}) + list(APPEND PROJECT_INCLUDE_DIRS "\${${dep}_INCLUDE_DIRS}") + endforeach() + list(LENGTH PROJECT_INCLUDE_DIRS PROJECT_INCLUDE_DIRS_LENGTH) + if(${PROJECT_INCLUDE_DIRS_LENGTH}) + list(REMOVE_DUPLICATES PROJECT_INCLUDE_DIRS) + endif() + + foreach(lib ${config_LIBRARIES}) + if(IS_ABSOLUTE ${lib} AND EXISTS ${lib}) + set(CUR_LIB ${lib}) + else() + set(CUR_LIB "\${PACKAGE_PREFIX_DIR}/lib/lib${lib}.so") + endif() + list(APPEND PROJECT_LIBRARIES ${CUR_LIB}) + set(CUR_LIB) + endforeach() + foreach(dep ${config_DEPS_LIBRARIES}) + list(APPEND PROJECT_LIBRARIES "\${${dep}_LIBRARIES}") + endforeach() + list(LENGTH PROJECT_LIBRARIES PROJECT_LIBRARIES_LENGTH) + if(${PROJECT_LIBRARIES_LENGTH}) + list(REMOVE_DUPLICATES PROJECT_LIBRARIES) + endif() + #configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY) + + configure_package_config_file( + "cmake/Config.cmake.in" + "${project_config}" + INSTALL_DESTINATION "${config_install_dir}" + NO_CHECK_REQUIRED_COMPONENTS_MACRO + NO_SET_AND_CHECK_MACRO + ) + + install(FILES "${project_config}" "${version_config}" + DESTINATION "${config_install_dir}" + ) + + install(EXPORT "${PROJECT_NAME}Targets" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION "${config_install_dir}" + ) +endfunction() diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt deleted file mode 100644 index ba14b86d..00000000 --- a/contrib/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.1) -project (MatplotlibCPP_Test) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -include_directories(${PYTHONHOME}/include) -link_directories(${PYTHONHOME}/libs) - -add_definitions(-DMATPLOTLIBCPP_PYTHON_HEADER=Python.h) - -# message(STATUS "*** dump start cmake variables ***") -# get_cmake_property(_variableNames VARIABLES) -# foreach(_variableName ${_variableNames}) - # message(STATUS "${_variableName}=${${_variableName}}") -# endforeach() -# message(STATUS "*** dump end ***") - -add_executable(minimal ${CMAKE_CURRENT_SOURCE_DIR}/../examples/minimal.cpp) -add_executable(basic ${CMAKE_CURRENT_SOURCE_DIR}/../examples/basic.cpp) -add_executable(modern ${CMAKE_CURRENT_SOURCE_DIR}/../examples/modern.cpp) diff --git a/contrib/README.md b/contrib/README.md deleted file mode 100644 index efc0a500..00000000 --- a/contrib/README.md +++ /dev/null @@ -1,19 +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 - -### Configuring and Building Samples - -```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. diff --git a/contrib/WinBuild.cmd b/contrib/WinBuild.cmd deleted file mode 100644 index 4e3b450b..00000000 --- a/contrib/WinBuild.cmd +++ /dev/null @@ -1,46 +0,0 @@ -@echo off -@setlocal EnableDelayedExpansion - -if NOT DEFINED MSVC_VERSION set MSVC_VERSION=14 -if NOT DEFINED CMAKE_CONFIG set CMAKE_CONFIG=Release -if NOT DEFINED PYTHONHOME set PYTHONHOME=C:/Users/%username%/Anaconda3 - -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 - ) -) - -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/animation.cpp b/examples/animation.cpp index d9794300..e195bb1f 100644 --- a/examples/animation.cpp +++ b/examples/animation.cpp @@ -1,6 +1,6 @@ #define _USE_MATH_DEFINES #include -#include "../matplotlibcpp.h" +#include "matplotlibcpp/matplotlibcpp.h" namespace plt = matplotlibcpp; @@ -9,6 +9,8 @@ int main() int n = 1000; std::vector x, y, z; + plt::init(); // Required for Python 3 (doesn't hurt for Python 2) + for(int i=0; i #include -#include "../matplotlibcpp.h" +#include "matplotlibcpp/matplotlibcpp.h" namespace plt = matplotlibcpp; -int main() +int main() { // Prepare data. int n = 5000; @@ -16,6 +16,8 @@ int main() z.at(i) = log(i); } + plt::init(); // Required for Python 3 (doesn't hurt for Python 2) + // Plot line from given x and y data. Color is selected automatically. plt::plot(x, y); // Plot a red dashed line from given x and y data. diff --git a/examples/fill_inbetween.cpp b/examples/fill_inbetween.cpp index 788d0086..87f1cbde 100644 --- a/examples/fill_inbetween.cpp +++ b/examples/fill_inbetween.cpp @@ -1,5 +1,5 @@ #define _USE_MATH_DEFINES -#include "../matplotlibcpp.h" +#include "matplotlibcpp/matplotlibcpp.h" #include #include @@ -23,6 +23,7 @@ int main() { keywords["color"] = "grey"; keywords["hatch"] = "-"; + plt::init(); // Required for Python 3 (doesn't hurt for Python 2) plt::fill_between(x, y, z, keywords); plt::show(); } diff --git a/examples/minimal.cpp b/examples/minimal.cpp index fbe1e1cd..57a40f42 100644 --- a/examples/minimal.cpp +++ b/examples/minimal.cpp @@ -1,8 +1,9 @@ -#include "../matplotlibcpp.h" +#include "matplotlibcpp/matplotlibcpp.h" namespace plt = matplotlibcpp; int main() { + plt::init(); // Required for Python 3 (doesn't hurt for Python 2) plt::plot({1,3,2,4}); plt::show(); } diff --git a/examples/modern.cpp b/examples/modern.cpp index a8aa0c75..78d8efba 100644 --- a/examples/modern.cpp +++ b/examples/modern.cpp @@ -1,27 +1,29 @@ #define _USE_MATH_DEFINES #include -#include "../matplotlibcpp.h" +#include "matplotlibcpp/matplotlibcpp.h" using namespace std; namespace plt = matplotlibcpp; -int main() +int main() { // plot(y) - the x-coordinates are implicitly set to [0,1,...,n) - //plt::plot({1,2,3,4}); - + //plt::plot({1,2,3,4}); + // Prepare data for parametric plot. int n = 5000; // number of data points - vector x(n),y(n); + vector x(n),y(n); for(int i=0; i -#include "../matplotlibcpp.h" +#include "matplotlibcpp/matplotlibcpp.h" namespace plt = matplotlibcpp; @@ -19,6 +19,8 @@ int main() z.at(i) = log(i); } + plt::init(); // Required for Python 3 (doesn't hurt for Python 2) + // Plot line from given x and y data. Color is selected automatically. plt::subplot(2,2,1); plt::plot(x, y); @@ -40,6 +42,7 @@ int main() plt::legend(); plt::show(false); + plt::pause(0.1); cout << "matplotlibcpp::show() is working in an non-blocking mode" << endl; getchar(); diff --git a/examples/xkcd.cpp b/examples/xkcd.cpp index 9bf64548..f32791ce 100644 --- a/examples/xkcd.cpp +++ b/examples/xkcd.cpp @@ -1,4 +1,4 @@ -#include "../matplotlibcpp.h" +#include "matplotlibcpp/matplotlibcpp.h" #include #include @@ -13,6 +13,7 @@ int main() { x[i] = sin(2.0 * M_PI * 1.0 * t[i]); } + plt::init(); // Required for Python 3 (doesn't hurt for Python 2) plt::xkcd(); plt::plot(t, x); plt::title("AN ORDINARY SIN WAVE"); diff --git a/matplotlibcpp.h b/include/matplotlibcpp/matplotlibcpp.h similarity index 71% rename from matplotlibcpp.h rename to include/matplotlibcpp/matplotlibcpp.h index 95d37725..68704546 100644 --- a/matplotlibcpp.h +++ b/include/matplotlibcpp/matplotlibcpp.h @@ -7,20 +7,21 @@ #include #include #include // requires c++11 support +#include #if __cplusplus > 199711L || _MSC_VER > 1800 -# include +#include #endif #include #ifndef WITHOUT_NUMPY -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -# include +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include #endif // WITHOUT_NUMPY #if PY_MAJOR_VERSION >= 3 -# define PyString_FromString PyUnicode_FromString +#define PyString_FromString PyUnicode_FromString #endif @@ -30,35 +31,8 @@ namespace detail { static std::string s_backend; struct _interpreter { - PyObject *s_python_function_show; - PyObject *s_python_function_close; - PyObject *s_python_function_draw; - PyObject *s_python_function_pause; - PyObject *s_python_function_save; - PyObject *s_python_function_figure; - PyObject *s_python_function_plot; - PyObject *s_python_function_semilogx; - PyObject *s_python_function_semilogy; - PyObject *s_python_function_loglog; - PyObject *s_python_function_fill_between; - PyObject *s_python_function_hist; - PyObject *s_python_function_subplot; - PyObject *s_python_function_legend; - PyObject *s_python_function_xlim; - PyObject *s_python_function_ion; - PyObject *s_python_function_ylim; - PyObject *s_python_function_title; - PyObject *s_python_function_axis; - PyObject *s_python_function_xlabel; - PyObject *s_python_function_ylabel; - PyObject *s_python_function_grid; - PyObject *s_python_function_clf; - PyObject *s_python_function_errorbar; - PyObject *s_python_function_annotate; - PyObject *s_python_function_tight_layout; + std::map python_functions; PyObject *s_python_empty_tuple; - PyObject *s_python_function_stem; - PyObject *s_python_function_xkcd; /* 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 @@ -74,20 +48,20 @@ struct _interpreter { private: #ifndef WITHOUT_NUMPY -# if PY_MAJOR_VERSION >= 3 +#if PY_MAJOR_VERSION >= 3 void *import_numpy() { import_array(); // initialize C-API return NULL; } -# else +#else void import_numpy() { import_array(); // initialize C-API } -# endif +#endif #endif _interpreter() { @@ -114,7 +88,7 @@ struct _interpreter { PyObject* matplotlib = PyImport_Import(matplotlibname); Py_DECREF(matplotlibname); - if (!matplotlib) { throw std::runtime_error("Error loading module matplotlib!"); } + if(!matplotlib) { 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 @@ -124,100 +98,30 @@ struct _interpreter { PyObject* pymod = PyImport_Import(pyplotname); Py_DECREF(pyplotname); - if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); } + if(!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); } PyObject* pylabmod = PyImport_Import(pylabname); Py_DECREF(pylabname); - if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); } - - s_python_function_show = PyObject_GetAttrString(pymod, "show"); - s_python_function_close = PyObject_GetAttrString(pymod, "close"); - s_python_function_draw = PyObject_GetAttrString(pymod, "draw"); - s_python_function_pause = PyObject_GetAttrString(pymod, "pause"); - s_python_function_figure = PyObject_GetAttrString(pymod, "figure"); - s_python_function_plot = PyObject_GetAttrString(pymod, "plot"); - s_python_function_semilogx = PyObject_GetAttrString(pymod, "semilogx"); - s_python_function_semilogy = PyObject_GetAttrString(pymod, "semilogy"); - s_python_function_loglog = PyObject_GetAttrString(pymod, "loglog"); - s_python_function_fill_between = PyObject_GetAttrString(pymod, "fill_between"); - s_python_function_hist = PyObject_GetAttrString(pymod,"hist"); - s_python_function_subplot = PyObject_GetAttrString(pymod, "subplot"); - s_python_function_legend = PyObject_GetAttrString(pymod, "legend"); - s_python_function_ylim = PyObject_GetAttrString(pymod, "ylim"); - s_python_function_title = PyObject_GetAttrString(pymod, "title"); - s_python_function_axis = PyObject_GetAttrString(pymod, "axis"); - s_python_function_xlabel = PyObject_GetAttrString(pymod, "xlabel"); - s_python_function_ylabel = PyObject_GetAttrString(pymod, "ylabel"); - s_python_function_grid = PyObject_GetAttrString(pymod, "grid"); - s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim"); - s_python_function_ion = PyObject_GetAttrString(pymod, "ion"); - s_python_function_save = PyObject_GetAttrString(pylabmod, "savefig"); - s_python_function_annotate = PyObject_GetAttrString(pymod,"annotate"); - s_python_function_clf = PyObject_GetAttrString(pymod, "clf"); - s_python_function_errorbar = PyObject_GetAttrString(pymod, "errorbar"); - s_python_function_tight_layout = PyObject_GetAttrString(pymod, "tight_layout"); - s_python_function_stem = PyObject_GetAttrString(pymod, "stem"); - s_python_function_xkcd = PyObject_GetAttrString(pymod, "xkcd"); - - if( !s_python_function_show - || !s_python_function_close - || !s_python_function_draw - || !s_python_function_pause - || !s_python_function_figure - || !s_python_function_plot - || !s_python_function_semilogx - || !s_python_function_semilogy - || !s_python_function_loglog - || !s_python_function_fill_between - || !s_python_function_subplot - || !s_python_function_legend - || !s_python_function_ylim - || !s_python_function_title - || !s_python_function_axis - || !s_python_function_xlabel - || !s_python_function_ylabel - || !s_python_function_grid - || !s_python_function_xlim - || !s_python_function_ion - || !s_python_function_save - || !s_python_function_clf - || !s_python_function_annotate - || !s_python_function_errorbar - || !s_python_function_errorbar - || !s_python_function_tight_layout - || !s_python_function_stem - || !s_python_function_xkcd - ) { throw std::runtime_error("Couldn't find required function!"); } - - if ( !PyFunction_Check(s_python_function_show) - || !PyFunction_Check(s_python_function_close) - || !PyFunction_Check(s_python_function_draw) - || !PyFunction_Check(s_python_function_pause) - || !PyFunction_Check(s_python_function_figure) - || !PyFunction_Check(s_python_function_plot) - || !PyFunction_Check(s_python_function_semilogx) - || !PyFunction_Check(s_python_function_semilogy) - || !PyFunction_Check(s_python_function_loglog) - || !PyFunction_Check(s_python_function_fill_between) - || !PyFunction_Check(s_python_function_subplot) - || !PyFunction_Check(s_python_function_legend) - || !PyFunction_Check(s_python_function_annotate) - || !PyFunction_Check(s_python_function_ylim) - || !PyFunction_Check(s_python_function_title) - || !PyFunction_Check(s_python_function_axis) - || !PyFunction_Check(s_python_function_xlabel) - || !PyFunction_Check(s_python_function_ylabel) - || !PyFunction_Check(s_python_function_grid) - || !PyFunction_Check(s_python_function_xlim) - || !PyFunction_Check(s_python_function_ion) - || !PyFunction_Check(s_python_function_save) - || !PyFunction_Check(s_python_function_clf) - || !PyFunction_Check(s_python_function_tight_layout) - || !PyFunction_Check(s_python_function_errorbar) - || !PyFunction_Check(s_python_function_stem) - || !PyFunction_Check(s_python_function_xkcd) - ) { throw std::runtime_error("Python object is unexpectedly not a PyFunction."); } + if(!pylabmod) { throw std::runtime_error("Error loading module pylab!"); } + + std::vector const function_names = {"show", "close", + "draw", "pause", "figure", "plot", "semilogx", "semilogy", "loglog", + "fill_between", "hist", "subplot", "legend", "xlim", "ylim", "title", + "axis", "xlabel", "ylabel", "grid", "ion", "savefig", "annotate", + "clf", "errorbar", "tight_layout", "stem", "xkcd", "scatter"}; + + for(size_t i = 0; i < function_names.size(); ++i) + { + char const *const &func = function_names[i]; + PyObject *pyobject = PyObject_GetAttrString(pymod, func); + if(!pyobject) + throw std::runtime_error("Couldn't find required function!"); + if(!PyFunction_Check(pyobject)) + throw std::runtime_error("Python object is unexpectedly not a PyFunction."); + + python_functions.insert({func, pyobject}); + } s_python_empty_tuple = PyTuple_New(0); } @@ -230,12 +134,20 @@ struct _interpreter { } // end namespace detail // must be called before the first regular call to matplotlib to have any effect -inline void backend(const std::string& name) +void backend(const std::string& name) { detail::s_backend = name; } -inline bool annotate(std::string annotation, double x, double y) +/** + * Needs to be called before using the plotting functions for Python 3 + */ +void init() +{ + Py_Initialize(); +} + +bool annotate(std::string annotation, double x, double y) { PyObject * xy = PyTuple_New(2); PyObject * str = PyString_FromString(annotation.c_str()); @@ -249,7 +161,7 @@ inline bool annotate(std::string annotation, double x, double y) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, str); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["annotate"], args, kwargs); Py_DECREF(args); Py_DECREF(kwargs); @@ -282,13 +194,13 @@ PyObject* get_array(const std::vector& v) if (type == NPY_NOTYPE) { std::vector vd(v.size()); - npy_intp vsize = v.size(); + npy_intp vsize = static_cast(v.size()); std::copy(v.begin(),v.end(),vd.begin()); PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void*)(vd.data())); return varray; } - npy_intp vsize = v.size(); + npy_intp vsize = static_cast(v.size()); PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data())); return varray; } @@ -328,7 +240,7 @@ bool plot(const std::vector &x, const std::vector &y, const st 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["plot"], args, kwargs); Py_DECREF(args); Py_DECREF(kwargs); @@ -360,7 +272,7 @@ bool stem(const std::vector &x, const std::vector &y, const st } PyObject* res = PyObject_Call( - detail::_interpreter::get().s_python_function_stem, args, kwargs); + detail::_interpreter::get().python_functions["stem"], args, kwargs); Py_DECREF(args); Py_DECREF(kwargs); @@ -391,10 +303,10 @@ bool fill_between(const std::vector& x, const std::vector& y1, 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())); + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); } - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["fill_between"], args, kwargs); Py_DECREF(args); Py_DECREF(kwargs); @@ -420,7 +332,7 @@ bool hist(const std::vector& y, long bins=10,std::string color="b", dou PyTuple_SetItem(plot_args, 0, yarray); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["hist"], plot_args, kwargs); Py_DECREF(plot_args); @@ -445,7 +357,7 @@ bool named_hist(std::string label,const std::vector& y, long bins=10, s 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["hist"], plot_args, kwargs); Py_DECREF(plot_args); Py_DECREF(kwargs); @@ -469,7 +381,7 @@ bool plot(const std::vector& x, const std::vector& y, const 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); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["plot"], plot_args); Py_DECREF(plot_args); if(res) Py_DECREF(res); @@ -493,7 +405,7 @@ bool stem(const std::vector& x, const std::vector& y, const PyTuple_SetItem(plot_args, 2, pystring); PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_stem, plot_args); + detail::_interpreter::get().python_functions["stem"], plot_args); Py_DECREF(plot_args); if (res) @@ -517,7 +429,7 @@ bool semilogx(const std::vector& x, const std::vector& y, co 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); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["semilogx"], plot_args); Py_DECREF(plot_args); if(res) Py_DECREF(res); @@ -540,7 +452,7 @@ bool semilogy(const std::vector& x, const std::vector& y, co 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); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["semilogy"], plot_args); Py_DECREF(plot_args); if(res) Py_DECREF(res); @@ -563,7 +475,7 @@ bool loglog(const std::vector& x, const std::vector& y, cons 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); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["loglog"], plot_args); Py_DECREF(plot_args); if(res) Py_DECREF(res); @@ -590,7 +502,7 @@ bool errorbar(const std::vector &x, const std::vector &y, co 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); + PyObject *res = PyObject_Call(detail::_interpreter::get().python_functions["errorbar"], plot_args, kwargs); Py_DECREF(kwargs); Py_DECREF(plot_args); @@ -603,6 +515,32 @@ bool errorbar(const std::vector &x, const std::vector &y, co return res; } +template +bool scatter(const std::vector& x, const std::vector& y, const std::vector& s, const std::string& format = "") +{ + PyObject* xarray = get_array(x); + PyObject* yarray = get_array(y); + PyObject* sarray = get_array(s); + + PyObject* pystring = PyString_FromString(format.c_str()); + + PyObject* plot_args = PyTuple_New(4); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, sarray); + PyTuple_SetItem(plot_args, 3, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["scatter"], plot_args); + + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + else + throw std::runtime_error("Call to scatter() failed."); + + return res; +} + + template bool named_plot(const std::string& name, const std::vector& y, const std::string& format = "") { @@ -618,11 +556,11 @@ bool named_plot(const std::string& name, const std::vector& y, const st 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["plot"], plot_args, kwargs); Py_DECREF(kwargs); Py_DECREF(plot_args); - if (res) Py_DECREF(res); + if(res) Py_DECREF(res); return res; } @@ -643,11 +581,11 @@ bool named_plot(const std::string& name, const std::vector& x, const st 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["plot"], plot_args, kwargs); Py_DECREF(kwargs); Py_DECREF(plot_args); - if (res) Py_DECREF(res); + if(res) Py_DECREF(res); return res; } @@ -668,11 +606,11 @@ bool named_semilogx(const std::string& name, const std::vector& x, cons 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["semilogx"], plot_args, kwargs); Py_DECREF(kwargs); Py_DECREF(plot_args); - if (res) Py_DECREF(res); + if(res) Py_DECREF(res); return res; } @@ -693,11 +631,11 @@ bool named_semilogy(const std::string& name, const std::vector& x, cons 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["semilogy"], plot_args, kwargs); Py_DECREF(kwargs); Py_DECREF(plot_args); - if (res) Py_DECREF(res); + if(res) Py_DECREF(res); return res; } @@ -718,11 +656,40 @@ bool named_loglog(const std::string& name, const std::vector& x, const 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["loglog"], plot_args, kwargs); Py_DECREF(kwargs); Py_DECREF(plot_args); - if (res) Py_DECREF(res); + if(res) Py_DECREF(res); + + return res; +} + +template +bool named_scatter(const std::string& name, const std::vector& x, const std::vector& y, const std::vector& s, const std::string& color = "C0") +{ + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xarray = get_array(x); + PyObject* yarray = get_array(y); + PyObject* sarray = get_array(s); + + PyObject* colorstring = PyString_FromString(color.c_str()); + + PyObject* plot_args = PyTuple_New(4); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, sarray); + PyTuple_SetItem(plot_args, 3, colorstring); + + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["scatter"], plot_args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + else + throw std::runtime_error("Call to named_scatter() failed."); return res; } @@ -731,7 +698,7 @@ template bool plot(const std::vector& y, const std::string& format = "") { std::vector x(y.size()); - for(size_t i=0; i(i); return plot(x,y,format); } @@ -739,13 +706,13 @@ 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; + for (size_t i = 0; i < x.size(); ++i) x.at(i) = static_cast(i); return stem(x, y, format); } inline void figure() { - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["figure"], detail::_interpreter::get().s_python_empty_tuple); if(!res) throw std::runtime_error("Call to figure() failed."); Py_DECREF(res); @@ -753,7 +720,7 @@ inline void figure() inline void legend() { - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["legend"], detail::_interpreter::get().s_python_empty_tuple); if(!res) throw std::runtime_error("Call to legend() failed."); Py_DECREF(res); @@ -769,7 +736,7 @@ void ylim(Numeric left, Numeric right) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, list); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["ylim"], args); if(!res) throw std::runtime_error("Call to ylim() failed."); Py_DECREF(args); @@ -786,7 +753,7 @@ void xlim(Numeric left, Numeric right) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, list); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["xlim"], args); if(!res) throw std::runtime_error("Call to xlim() failed."); Py_DECREF(args); @@ -797,7 +764,10 @@ void xlim(Numeric left, Numeric right) inline double* xlim() { PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["xlim"], args); + if(!res) throw std::runtime_error("Call to xlim() failed."); + PyObject* left = PyTuple_GetItem(res,0); PyObject* right = PyTuple_GetItem(res,1); @@ -805,8 +775,6 @@ inline double* xlim() arr[0] = PyFloat_AsDouble(left); arr[1] = PyFloat_AsDouble(right); - if(!res) throw std::runtime_error("Call to xlim() failed."); - Py_DECREF(res); return arr; } @@ -815,7 +783,10 @@ inline double* xlim() inline double* ylim() { PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["ylim"], args); + if(!res) throw std::runtime_error("Call to ylim() failed."); + PyObject* left = PyTuple_GetItem(res,0); PyObject* right = PyTuple_GetItem(res,1); @@ -823,8 +794,6 @@ inline double* ylim() arr[0] = PyFloat_AsDouble(left); arr[1] = PyFloat_AsDouble(right); - if(!res) throw std::runtime_error("Call to ylim() failed."); - Py_DECREF(res); return arr; } @@ -833,11 +802,11 @@ inline void subplot(long nrows, long ncols, long plot_number) { // construct positional args PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); + PyTuple_SetItem(args, 0, PyLong_FromLong(nrows)); + PyTuple_SetItem(args, 1, PyLong_FromLong(ncols)); + PyTuple_SetItem(args, 2, PyLong_FromLong(plot_number)); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["subplot"], args); if(!res) throw std::runtime_error("Call to subplot() failed."); Py_DECREF(args); @@ -850,7 +819,7 @@ inline void title(const std::string &titlestr) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, pytitlestr); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_title, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["title"], args); if(!res) throw std::runtime_error("Call to title() failed."); Py_DECREF(args); @@ -863,7 +832,7 @@ inline void axis(const std::string &axisstr) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, str); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["axis"], args); if(!res) throw std::runtime_error("Call to title() failed."); Py_DECREF(args); @@ -876,7 +845,7 @@ inline void xlabel(const std::string &str) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, pystr); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlabel, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["xlabel"], args); if(!res) throw std::runtime_error("Call to xlabel() failed."); Py_DECREF(args); @@ -889,7 +858,7 @@ inline void ylabel(const std::string &str) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, pystr); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylabel, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["ylabel"], args); if(!res) throw std::runtime_error("Call to ylabel() failed."); Py_DECREF(args); @@ -904,7 +873,7 @@ inline void grid(bool flag) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, pyflag); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["grid"], args); if(!res) throw std::runtime_error("Call to grid() failed."); Py_DECREF(args); @@ -917,15 +886,15 @@ inline void show(const bool block = true) if(block) { res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_show, - detail::_interpreter::get().s_python_empty_tuple); + detail::_interpreter::get().python_functions["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); + res = PyObject_Call( detail::_interpreter::get().python_functions["show"], detail::_interpreter::get().s_python_empty_tuple, kwargs); + Py_DECREF(kwargs); } @@ -937,8 +906,8 @@ inline void show(const bool block = true) inline void close() { PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_close, - detail::_interpreter::get().s_python_empty_tuple); + detail::_interpreter::get().python_functions["close"], + detail::_interpreter::get().s_python_empty_tuple); if (!res) throw std::runtime_error("Call to close() failed."); @@ -949,9 +918,8 @@ inline void xkcd() { PyObject* res; PyObject *kwargs = PyDict_New(); - res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd, - detail::_interpreter::get().s_python_empty_tuple, kwargs); - + res = PyObject_Call(detail::_interpreter::get().python_functions["xkcd"], + detail::_interpreter::get().s_python_empty_tuple, kwargs); Py_DECREF(kwargs); if (!res) @@ -963,7 +931,7 @@ inline void xkcd() { inline void draw() { PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_draw, + detail::_interpreter::get().python_functions["draw"], detail::_interpreter::get().s_python_empty_tuple); if (!res) throw std::runtime_error("Call to draw() failed."); @@ -972,12 +940,12 @@ inline void draw() } template -inline void pause(Numeric interval) +void pause(Numeric interval) { PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval)); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["pause"], args); if(!res) throw std::runtime_error("Call to pause() failed."); Py_DECREF(args); @@ -991,7 +959,7 @@ inline void save(const std::string& filename) PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, pyfilename); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["savefig"], args); if (!res) throw std::runtime_error("Call to save() failed."); Py_DECREF(args); @@ -1000,7 +968,7 @@ inline void save(const std::string& filename) inline void clf() { PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_clf, + detail::_interpreter::get().python_functions["clf"], detail::_interpreter::get().s_python_empty_tuple); if (!res) throw std::runtime_error("Call to clf() failed."); @@ -1008,9 +976,9 @@ inline void clf() { Py_DECREF(res); } - inline void ion() { +inline void ion() { PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_ion, + detail::_interpreter::get().python_functions["ion"], detail::_interpreter::get().s_python_empty_tuple); if (!res) throw std::runtime_error("Call to ion() failed."); @@ -1021,7 +989,7 @@ inline void clf() { // Actually, is there any reason not to call this automatically for every plot? inline void tight_layout() { PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_tight_layout, + detail::_interpreter::get().python_functions["tight_layout"], detail::_interpreter::get().s_python_empty_tuple); if (!res) throw std::runtime_error("Call to tight_layout() failed."); @@ -1106,7 +1074,7 @@ struct plot_impl 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); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().python_functions["plot"], plot_args); Py_DECREF(plot_args); if(res) Py_DECREF(res); @@ -1147,15 +1115,15 @@ bool plot(const A& a, const B& b, const std::string& format, Args... 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 = "") { +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 = "") { +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) { +bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { return plot(x,y,keywords); } diff --git a/package.xml b/package.xml new file mode 100644 index 00000000..65932beb --- /dev/null +++ b/package.xml @@ -0,0 +1,19 @@ + + + matplotlibcpp + 0.1.0 + The matplotlibcpp package + + Kartik Mohta + + Benno Evers + + MIT + + catkin + + + cmake + + + From f938132a90849486ba2da974d95e404462e26425 Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Tue, 19 Jun 2018 17:14:51 -0400 Subject: [PATCH 2/9] Add a FindNumPy.cmake in case numpy headers are not in /usr/include --- CMakeLists.txt | 5 ++- cmake/FindNumPy.cmake | 102 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 cmake/FindNumPy.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 9504ff51..d113b07a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,12 +2,15 @@ cmake_minimum_required(VERSION 3.0) project(matplotlibcpp VERSION 0.1.0 LANGUAGES CXX) +LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + find_package(PythonLibs REQUIRED) +find_package(NumPy REQUIRED) # In case numpy headers are at non-standard locations add_library(${PROJECT_NAME} INTERFACE) target_include_directories(${PROJECT_NAME} INTERFACE $) -target_include_directories(${PROJECT_NAME} INTERFACE ${PYTHON_INCLUDE_DIRS}) +target_include_directories(${PROJECT_NAME} INTERFACE ${PYTHON_INCLUDE_DIRS} ${NUMPY_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} INTERFACE ${PYTHON_LIBRARIES}) install(TARGETS ${PROJECT_NAME} EXPORT "${PROJECT_NAME}Targets" diff --git a/cmake/FindNumPy.cmake b/cmake/FindNumPy.cmake new file mode 100644 index 00000000..f14142f1 --- /dev/null +++ b/cmake/FindNumPy.cmake @@ -0,0 +1,102 @@ +# - Find the NumPy libraries +# This module finds if NumPy is installed, and sets the following variables +# indicating where it is. +# +# TODO: Update to provide the libraries and paths for linking npymath lib. +# +# NUMPY_FOUND - was NumPy found +# NUMPY_VERSION - the version of NumPy found as a string +# NUMPY_VERSION_MAJOR - the major version number of NumPy +# NUMPY_VERSION_MINOR - the minor version number of NumPy +# NUMPY_VERSION_PATCH - the patch version number of NumPy +# NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 +# NUMPY_INCLUDE_DIRS - path to the NumPy include files + +#============================================================================ +# Copyright 2012 Continuum Analytics, Inc. +# +# MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +#============================================================================ + +# Finding NumPy involves calling the Python interpreter +if(NumPy_FIND_REQUIRED) + find_package(PythonInterp REQUIRED) +else() + find_package(PythonInterp) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(NUMPY_FOUND FALSE) + return() +endif() + +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "import numpy as n; print(n.__version__); print(n.get_include());" + RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS + OUTPUT_VARIABLE _NUMPY_VALUES_OUTPUT + ERROR_VARIABLE _NUMPY_ERROR_VALUE + OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0) + if(NumPy_FIND_REQUIRED) + message(FATAL_ERROR + "NumPy import failure:\n${_NUMPY_ERROR_VALUE}") + endif() + set(NUMPY_FOUND FALSE) + return() +endif() + +# Convert the process output into a list +string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES_OUTPUT}) +string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES}) +# Just in case there is unexpected output from the Python command. +list(GET _NUMPY_VALUES -2 NUMPY_VERSION) +list(GET _NUMPY_VALUES -1 NUMPY_INCLUDE_DIRS) + +string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NUMPY_VERSION}") +if("${_VER_CHECK}" STREQUAL "") + # The output from Python was unexpected. Raise an error always + # here, because we found NumPy, but it appears to be corrupted somehow. + message(FATAL_ERROR + "Requested version and include path from NumPy, got instead:\n${_NUMPY_VALUES_OUTPUT}\n") + return() +endif() + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS}) + +# Get the major and minor version numbers +string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION}) +list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR) +list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR) +list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH) +string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH}) +math(EXPR NUMPY_VERSION_DECIMAL + "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}") + +find_package_message(NUMPY + "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}" + "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}") + +set(NUMPY_FOUND TRUE) + From d5a95537fb8fa30fe28b4295880b6c0616b70610 Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Tue, 14 Aug 2018 01:12:05 -0400 Subject: [PATCH 3/9] Rename variables in FindNumPy.cmake from NUMPY_* to NumPy_* --- CMakeLists.txt | 2 +- cmake/FindNumPy.cmake | 65 +++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d113b07a..a26587db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ find_package(NumPy REQUIRED) # In case numpy headers are at non-standard locatio add_library(${PROJECT_NAME} INTERFACE) target_include_directories(${PROJECT_NAME} INTERFACE $) -target_include_directories(${PROJECT_NAME} INTERFACE ${PYTHON_INCLUDE_DIRS} ${NUMPY_INCLUDE_DIRS}) +target_include_directories(${PROJECT_NAME} INTERFACE ${PYTHON_INCLUDE_DIRS} ${NumPy_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} INTERFACE ${PYTHON_LIBRARIES}) install(TARGETS ${PROJECT_NAME} EXPORT "${PROJECT_NAME}Targets" diff --git a/cmake/FindNumPy.cmake b/cmake/FindNumPy.cmake index f14142f1..5844e864 100644 --- a/cmake/FindNumPy.cmake +++ b/cmake/FindNumPy.cmake @@ -4,13 +4,13 @@ # # TODO: Update to provide the libraries and paths for linking npymath lib. # -# NUMPY_FOUND - was NumPy found -# NUMPY_VERSION - the version of NumPy found as a string -# NUMPY_VERSION_MAJOR - the major version number of NumPy -# NUMPY_VERSION_MINOR - the minor version number of NumPy -# NUMPY_VERSION_PATCH - the patch version number of NumPy -# NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 -# NUMPY_INCLUDE_DIRS - path to the NumPy include files +# NumPy_FOUND - was NumPy found +# NumPy_VERSION - the version of NumPy found as a string +# NumPy_VERSION_MAJOR - the major version number of NumPy +# NumPy_VERSION_MINOR - the minor version number of NumPy +# NumPy_VERSION_PATCH - the patch version number of NumPy +# NumPy_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 +# NumPy_INCLUDE_DIRS - path to the NumPy include files #============================================================================ # Copyright 2012 Continuum Analytics, Inc. @@ -46,57 +46,56 @@ else() endif() if(NOT PYTHONINTERP_FOUND) - set(NUMPY_FOUND FALSE) + set(NumPy_FOUND FALSE) return() endif() execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" "import numpy as n; print(n.__version__); print(n.get_include());" - RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS - OUTPUT_VARIABLE _NUMPY_VALUES_OUTPUT - ERROR_VARIABLE _NUMPY_ERROR_VALUE + RESULT_VARIABLE _NumPy_SEARCH_SUCCESS + OUTPUT_VARIABLE _NumPy_VALUES_OUTPUT + ERROR_VARIABLE _NumPy_ERROR_VALUE OUTPUT_STRIP_TRAILING_WHITESPACE) -if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0) +if(NOT _NumPy_SEARCH_SUCCESS MATCHES 0) if(NumPy_FIND_REQUIRED) message(FATAL_ERROR - "NumPy import failure:\n${_NUMPY_ERROR_VALUE}") + "NumPy import failure:\n${_NumPy_ERROR_VALUE}") endif() - set(NUMPY_FOUND FALSE) + set(NumPy_FOUND FALSE) return() endif() # Convert the process output into a list -string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES_OUTPUT}) -string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES}) +string(REGEX REPLACE ";" "\\\\;" _NumPy_VALUES ${_NumPy_VALUES_OUTPUT}) +string(REGEX REPLACE "\n" ";" _NumPy_VALUES ${_NumPy_VALUES}) # Just in case there is unexpected output from the Python command. -list(GET _NUMPY_VALUES -2 NUMPY_VERSION) -list(GET _NUMPY_VALUES -1 NUMPY_INCLUDE_DIRS) +list(GET _NumPy_VALUES -2 NumPy_VERSION) +list(GET _NumPy_VALUES -1 NumPy_INCLUDE_DIRS) -string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NUMPY_VERSION}") +string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NumPy_VERSION}") if("${_VER_CHECK}" STREQUAL "") # The output from Python was unexpected. Raise an error always # here, because we found NumPy, but it appears to be corrupted somehow. message(FATAL_ERROR - "Requested version and include path from NumPy, got instead:\n${_NUMPY_VALUES_OUTPUT}\n") + "Requested version and include path from NumPy, got instead:\n${_NumPy_VALUES_OUTPUT}\n") return() endif() # Make sure all directory separators are '/' -string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS}) +string(REGEX REPLACE "\\\\" "/" NumPy_INCLUDE_DIRS ${NumPy_INCLUDE_DIRS}) # Get the major and minor version numbers -string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION}) -list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR) -list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR) -list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH) -string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH}) -math(EXPR NUMPY_VERSION_DECIMAL - "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}") +string(REGEX REPLACE "\\." ";" _NumPy_VERSION_LIST ${NumPy_VERSION}) +list(GET _NumPy_VERSION_LIST 0 NumPy_VERSION_MAJOR) +list(GET _NumPy_VERSION_LIST 1 NumPy_VERSION_MINOR) +list(GET _NumPy_VERSION_LIST 2 NumPy_VERSION_PATCH) +string(REGEX MATCH "[0-9]*" NumPy_VERSION_PATCH ${NumPy_VERSION_PATCH}) +math(EXPR NumPy_VERSION_DECIMAL + "(${NumPy_VERSION_MAJOR} * 10000) + (${NumPy_VERSION_MINOR} * 100) + ${NumPy_VERSION_PATCH}") -find_package_message(NUMPY - "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}" - "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}") - -set(NUMPY_FOUND TRUE) +find_package_message(NumPy + "Found NumPy: version \"${NumPy_VERSION}\" ${NumPy_INCLUDE_DIRS}" + "${NumPy_INCLUDE_DIRS}${NumPy_VERSION}") +set(NumPy_FOUND TRUE) From 693391ec4781f0559de51601bfbf741874a81bfe Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Tue, 14 Aug 2018 01:12:46 -0400 Subject: [PATCH 4/9] Install FindNumpy.cmake + include it in the generated cmake config file --- CMakeLists.txt | 8 ++++++-- cmake/Config.cmake.in | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a26587db..cb7db846 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,12 +26,16 @@ install(DIRECTORY "include/${PROJECT_NAME}" PATTERN ".svn" EXCLUDE ) +install(FILES cmake/FindNumPy.cmake + DESTINATION "share/${PROJECT_NAME}/cmake" + ) + include(cmake/GenerateAndInstallConfig.cmake) generate_and_install_config_file( INCLUDE_DIRS "include" LIBRARIES - DEPS PythonLibs - DEPS_INCLUDE_DIRS PYTHON + DEPS PythonLibs NumPy + DEPS_INCLUDE_DIRS PYTHON NumPy DEPS_LIBRARIES PYTHON ) diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index 94d42cd5..f44fecee 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -1,5 +1,7 @@ @PACKAGE_INIT@ +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + include(CMakeFindDependencyMacro) @PROJECT_DEPS@ From 0dc923395a47fb8c414e8094f5181a2a4831476c Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Tue, 14 Aug 2018 01:30:51 -0400 Subject: [PATCH 5/9] Small fix in the figure_size function --- include/matplotlibcpp/matplotlibcpp.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/matplotlibcpp/matplotlibcpp.h b/include/matplotlibcpp/matplotlibcpp.h index deff99af..4be7572f 100644 --- a/include/matplotlibcpp/matplotlibcpp.h +++ b/include/matplotlibcpp/matplotlibcpp.h @@ -729,8 +729,7 @@ inline void figure_size(size_t w, size_t h) 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); + PyObject* res = PyObject_Call(detail::_interpreter::get().python_functions["figure"], detail::_interpreter::get().s_python_empty_tuple, kwargs); Py_DECREF(kwargs); From 30d56de3aec8e19edab5f81512731c285875976e Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Sun, 19 Aug 2018 21:42:26 -0400 Subject: [PATCH 6/9] Remove unused variable warning in errorbar --- include/matplotlibcpp/matplotlibcpp.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/matplotlibcpp/matplotlibcpp.h b/include/matplotlibcpp/matplotlibcpp.h index 4be7572f..8c444afe 100644 --- a/include/matplotlibcpp/matplotlibcpp.h +++ b/include/matplotlibcpp/matplotlibcpp.h @@ -484,7 +484,7 @@ bool loglog(const std::vector& x, const std::vector& y, cons } template -bool errorbar(const std::vector &x, const std::vector &y, const std::vector &yerr, const std::string &s = "") +bool errorbar(const std::vector &x, const std::vector &y, const std::vector &yerr, const std::string &fmt = "") { assert(x.size() == y.size()); @@ -496,7 +496,8 @@ bool errorbar(const std::vector &x, const std::vector &y, co PyDict_SetItemString(kwargs, "yerr", yerrarray); - PyObject *pystring = PyString_FromString(s.c_str()); + PyObject *pystring = PyString_FromString(fmt.c_str()); + PyDict_SetItemString(kwargs, "fmt", pystring); PyObject *plot_args = PyTuple_New(2); PyTuple_SetItem(plot_args, 0, xarray); From 2da6cc63d88c1592c2b23210650e6f1fa2363d72 Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Thu, 22 Aug 2019 01:21:01 -0700 Subject: [PATCH 7/9] Add build folder to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7622be79..60d7cfa3 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ # Build /examples/build/* +/build From 01971d35ec4e3d0c7f1d0f436bae01d29f056f5e Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Fri, 21 Feb 2020 15:49:13 -0800 Subject: [PATCH 8/9] Get the correct python version based on the default python interpreter --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca0b242c..d914941f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,12 @@ set(CMAKE_CXX_STANDARD 11) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +# Call find_package(PythonInterp) first to get the currently active Python version and use that for +# setting the version to find for find_package(PythonLibs) +find_package(PythonInterp REQUIRED) +set(Python_ADDITIONAL_VERSIONS ${PYTHON_VERSION_STRING}) find_package(PythonLibs REQUIRED) + find_package(NumPy REQUIRED) # In case numpy headers are at non-standard locations add_library(${PROJECT_NAME} INTERFACE) @@ -36,7 +41,7 @@ include(cmake/GenerateAndInstallConfig.cmake) generate_and_install_config_file( INCLUDE_DIRS "include" LIBRARIES - DEPS PythonLibs NumPy + DEPS "PythonLibs ${Python_ADDITIONAL_VERSIONS}" NumPy DEPS_INCLUDE_DIRS PYTHON NumPy DEPS_LIBRARIES PYTHON ) From 5edd48b0e7f30bf00119f23224c4364f12d088e7 Mon Sep 17 00:00:00 2001 From: Kartik Mohta Date: Fri, 21 Feb 2020 15:50:05 -0800 Subject: [PATCH 9/9] Make numpy optional --- CMakeLists.txt | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d914941f..394826e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.0) project(matplotlibcpp VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_EXTENSIONS OFF) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -12,13 +13,18 @@ find_package(PythonInterp REQUIRED) set(Python_ADDITIONAL_VERSIONS ${PYTHON_VERSION_STRING}) find_package(PythonLibs REQUIRED) -find_package(NumPy REQUIRED) # In case numpy headers are at non-standard locations +find_package(NumPy) # In case numpy headers are at non-standard locations add_library(${PROJECT_NAME} INTERFACE) target_include_directories(${PROJECT_NAME} INTERFACE $) target_include_directories(${PROJECT_NAME} INTERFACE ${PYTHON_INCLUDE_DIRS} ${NumPy_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} INTERFACE ${PYTHON_LIBRARIES}) +if(NOT NumPy_FOUND) + target_compile_definitions(${PROJECT_NAME} INTERFACE "-DWITHOUT_NUMPY") +else() + set(NUMPY_DEP "NumPy") +endif() install(TARGETS ${PROJECT_NAME} EXPORT "${PROJECT_NAME}Targets" LIBRARY DESTINATION "lib" @@ -33,16 +39,18 @@ install(DIRECTORY "include/${PROJECT_NAME}" PATTERN ".svn" EXCLUDE ) +if(NumPy_FOUND) install(FILES cmake/FindNumPy.cmake DESTINATION "share/${PROJECT_NAME}/cmake" ) +endif() include(cmake/GenerateAndInstallConfig.cmake) generate_and_install_config_file( INCLUDE_DIRS "include" LIBRARIES - DEPS "PythonLibs ${Python_ADDITIONAL_VERSIONS}" NumPy - DEPS_INCLUDE_DIRS PYTHON NumPy + DEPS "PythonLibs ${Python_ADDITIONAL_VERSIONS}" ${NUMPY_DEP} + DEPS_INCLUDE_DIRS PYTHON ${NUMPY_DEP} DEPS_LIBRARIES PYTHON ) @@ -65,12 +73,13 @@ if(MATPLOTLIBCPP_EXAMPLES) add_executable(fill_inbetween "examples/fill_inbetween.cpp") target_link_libraries(fill_inbetween matplotlibcpp) - add_executable(imshow "examples/imshow.cpp") - target_link_libraries(imshow matplotlibcpp) + if(NumPy_FOUND) + add_executable(imshow "examples/imshow.cpp") + target_link_libraries(imshow matplotlibcpp) + endif() add_executable(minimal "examples/minimal.cpp") target_link_libraries(minimal matplotlibcpp) - target_compile_definitions(minimal PRIVATE "-DWITHOUT_NUMPY") add_executable(modern "examples/modern.cpp") target_link_libraries(modern matplotlibcpp) @@ -87,8 +96,10 @@ if(MATPLOTLIBCPP_EXAMPLES) add_executable(subplot2grid "examples/subplot2grid.cpp") target_link_libraries(subplot2grid matplotlibcpp) - add_executable(surface "examples/surface.cpp") - target_link_libraries(surface matplotlibcpp) + if(NumPy_FOUND) + add_executable(surface "examples/surface.cpp") + target_link_libraries(surface matplotlibcpp) + endif() add_executable(update "examples/update.cpp") target_link_libraries(update matplotlibcpp)