Skip to content

Develop #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ find_package(pybind11 2.4.3 REQUIRED)
# check matplotlib minor version
execute_process(
COMMAND
"python3" "-c"
${Python3_EXECUTABLE} "-c"
"import matplotlib;
print(str(matplotlib.__version__))"
RESULT_VARIABLE MATPLOTLIB_VERSION_CHECKING
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ It is supposed to provide the user with almost full access to matplotlib feature
- [pybind11](https://github.com/pybind/pybind11) >= 2.4.3
- `sudo apt install pybind11-dev` (on Ubuntu20.04)
- or manual install
- compatible with [matplotlib](https://matplotlib.org/stable/index.html) == 3.5.1
- [matplotlib](https://matplotlib.org/stable/index.html) >= 3.4.0
- numpy for `mplot3d`
- ([xtensor](https://github.com/xtensor-stack/xtensor) == 0.24.0 + [xtl](https://github.com/xtensor-stack/xtl), only for `gallery` demos)

Expand Down
52 changes: 35 additions & 17 deletions gallery/shapes_and_collections/patch_collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,50 @@

#include <pybind11/embed.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>

#include <matplotlibcpp17/pyplot.h>
#include <matplotlibcpp17/patches.h>

#include <algorithm>
#include <iostream>
#include <xtensor/xrandom.hpp>

#include <vector>

namespace py = pybind11;
using namespace py::literals;
using namespace std;
using namespace matplotlibcpp17;
using namespace matplotlibcpp17::patches;
using namespace matplotlibcpp17;

int main() {
py::scoped_interpreter guard{};
auto plt = matplotlibcpp17::pyplot::import();
auto [fig, ax] = plt.subplots();

const int resolution = 50;
const int N = 3;
vector<double> x = {0.7003673, 0.74275081, 0.70928001},
y = {0.56674552, 0.97778533, 0.70633485},
radii = {0.02479158, 0.01578834, 0.06976985};
xt::xarray<double> x = xt::random::rand<double>({N});
xt::xarray<double> y = xt::random::rand<double>({N});
xt::xarray<double> radii = 0.1 * xt::random::rand<double>({N});
py::list patches; // instead of patches = []
for (int i = 0; i < N; ++i) {
const double x1 = x[i], y1 = y[i], r = radii[i];
auto circle = patches::Circle(Args(py::make_tuple(x1, y1), r));
patches.append(circle.unwrap());
}
x = {0.71995667, 0.25774443, 0.34154678};
y = {0.96876117, 0.6945071, 0.46638326};
radii = {0.07028127, 0.05117859, 0.09287414};
vector<double> theta1 = {266.3169476, 224.07805212, 234.5563688},
theta2 = {142.85074015, 195.56618216, 287.96383014};

x = xt::random::rand<double>({N});
y = xt::random::rand<double>({N});
radii = 0.1 * xt::random::rand<double>({N});
xt::xarray<double> theta1 = 360.0 * xt::random::rand<double>({N});
xt::xarray<double> theta2 = 360.0 * xt::random::rand<double>({N});
for (int i = 0; i < N; ++i) {
const double x1 = x[i], y1 = y[i], r = radii[i], th1 = theta1[i],
th2 = theta2[i];
auto wedge = patches::Wedge(Args(py::make_tuple(x1, y1), r, th1, th2));
patches.append(wedge.unwrap());
}

patches.append(
patches::Wedge(Args(py::make_tuple(0.3, 0.7), 0.1, 0, 360)).unwrap());
patches.append(patches::Wedge(Args(py::make_tuple(0.7, 0.8), 0.2, 0, 360),
Expand All @@ -53,16 +57,30 @@ int main() {
patches.append(patches::Wedge(Args(py::make_tuple(0.8, 0.3), 0.2, 45, 90),
Kwargs("width"_a = 0.10))
.unwrap());
// NOTE: Polygon take numpy array as argument, so skip it
vector<double> colors_ = {90.63036451, 16.10182093, 74.36211347, 63.29741618,
32.41800177, 92.23765324, 23.72264387, 82.39455709,
75.06071403, 11.37844527};
py::list colors = py::cast(colors_);

for (int i = 0; i < N; ++i) {
auto poly__ = xt::random::rand<double>({N, 2});
vector<vector<double>> poly_(N);
// to vector<vector>>
for (int j = 0; j < N; ++j) {
poly_[j].resize(2);
poly_[j][0] = poly__(j, 0);
poly_[j][1] = poly__(j, 1);
}
// to numpy array
auto poly = py::array(py::cast(std::move(poly_)));
auto polygon = Polygon(Args(poly, true));
patches.append(polygon.unwrap());
}

auto colors__ = 100.0 * xt::random::rand<double>({patches.size()});
vector<double> colors_(colors__.begin(), colors__.end());
py::array colors = py::cast(colors_);
auto p = collections::PatchCollection(Args(patches), Kwargs("alpha"_a = 0.4));
p.set_array(Args(colors));
// NOTE: error in python3.6.9 ?
ax.add_collection(Args(p.unwrap()));
fig.colorbar(Args(p.unwrap()), Kwargs("ax"_a = ax.unwrap()));

#if USE_GUI
plt.show();
#else
Expand Down
5 changes: 4 additions & 1 deletion gallery/subplots_axes_and_figures/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
add_demo(align_labels_demo align_labels_demo.cpp)
add_demo(gridspec_multicolumn gridspec_multicolumn.cpp)
add_demo(multiple_figs_demo multiple_figs_demo.cpp)
add_demo(colorbar_placement colorbar_placement.cpp)
add_demo(subplots subplots.cpp)

add_custom_target(subplots_axes_and_figures
DEPENDS align_labels_demo gridspec_multicolumn multiple_figs_demo
DEPENDS align_labels_demo gridspec_multicolumn multiple_figs_demo colorbar_placement
COMMAND align_labels_demo
COMMAND gridspec_multicolumn
COMMAND multiple_figs_demo
COMMAND colorbar_placement
COMMENT "subplots_axes_and_figures"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
48 changes: 48 additions & 0 deletions gallery/subplots_axes_and_figures/colorbar_placement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// example from
// https://matplotlib.org/stable/gallery/subplots_axes_and_figures/colorbar_placement.html

#include <pybind11/embed.h>
#include <pybind11/stl.h>

#include <matplotlibcpp17/pyplot.h>

#include <xtensor/xrandom.hpp>

#include <vector>

namespace py = pybind11;
using namespace py::literals;
using namespace std;
using namespace matplotlibcpp17;

int main1() {
auto plt = matplotlibcpp17::pyplot::import();
auto [fig, axs] = plt.subplots(2, 2);
const vector<string> cmaps = {"RdBu_r", "viridis"};
for (auto col : {0, 1}) {
for (auto row : {0, 1}) {
auto x_ = xt::random::randn<double>({20, 20}) * (col + 1.0);
vector<vector<double>> x(20);
for (int i = 0; i < 20; ++i) {
x[i].resize(20);
for (int j = 0; j < 20; ++j)
x[i][j] = x_(i, j);
}
auto &ax = axs[col + row * 2];
auto pcm = ax.pcolormesh(Args(x), Kwargs("cmap"_a = cmaps[col]));
fig.colorbar(Args(pcm.unwrap()),
Kwargs("ax"_a = ax.unwrap(), "shrink"_a = 0.6));
}
}
#if USE_GUI
plt.show();
#else
plt.savefig(Args("colorbar_placement1.png"));
#endif
return 0;
}

int main() {
py::scoped_interpreter guard{};
main1();
}
27 changes: 27 additions & 0 deletions gallery/subplots_axes_and_figures/subplots.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <pybind11/embed.h>
#include <pybind11/stl.h>

#include <matplotlibcpp17/pyplot.h>

namespace py = pybind11;
using namespace py::literals;
using namespace std;
using namespace matplotlibcpp17;

int main() {
py::scoped_interpreter guard{};
auto plt = matplotlibcpp17::pyplot::import();
{
auto [fig, axs] = plt.subplots(3, 1);
std::cout << axs.size() << std::endl;
}
{
auto [fig, axs] = plt.subplots(1, 1);
std::cout << axs.size() << std::endl;
}
{
auto [fig, axs] = plt.subplots(3, 3);
std::cout << axs.size() << std::endl;
}
return 0;
}
14 changes: 14 additions & 0 deletions include/matplotlibcpp17/axes.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ struct DECL_STRUCT_ATTR Axes : public BaseWrapper {
legend::Legend legend(const pybind11::tuple &args = pybind11::tuple(),
const pybind11::dict &kwargs = pybind11::dict());

// pcolormesh
collections::QuadMesh
pcolormesh(const pybind11::tuple &args = pybind11::tuple(),
const pybind11::dict &kwargs = pybind11::dict());

// plot
pybind11::object plot(const pybind11::tuple &args = pybind11::tuple(),
const pybind11::dict &kwargs = pybind11::dict());
Expand Down Expand Up @@ -230,6 +235,7 @@ struct DECL_STRUCT_ATTR Axes : public BaseWrapper {
LOAD_FUNC_ATTR(hist2d, self);
LOAD_FUNC_ATTR(invert_yaxis, self);
LOAD_FUNC_ATTR(legend, self);
LOAD_FUNC_ATTR(pcolormesh, self);
LOAD_FUNC_ATTR(plot, self);
// NOTE: only when called with projection='3d', `plot_surface`, `plot_wireframe`, `set_zlabel` prop exists.
try {
Expand Down Expand Up @@ -278,6 +284,7 @@ struct DECL_STRUCT_ATTR Axes : public BaseWrapper {
pybind11::object hist2d_attr;
pybind11::object invert_yaxis_attr;
pybind11::object legend_attr;
pybind11::object pcolormesh_attr;
pybind11::object plot_attr;
pybind11::object plot_surface_attr;
pybind11::object plot_wireframe_attr;
Expand Down Expand Up @@ -478,6 +485,13 @@ legend::Legend Axes::legend(const pybind11::tuple &args,
return legend::Legend(obj);
}

// pcolormesh
collections::QuadMesh Axes::pcolormesh(const pybind11::tuple &args,
const pybind11::dict &kwargs) {
pybind11::object ret = pcolormesh_attr(*args, **kwargs);
return collections::QuadMesh(ret);
}

// plot
pybind11::object Axes::plot(const pybind11::tuple &args,
const pybind11::dict &kwargs) {
Expand Down
9 changes: 9 additions & 0 deletions include/matplotlibcpp17/collections.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ pybind11::object PatchCollection::set_array(const pybind11::tuple &args,
pybind11::object ret = set_array_attr(*args, **kwargs);
return ret;
}

/**
* @brief A wrapper class for matplotlib.collections.QuadMesh
**/
struct DECL_STRUCT_ATTR QuadMesh : public BaseWrapper {
public:
QuadMesh(pybind11::object quadmesh) { self = quadmesh; }
};

} // namespace matplotlibcpp17::collections

#endif /* MATPLOTLIBCPP17_COLLECTIONS_H */
16 changes: 16 additions & 0 deletions include/matplotlibcpp17/patches.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ struct DECL_STRUCT_ATTR Wedge : public BaseWrapper {
pybind11::object wedge_attr;
};

/**
* @brief A wrapper class for matplotlib.patches.Polygon
**/
struct DECL_STRUCT_ATTR Polygon : public BaseWrapper {
public:
Polygon(const pybind11::tuple &args = pybind11::tuple(),
const pybind11::dict &kwargs = pybind11::dict()) {
polygon_attr =
pybind11::module::import("matplotlib.patches").attr("Polygon");
self = polygon_attr(*args, **kwargs);
}

private:
pybind11::object polygon_attr;
};

} // namespace matplotlibcpp17::patches

#endif /* MATPLOTLIBCPP17_PATCHES_H */
17 changes: 14 additions & 3 deletions include/matplotlibcpp17/pyplot.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,18 +300,29 @@ PyPlot::subplots(const pybind11::dict &kwargs) {

std::tuple<figure::Figure, std::vector<axes::Axes>>
PyPlot::subplots(int r, int c, const pybind11::dict &kwargs) {
// subplots() returns [][] (if r > 1 && c > 1) else []
// return []axes in row-major
// NOTE: equal to Axes.flat
pybind11::tuple args = pybind11::make_tuple(r, c);
pybind11::list ret = subplots_attr(*args, **kwargs);
std::vector<axes::Axes> axes;
pybind11::object fig = ret[0];
figure::Figure figure(fig);
if (r == 1 and c == 1) {
pybind11::object ax = ret[1];
axes.push_back(axes::Axes(ax));
// python returns Axes
axes.push_back(axes::Axes(ret[1]));
} else if (r == 1 or c == 1) {
// python returns []Axes
pybind11::list axs = ret[1];
for (int i = 0; i < r * c; ++i)
axes.push_back(axes::Axes(axs[i]));
} else {
// python returns [][]Axes
pybind11::list axs = ret[1];
for (pybind11::size_t i = 0; i < axs.size(); ++i) {
axes.push_back(axes::Axes(axs[i]));
pybind11::list axsi = axs[i];
for (unsigned j = 0; j < axsi.size(); ++j)
axes.push_back(axes::Axes(axsi[j]));
}
}
return {figure, axes};
Expand Down