Skip to content

Commit d08fd8f

Browse files
Merge pull request matplotlib#29399 from scottshambaugh/plot_wireframe_speedup
plot_wireframe plotting speedup
2 parents 8d3c4db + 00062a6 commit d08fd8f

File tree

4 files changed

+84
-102
lines changed

4 files changed

+84
-102
lines changed

lib/matplotlib/_path.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@ from .transforms import BboxBase
66

77
def affine_transform(points: np.ndarray, trans: np.ndarray) -> np.ndarray: ...
88
def count_bboxes_overlapping_bbox(bbox: BboxBase, bboxes: Sequence[BboxBase]) -> int: ...
9-
def update_path_extents(path, trans, rect, minpos, ignore): ...

lib/matplotlib/transforms.py

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@
4646
from numpy.linalg import inv
4747

4848
from matplotlib import _api
49-
from matplotlib._path import (
50-
affine_transform, count_bboxes_overlapping_bbox, update_path_extents)
49+
from matplotlib._path import affine_transform, count_bboxes_overlapping_bbox
5150
from .path import Path
5251

5352
DEBUG = False
@@ -871,8 +870,8 @@ def update_from_path(self, path, ignore=None, updatex=True, updatey=True):
871870
if path.vertices.size == 0:
872871
return
873872

874-
points, minpos, changed = update_path_extents(
875-
path, None, self._points, self._minpos, ignore)
873+
points, minpos, changed = self._calc_extents_from_path(path, ignore,
874+
updatex, updatey)
876875

877876
if changed:
878877
self.invalidate()
@@ -883,6 +882,56 @@ def update_from_path(self, path, ignore=None, updatex=True, updatey=True):
883882
self._points[:, 1] = points[:, 1]
884883
self._minpos[1] = minpos[1]
885884

885+
def _calc_extents_from_path(self, path, ignore, updatex=True, updatey=True):
886+
"""
887+
Calculate the new bounds and minimum positive values for a `Bbox` from
888+
the path.
889+
890+
Parameters
891+
----------
892+
path : `~matplotlib.path.Path`
893+
ignore : bool
894+
- When ``True``, ignore the existing bounds of the `Bbox`.
895+
- When ``False``, include the existing bounds of the `Bbox`.
896+
updatex : bool
897+
When ``True``, update the x-values.
898+
updatey : bool
899+
When ``True``, update the y-values.
900+
901+
Returns
902+
-------
903+
points : (2, 2) array
904+
minpos : (2,) array
905+
changed : bool
906+
"""
907+
if ignore:
908+
points = np.array([[np.inf, np.inf], [-np.inf, -np.inf]])
909+
minpos = np.array([np.inf, np.inf])
910+
else:
911+
points = self._points.copy()
912+
minpos = self._minpos.copy()
913+
914+
if not (updatex or updatey):
915+
return points, minpos, False
916+
917+
valid_points = (np.isfinite(path.vertices[..., 0])
918+
& np.isfinite(path.vertices[..., 1]))
919+
920+
if updatex:
921+
x = path.vertices[..., 0][valid_points]
922+
points[0, 0] = min(points[0, 0], np.min(x, initial=np.inf))
923+
points[1, 0] = max(points[1, 0], np.max(x, initial=-np.inf))
924+
minpos[0] = min(minpos[0], np.min(x[x > 0], initial=np.inf))
925+
if updatey:
926+
y = path.vertices[..., 1][valid_points]
927+
points[0, 1] = min(points[0, 1], np.min(y, initial=np.inf))
928+
points[1, 1] = max(points[1, 1], np.max(y, initial=-np.inf))
929+
minpos[1] = min(minpos[1], np.min(y[y > 0], initial=np.inf))
930+
931+
changed = np.any(points != self._points) or np.any(minpos != self._minpos)
932+
933+
return points, minpos, changed
934+
886935
def update_from_data_x(self, x, ignore=None):
887936
"""
888937
Update the x-bounds of the `Bbox` based on the passed in data. After
@@ -899,8 +948,9 @@ def update_from_data_x(self, x, ignore=None):
899948
- When ``None``, use the last value passed to :meth:`ignore`.
900949
"""
901950
x = np.ravel(x)
902-
self.update_from_data_xy(np.column_stack([x, np.ones(x.size)]),
903-
ignore=ignore, updatey=False)
951+
# The y-component in np.array([x, *y*]).T is not used. We simply pass
952+
# x again to not spend extra time on creating an array of unused data
953+
self.update_from_data_xy(np.array([x, x]).T, ignore=ignore, updatey=False)
904954

905955
def update_from_data_y(self, y, ignore=None):
906956
"""
@@ -918,8 +968,9 @@ def update_from_data_y(self, y, ignore=None):
918968
- When ``None``, use the last value passed to :meth:`ignore`.
919969
"""
920970
y = np.ravel(y)
921-
self.update_from_data_xy(np.column_stack([np.ones(y.size), y]),
922-
ignore=ignore, updatex=False)
971+
# The x-component in np.array([*x*, y]).T is not used. We simply pass
972+
# y again to not spend extra time on creating an array of unused data
973+
self.update_from_data_xy(np.array([y, y]).T, ignore=ignore, updatex=False)
923974

924975
def update_from_data_xy(self, xy, ignore=None, updatex=True, updatey=True):
925976
"""

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,47 +2413,39 @@ def plot_wireframe(self, X, Y, Z, *, axlim_clip=False, **kwargs):
24132413
rstride = int(max(np.ceil(rows / rcount), 1)) if rcount else 0
24142414
cstride = int(max(np.ceil(cols / ccount), 1)) if ccount else 0
24152415

2416+
if rstride == 0 and cstride == 0:
2417+
raise ValueError("Either rstride or cstride must be non zero")
2418+
24162419
# We want two sets of lines, one running along the "rows" of
24172420
# Z and another set of lines running along the "columns" of Z.
24182421
# This transpose will make it easy to obtain the columns.
24192422
tX, tY, tZ = np.transpose(X), np.transpose(Y), np.transpose(Z)
24202423

2421-
if rstride:
2422-
rii = list(range(0, rows, rstride))
2423-
# Add the last index only if needed
2424-
if rows > 0 and rii[-1] != (rows - 1):
2425-
rii += [rows-1]
2424+
# Compute the indices of the row and column lines to be drawn
2425+
# For Z.size == 0, we don't want to draw any lines since the data is empty
2426+
if rstride == 0 or Z.size == 0:
2427+
rii = np.array([], dtype=int)
2428+
elif (rows - 1) % rstride == 0:
2429+
# last index is hit: rii[-1] == rows - 1
2430+
rii = np.arange(0, rows, rstride)
24262431
else:
2427-
rii = []
2428-
if cstride:
2429-
cii = list(range(0, cols, cstride))
2430-
# Add the last index only if needed
2431-
if cols > 0 and cii[-1] != (cols - 1):
2432-
cii += [cols-1]
2432+
# add the last index
2433+
rii = np.arange(0, rows + rstride, rstride)
2434+
rii[-1] = rows - 1
2435+
2436+
if cstride == 0 or Z.size == 0:
2437+
cii = np.array([], dtype=int)
2438+
elif (cols - 1) % cstride == 0:
2439+
# last index is hit: cii[-1] == cols - 1
2440+
cii = np.arange(0, cols, cstride)
24332441
else:
2434-
cii = []
2435-
2436-
if rstride == 0 and cstride == 0:
2437-
raise ValueError("Either rstride or cstride must be non zero")
2438-
2439-
# If the inputs were empty, then just
2440-
# reset everything.
2441-
if Z.size == 0:
2442-
rii = []
2443-
cii = []
2444-
2445-
xlines = [X[i] for i in rii]
2446-
ylines = [Y[i] for i in rii]
2447-
zlines = [Z[i] for i in rii]
2448-
2449-
txlines = [tX[i] for i in cii]
2450-
tylines = [tY[i] for i in cii]
2451-
tzlines = [tZ[i] for i in cii]
2442+
# add the last index
2443+
cii = np.arange(0, cols + cstride, cstride)
2444+
cii[-1] = cols - 1
24522445

2453-
lines = ([list(zip(xl, yl, zl))
2454-
for xl, yl, zl in zip(xlines, ylines, zlines)]
2455-
+ [list(zip(xl, yl, zl))
2456-
for xl, yl, zl in zip(txlines, tylines, tzlines)])
2446+
row_lines = np.stack([X[rii], Y[rii], Z[rii]], axis=-1)
2447+
col_lines = np.stack([tX[cii], tY[cii], tZ[cii]], axis=-1)
2448+
lines = np.concatenate([row_lines, col_lines])
24572449

24582450
linec = art3d.Line3DCollection(lines, axlim_clip=axlim_clip, **kwargs)
24592451
self.add_collection(linec)

src/_path_wrapper.cpp

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -52,64 +52,6 @@ Py_points_in_path(py::array_t<double> points_obj, double r, mpl::PathIterator pa
5252
return results;
5353
}
5454

55-
static py::tuple
56-
Py_update_path_extents(mpl::PathIterator path, agg::trans_affine trans,
57-
agg::rect_d rect, py::array_t<double> minpos, bool ignore)
58-
{
59-
bool changed;
60-
61-
if (minpos.ndim() != 1) {
62-
throw py::value_error(
63-
"minpos must be 1D, got " + std::to_string(minpos.ndim()));
64-
}
65-
if (minpos.shape(0) != 2) {
66-
throw py::value_error(
67-
"minpos must be of length 2, got " + std::to_string(minpos.shape(0)));
68-
}
69-
70-
extent_limits e;
71-
72-
if (ignore) {
73-
reset_limits(e);
74-
} else {
75-
if (rect.x1 > rect.x2) {
76-
e.x0 = std::numeric_limits<double>::infinity();
77-
e.x1 = -std::numeric_limits<double>::infinity();
78-
} else {
79-
e.x0 = rect.x1;
80-
e.x1 = rect.x2;
81-
}
82-
if (rect.y1 > rect.y2) {
83-
e.y0 = std::numeric_limits<double>::infinity();
84-
e.y1 = -std::numeric_limits<double>::infinity();
85-
} else {
86-
e.y0 = rect.y1;
87-
e.y1 = rect.y2;
88-
}
89-
e.xm = *minpos.data(0);
90-
e.ym = *minpos.data(1);
91-
}
92-
93-
update_path_extents(path, trans, e);
94-
95-
changed = (e.x0 != rect.x1 || e.y0 != rect.y1 || e.x1 != rect.x2 || e.y1 != rect.y2 ||
96-
e.xm != *minpos.data(0) || e.ym != *minpos.data(1));
97-
98-
py::ssize_t extentsdims[] = { 2, 2 };
99-
py::array_t<double> outextents(extentsdims);
100-
*outextents.mutable_data(0, 0) = e.x0;
101-
*outextents.mutable_data(0, 1) = e.y0;
102-
*outextents.mutable_data(1, 0) = e.x1;
103-
*outextents.mutable_data(1, 1) = e.y1;
104-
105-
py::ssize_t minposdims[] = { 2 };
106-
py::array_t<double> outminpos(minposdims);
107-
*outminpos.mutable_data(0) = e.xm;
108-
*outminpos.mutable_data(1) = e.ym;
109-
110-
return py::make_tuple(outextents, outminpos, changed);
111-
}
112-
11355
static py::tuple
11456
Py_get_path_collection_extents(agg::trans_affine master_transform,
11557
mpl::PathGenerator paths,
@@ -374,8 +316,6 @@ PYBIND11_MODULE(_path, m, py::mod_gil_not_used())
374316
"x"_a, "y"_a, "radius"_a, "path"_a, "trans"_a);
375317
m.def("points_in_path", &Py_points_in_path,
376318
"points"_a, "radius"_a, "path"_a, "trans"_a);
377-
m.def("update_path_extents", &Py_update_path_extents,
378-
"path"_a, "trans"_a, "rect"_a, "minpos"_a, "ignore"_a);
379319
m.def("get_path_collection_extents", &Py_get_path_collection_extents,
380320
"master_transform"_a, "paths"_a, "transforms"_a, "offsets"_a,
381321
"offset_transform"_a);

0 commit comments

Comments
 (0)