Skip to content

Commit 18db2e1

Browse files
committed
Squashed commit of the following:
commit 9047863bb0c1a34bc6473c2dadee720073db631e Author: Claire Guilbaud <[email protected]> Date: Thu Mar 26 15:46:34 2020 +0100 @enh figure with variadic template args commit a8a3dd018c14a5e5c98b084f566865ba936c4fec Author: Claire Guilbaud <[email protected]> Date: Thu Mar 26 14:55:36 2020 +0100 @enh close commit 6440609657de0f47b6fd4c1a8c213704f2810a4f Author: Claire Guilbaud <[email protected]> Date: Thu Mar 26 13:41:18 2020 +0100 @enh generic form for scatter
1 parent 59fc635 commit 18db2e1

File tree

5 files changed

+217
-31
lines changed

5 files changed

+217
-31
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333

3434
# Build
3535
/examples/build/*
36+
/contrib/unittests_catch2/build/*
37+
/contrib/unittests_catch2/cmake-
38+
/contrib/unittests_catch2/build/*
39+
/contrib/unittests_catch2/cmake-*
3640

3741
# vim temp files
3842
*.sw*
43+
44+
# IDE directories
45+
.idea

contrib/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Note: platforms folder is necessary to make qt works.
3535

3636
```cmd
3737
> cd contrib/unittests_catch2
38-
> cmake -S -B "./build"
38+
> cmake -S . -B "./build"
3939
> cd build
4040
> make
4141
> ./run_MatplotlibcppTests

contrib/unittests_catch2/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ target_compile_features(${TU_EXE}
7676
)
7777
target_compile_definitions(${TU_EXE}
7878
PRIVATE
79+
USE_VARIADIC_TEMPLATES_ARGS
7980
CATCH_UNIT_TESTS)
8081
target_compile_options(${TU_EXE}
8182
PRIVATE

contrib/unittests_catch2/tu_matplotlibcpp.cpp

+24-12
Original file line numberDiff line numberDiff line change
@@ -304,15 +304,14 @@ TEST_CASE("matplotlib", "[matplotlibcpp][matplotlib]") {
304304
REQUIRE_NOTHROW(plt::scatter(x_scatter, y_scatter, 1.0, kw_2));
305305
plt::show(true);
306306

307-
// @FIXME
308-
// plt::clf();
309-
// REQUIRE_NOTHROW(plt::scatter(x_scatter, y_scatter, make_tuple(
310-
// "cmap", "twilight",
311-
// "c", z_scatter,
312-
// "s", 46.,
313-
// "marker", "o"
314-
// )));
315-
// plt::show(true);
307+
plt::clf();
308+
REQUIRE_NOTHROW(plt::scatter(x_scatter, y_scatter, make_tuple(
309+
"cmap", "twilight",
310+
"c", z_scatter,
311+
"s", 46.,
312+
"marker", "o"
313+
)));
314+
plt::show(true);
316315
}
317316

318317
SECTION("quiver") {
@@ -534,12 +533,25 @@ TEST_CASE("matplotlib_ending", "[matplotlibcpp][matplotlib]") {
534533
plt::clf();
535534
plt::named_plot("Courbe", x, y);
536535
plt::show(true);
537-
REQUIRE_NOTHROW(plt::close());
536+
REQUIRE_NOTHROW(plt::close(fig_num));
538537
}
539538

540539
TEST_CASE("matplotlib_figure", "[matplotlibcpp]") {
541540
plt::ion();
542-
REQUIRE_NOTHROW(plt::figure_size(100, 100));
543-
plt::show(true);
541+
542+
SECTION("figure_size") {
543+
REQUIRE_NOTHROW(plt::figure_size(100, 100));
544+
plt::show(true);
545+
REQUIRE_NOTHROW(plt::close());
546+
}
547+
548+
SECTION("figure_template") {
549+
vector<double> fsize {6., 4.};
550+
REQUIRE_NOTHROW(plt::figure(111, make_tuple(
551+
"dpi", 50,
552+
"edgecolor", "green",
553+
"figsize", fsize
554+
)));
555+
}
544556
}
545557
#endif // CATCH_UNIT_TESTS

matplotlibcpp.h

+184-18
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,103 @@ PyObject* get_listlist(const std::vector<std::vector<Numeric>>& ll)
468468

469469
} // namespace detail
470470

471+
472+
#ifdef USE_VARIADIC_TEMPLATES_ARGS
473+
474+
// ---------------------------------------------
475+
// Analyse des keywords dans un tuple
476+
//
477+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const bool value) {
478+
return {key, value ? Py_True : Py_False};
479+
}
480+
481+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const int value) {
482+
return {key, PyLong_FromLong(value)};
483+
}
484+
485+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const long value) {
486+
return {key, PyLong_FromLong(value)};
487+
}
488+
489+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const double value) {
490+
return {key, PyFloat_FromDouble(value)};
491+
}
492+
493+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const char* value) {
494+
return {key, PyString_FromString(value)};
495+
}
496+
497+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const std::string& value) {
498+
PyObject* str_value = PyString_FromString(value.c_str());
499+
return {key, str_value};
500+
}
501+
502+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const std::vector<double>& value) {
503+
// PyObject* py_levels = PyList_New(value.size());
504+
// for (size_t i = 0; i < value.size(); ++i) {
505+
// PyList_SetItem(py_levels, i, PyFloat_FromDouble(value.at(i)));
506+
// }
507+
return {key, get_array(value)};
508+
}
509+
510+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const std::vector<int>& value) {
511+
PyObject* args = PyTuple_New(value.size());
512+
for(size_t i = 0; i < value.size(); ++i) {
513+
PyTuple_SetItem(args, i, PyLong_FromLong(value.at(i)));
514+
}
515+
return {key, get_array(value)};
516+
}
517+
518+
template<class Tuple, std::size_t N>
519+
struct AnalyzeKeywordsHelper {
520+
static void analyze_keywords(const Tuple& kw, PyObject* keywords) {
521+
AnalyzeKeywordsHelper<Tuple, N-2>::analyze_keywords(kw, keywords);
522+
std::string key;
523+
PyObject* value;
524+
std::tie(key, value) = analyze_key_value(std::get<N-2>(kw), std::get<N-1>(kw));
525+
PyDict_SetItemString(keywords, key.c_str(), value);
526+
Py_DECREF(value); // @TST
527+
}
528+
};
529+
530+
template<class Tuple>
531+
struct AnalyzeKeywordsHelper<Tuple, 2> {
532+
static void analyze_keywords(const Tuple& kw, PyObject* keywords) {
533+
std::string key;
534+
PyObject* value;
535+
std::tie(key, value) = analyze_key_value(std::get<0>(kw), std::get<1>(kw));
536+
PyDict_SetItemString(keywords, key.c_str(), value);
537+
Py_DECREF(value); // @TST
538+
}
539+
};
540+
541+
template<class... Args>
542+
PyObject* analyze_keywords(const std::tuple<Args...>& kw) {
543+
// Inutile de vérifier que la longueur du tuple est un multiple de 2,
544+
// car comme on avance de 2 en 2, l'instanciation du template ne peut pas se faire pour
545+
// un tuple qui n'a pas un nombre pair d'items.
546+
PyObject* keywords = PyDict_New();
547+
AnalyzeKeywordsHelper<decltype(kw), sizeof...(Args)>::analyze_keywords(kw, keywords);
548+
return keywords;
549+
}
550+
551+
// ----------
552+
// Identity type conversion to PyObject* (for functions `figure(Identity, tuple<Args...>&)` and `close(Identity)`)
553+
//
554+
PyObject* get_pyobject_from(const int value) {
555+
return PyLong_FromLong(value);
556+
}
557+
558+
PyObject* get_pyobject_from(const char* value) {
559+
return PyString_FromString(value);
560+
}
561+
562+
PyObject* get_pyobject_from(const std::string& value) {
563+
return PyString_FromString(value.c_str());
564+
}
565+
#endif // USE_VARIADIC_TEMPLATES_ARGS
566+
567+
471568
/// Plot a line through the given x and y data points..
472569
///
473570
/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html
@@ -894,36 +991,58 @@ void imshow(const cv::Mat &image, const std::map<std::string, std::string> &keyw
894991
#endif // WITHOUT_NUMPY
895992

896993
template<typename NumericX, typename NumericY>
897-
bool scatter(const std::vector<NumericX>& x,
994+
bool __scatter(const std::vector<NumericX>& x,
898995
const std::vector<NumericY>& y,
899-
const double s=1.0, // The marker size in points**2
900-
const std::map<std::string, std::string> & keywords = {})
996+
PyObject* kwargs)
901997
{
902-
detail::_interpreter::get();
998+
detail::_interpreter::get();
999+
1000+
assert(x.size() == y.size());
9031001

904-
assert(x.size() == y.size());
1002+
PyObject* xarray = get_array(x);
1003+
PyObject* yarray = get_array(y);
9051004

906-
PyObject* xarray = detail::get_array(x);
907-
PyObject* yarray = detail::get_array(y);
1005+
PyObject* args = PyTuple_New(2);
1006+
PyTuple_SetItem(args, 0, xarray);
1007+
PyTuple_SetItem(args, 1, yarray);
1008+
1009+
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, args, kwargs);
9081010

1011+
Py_DECREF(args);
1012+
Py_DECREF(kwargs);
1013+
1014+
if(!res) {
1015+
throw std::runtime_error("Call to scatter(x, y [, marker]) failed.");
1016+
}
1017+
1018+
Py_DECREF(res);
1019+
1020+
return res;
1021+
}
1022+
1023+
// generic form
1024+
template <typename Numeric, class... Args>
1025+
inline bool scatter(const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::tuple<Args...>& keywords)
1026+
{
1027+
PyObject* kwargs = analyze_keywords(keywords);
1028+
return __scatter(x, y, kwargs);
1029+
}
1030+
1031+
// specialized form
1032+
template<typename NumericX, typename NumericY>
1033+
bool scatter(const std::vector<NumericX>& x,
1034+
const std::vector<NumericY>& y,
1035+
const double s=1.0, // The marker size in points**2
1036+
const std::map<std::string, std::string> & keywords = {})
1037+
{
9091038
PyObject* kwargs = PyDict_New();
9101039
PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s));
9111040
for (const auto& it : keywords)
9121041
{
9131042
PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
9141043
}
9151044

916-
PyObject* plot_args = PyTuple_New(2);
917-
PyTuple_SetItem(plot_args, 0, xarray);
918-
PyTuple_SetItem(plot_args, 1, yarray);
919-
920-
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs);
921-
922-
Py_DECREF(plot_args);
923-
Py_DECREF(kwargs);
924-
if(res) Py_DECREF(res);
925-
926-
return res;
1045+
return __scatter(x, y, kwargs);
9271046
}
9281047

9291048
template<typename Numeric>
@@ -1523,6 +1642,36 @@ inline long figure(long number = -1)
15231642
return figureNumber;
15241643
}
15251644

1645+
template <typename Identity, class... Args>
1646+
inline Identity figure(Identity number, const std::tuple<Args...>& keywords)
1647+
{
1648+
// Identity est de type int ou string
1649+
PyObject* args = PyTuple_New(1);
1650+
PyTuple_SetItem(args, 0, get_pyobject_from(number));
1651+
1652+
PyObject* kwargs = analyze_keywords(keywords);
1653+
1654+
PyObject* res = nullptr;
1655+
res = PyObject_Call(detail::_interpreter::get().s_python_function_figure, args, kwargs);
1656+
1657+
Py_DECREF(args);
1658+
Py_DECREF(kwargs);
1659+
1660+
if(!res) {
1661+
PyErr_Print();
1662+
throw std::runtime_error("Call to pyplot.figure(num) failed.");
1663+
}
1664+
1665+
PyObject* num = PyObject_GetAttrString(res, "number");
1666+
if (!num) throw std::runtime_error("Could not get number attribute of figure object");
1667+
auto figureNumber = static_cast<Identity>(PyLong_AsLong(num));
1668+
1669+
Py_DECREF(num);
1670+
Py_DECREF(res);
1671+
1672+
return figureNumber;
1673+
}
1674+
15261675
inline bool fignum_exists(long number)
15271676
{
15281677
detail::_interpreter::get();
@@ -2048,6 +2197,23 @@ inline void close()
20482197
Py_DECREF(res);
20492198
}
20502199

2200+
template <typename Identity>
2201+
inline void close(Identity num)
2202+
{
2203+
PyObject* args = PyTuple_New(1);
2204+
PyTuple_SetItem(args, 0, get_pyobject_from(num));
2205+
2206+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_close, args);
2207+
2208+
Py_DECREF(args);
2209+
2210+
if (!res) {
2211+
throw std::runtime_error("Call to close(num) failed.");
2212+
}
2213+
2214+
Py_DECREF(res);
2215+
}
2216+
20512217
inline void xkcd() {
20522218
detail::_interpreter::get();
20532219

0 commit comments

Comments
 (0)