8000 Merge pull request #17165 from Tillsten/optimize_transform · matplotlib/matplotlib@99ba9f9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 99ba9f9

Browse files
authored
Merge pull request #17165 from Tillsten/optimize_transform
Small optimizations to scale and translate of Affine2D
2 parents 6cd24b9 + 740ffbf commit 99ba9f9

File tree

3 files changed

+41
-32
lines changed

3 files changed

+41
-32
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343

4444
import matplotlib as mpl
4545
from matplotlib import (
46-
backend_tools as tools, cbook, colors, textpath, tight_bbox, transforms,
47-
widgets, get_backend, is_interactive, rcParams)
46+
backend_tools as tools, cbook, colors, textpath, tight_bbox,
47+
transforms, widgets, get_backend, is_interactive, rcParams)
4848
from matplotlib._pylab_helpers import Gcf
4949
from matplotlib.backend_managers import ToolManager
5050
from matplotlib.transforms import Affine2D
@@ -222,18 +222,22 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
222222
recommended to use those generators, so that changes to the
223223
behavior of :meth:`draw_path_collection` can be made globally.
224224
"""
225-
path_ids = [
226-
(path, transforms.Affine2D(transform))
227-
for path, transform in self._iter_collection_raw_paths(
228-
master_transform, paths, all_transforms)]
225+
path_ids = self._iter_collection_raw_paths(master_transform,
226+
paths, all_transforms)
229227

230228
for xo, yo, path_id, gc0, rgbFace in self._iter_collection(
231-
gc, master_transform, all_transforms, path_ids, offsets,
229+
gc, master_transform, all_transforms, list(path_ids), offsets,
232230
offsetTrans, facecolors, edgecolors, linewidths, linestyles,
233231
antialiaseds, urls, offset_position):
234232
path, transform = path_id
235-
transform = transforms.Affine2D(
236-
transform.get_matrix()).translate(xo, yo)
233+
# Only apply another translation if we have an offset, else we
234+
# resuse the inital transform.
235+
if xo != 0 or yo != 0:
236+
# The transformation can be used by multiple paths. Since
237+
# translate is a inplace operation, we need to copy the
238+
# transformation by .frozen() before applying the translation.
239+
transform = transform.frozen()
240+
transform.translate(xo, yo 8000 )
237241
self.draw_path(gc0, path, transform, rgbFace)
238242

239243
def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight,

lib/matplotlib/tests/test_backend_bases.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
def test_uses_per_path():
1515
id = transforms.Affine2D()
1616
paths = [path.Path.unit_regular_polygon(i) for i in range(3, 7)]
17-
tforms = [id.rotate(i) for i in range(1, 5)]
17+
tforms_matrices = [id.rotate(i).get_matrix().copy() for i in range(1, 5)]
1818
offsets = np.arange(20).reshape((10, 2))
1919
facecolors = ['red', 'green']
2020
edgecolors = ['red', 'green']
@@ -38,17 +38,18 @@ def check(master_transform, paths, all_transforms,
3838
seen = np.bincount(ids, minlength=len(raw_paths))
3939
assert set(seen).issubset([uses - 1, uses])
4040

41-
check(id, paths, tforms, offsets, facecolors, edgecolors)
42-
check(id, paths[0:1], tforms, offsets, facecolors, edgecolors)
43-
check(id, [], tforms, offsets, facecolors, edgecolors)
44-
check(id, paths, tforms[0:1], offsets, facecolors, edgecolors)
41+
check(id, paths, tforms_matrices, offsets, facecolors, edgecolors)
42+
check(id, paths[0:1], tforms_matrices, offsets, facecolors, edgecolors)
43+
check(id, [], tforms_matrices, offsets, facecolors, edgecolors)
44+
check(id, paths, tforms_matrices[0:1], offsets, facecolors, edgecolors)
4545
check(id, paths, [], offsets, facecolors, edgecolors)
4646
for n in range(0, offsets.shape[0]):
47-
check(id, paths, tforms, offsets[0:n, :], facecolors, edgecolors)
48-
check(id, paths, tforms, offsets, [], edgecolors)
49-
check(id, paths, tforms, offsets, facecolors, [])
50-
check(id, paths, tforms, offsets, [], [])
51-
check(id, paths, tforms, offsets, facecolors[0:1], edgecolors)
47+
check(id, paths, tforms_matrices, offsets[0:n, :],
48+
facecolors, edgecolors)
49+
check(id, paths, tforms_matrices, offsets, [], edgecolors)
50+
check(id, paths, tforms_matrices, offsets, facecolors, [])
51+
check(id, paths, tforms_matrices, offsets, [], [])
52+
check(id, paths, tforms_matrices, offsets, facecolors[0:1], edgecolors)
5253

5354

5455
def test_get_default_filename(tmpdir):

lib/matplotlib/transforms.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import functools
3737
import textwrap
3838
import weakref
39+
import math
3940

4041
import numpy as np
4142
from numpy.linalg import inv
@@ -1842,7 +1843,7 @@ def __init__(self, matrix=None, **kwargs):
18421843
if matrix is None:
18431844
# A bit faster than np.identity(3).
18441845
matrix = IdentityTransform._mtx.copy()
1845-
self._mtx = matrix
1846+
self._mtx = matrix.copy()
18461847
self._invalid = 0
18471848

18481849
__str__ = _make_str_method("_mtx")
@@ -1925,8 +1926,8 @@ def rotate(self, theta):
19251926
calls to :meth:`rotate`, :meth:`rotate_deg`, :meth:`translate`
19261927
and :meth:`scale`.
19271928
"""
1928-
a = np.cos(theta)
1929-
b = np.sin(theta)
1929+
a = math.cos(theta)
1930+
b = math.sin(theta)
19301931
rotate_mtx = np.array([[a, -b, 0.0], [b, a, 0.0], [0.0, 0.0, 1.0]],
19311932
float)
19321933
self._mtx = np.dot(rotate_mtx, self._mtx)
@@ -1941,7 +1942,7 @@ def rotate_deg(self, degrees):
19411942
calls to :meth:`rotate`, :meth:`rotate_deg`, :meth:`translate`
19421943
and :meth:`scale`.
19431944
"""
1944-
return self.rotate(np.deg2rad(degrees))
1945+
return self.rotate(math.radians(degrees))
19451946

19461947
def rotate_around(self, x, y, theta):
19471948
"""
F438
@@ -1973,9 +1974,8 @@ def translate(self, tx, ty):
19731974
calls to :meth:`rotate`, :meth:`rotate_deg`, :meth:`translate`
19741975
and :meth:`scale`.
19751976
"""
1976-
translate_mtx = np.array(
1977-
[[1.0, 0.0, tx], [0.0, 1.0, ty], [0.0, 0.0, 1.0]], float)
1978-
self._mtx = np.dot(translate_mtx, self._mtx)
1977+
self._mtx[0, 2] += tx
1978+
self._mtx[1, 2] += ty
19791979
self.invalidate()
19801980
return self
19811981

@@ -1992,9 +1992,13 @@ def scale(self, sx, sy=None):
19921992
"""
19931993
if sy is None:
19941994
sy = sx
1995-
scale_mtx = np.array(
1996-
[[sx, 0.0, 0.0], [0.0, sy, 0.0], [0.0, 0.0, 1.0]], float)
1997-
self._mtx = np.dot(scale_mtx, self._mtx)
1995+
# explicit element-wise scaling is fastest
1996+
self._mtx[0, 0] *= sx
1997+
self._mtx[0, 1] *= sx
1998+
self._mtx[0, 2] *= sx
1999+
self._mtx[1, 0] *= sy
2000+
self._mtx[1, 1] *= sy
2001+
self._mtx[1, 2] *= sy
19982002
self.invalidate()
19992003
return self
20002004

@@ -2009,8 +2013,8 @@ def skew(self, xShear, yShear):
20092013
calls to :meth:`rotate`, :meth:`rotate_deg`, :meth:`translate`
20102014
and :meth:`scale`.
20112015
"""
2012-
rotX = np.tan(xShear)
2013-
rotY = np.tan(yShear)
2016+
rotX = math.tan(xShear)
2017+
rotY = math.tan(yShear)
20142018
skew_mtx = np.array(
20152019
[[1.0, rotX, 0.0], [rotY, 1.0, 0.0], [0.0, 0.0, 1.0]], float)
20162020
self._mtx = np.dot(skew_mtx, self._mtx)
@@ -2028,7 +2032,7 @@ def skew_deg(self, xShear, yShear):
20282032
calls to :meth:`rotate`, :meth:`rotate_deg`, :meth:`translate`
20292033
and :meth:`scale`.
20302034
"""
2031-
return self.skew(np.deg2rad(xShear), np.deg2rad(yShear))
2035+
return self.skew(math.radians(xShear), math.radians(yShear))
20322036

20332037

20342038
class IdentityTransform(Affine2DBase):

0 commit comments

Comments
 (0)
0