Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b930aa7

Browse files
committed
Fixes matplotlib#1246. Repeated drawing handling fixed by correcting the affine/non-affine transformation of a BlendedGenericTransform.
1 parent 652e385 commit b930aa7

File tree

6 files changed

+66
-55
lines changed

6 files changed

+66
-55
lines changed

lib/matplotlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,7 @@ def tk_window_focus():
10751075
'matplotlib.tests.test_patches',
10761076
'matplotlib.tests.test_pickle',
10771077
'matplotlib.tests.test_rcparams',
1078+
'matplotlib.tests.test_scale',
10781079
'matplotlib.tests.test_simplification',
10791080
'matplotlib.tests.test_spines',
10801081
'matplotlib.tests.test_text',

lib/matplotlib/scale.py

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
from __future__ import print_function
2-
import textwrap
2+
33
import numpy as np
44
from numpy import ma
5-
MaskedArray = ma.MaskedArray
65

7-
from cbook import dedent
8-
from ticker import NullFormatter, ScalarFormatter, LogFormatterMathtext, Formatter
9-
from ticker import NullLocator, LogLocator, AutoLocator, SymmetricalLogLocator, FixedLocator
10-
from ticker import is_decade
11-
from transforms import Transform, IdentityTransform
6+
from matplotlib.cbook import dedent
7+
from matplotlib.ticker import (NullFormatter, ScalarFormatter,
8+
LogFormatterMathtext)
9+
from matplotlib.ticker import (NullLocator, LogLocator, AutoLocator,
10+
SymmetricalLogLocator)
11+
from matplotlib.transforms import Transform, IdentityTransform
1212
from matplotlib import docstring
1313

14+
1415
class ScaleBase(object):
1516
"""
1617
The base class for all scales.
@@ -31,15 +32,15 @@ def get_transform(self):
3132
Return the :class:`~matplotlib.transforms.Transform` object
3233
associated with this scale.
3334
"""
34-
raise NotImplementedError
35+
raise NotImplementedError()
3536

3637
def set_default_locators_and_formatters(self, axis):
3738
"""
3839
Set the :class:`~matplotlib.ticker.Locator` and
3940
:class:`~matplotlib.ticker.Formatter` objects on the given
4041
axis to match this scale.
4142
"""
42-
raise NotImplementedError
43+
raise NotImplementedError()
4344

4445
def limit_range_for_scale(self, vmin, vmax, minpos):
4546
"""
@@ -51,6 +52,7 @@ def limit_range_for_scale(self, vmin, vmax, minpos):
5152
"""
5253
return vmin, vmax
5354

55+
5456
class LinearScale(ScaleBase):
5557
"""
5658
The default linear scale.
@@ -90,10 +92,12 @@ def _mask_non_positives(a):
9092
return ma.MaskedArray(a, mask=mask)
9193
return a
9294

95+
9396
def _clip_non_positives(a):
9497
a[a <= 0.0] = 1e-300
9598
return a
9699

100+
97101
class LogScale(ScaleBase):
98102
"""
99103
A standard logarithmic scale. Care is taken so non-positive
@@ -116,21 +120,20 @@ class LogTransformBase(Transform):
116120
output_dims = 1
117121
is_separable = True
118122
has_inverse = True
119-
123+
120124
def __init__(self, nonpos):
121125
Transform.__init__(self)
122126
if nonpos == 'mask':
123127
self._handle_nonpos = _mask_non_positives
124128
else:
125129
self._handle_nonpos = _clip_non_positives
126130

127-
128131
class Log10Transform(LogTransformBase):
129132
base = 10.0
130133

131-
def transform(self, a):
134+
def transform_non_affine(self, a):
132135
a = self._handle_nonpos(a * 10.0)
133-
if isinstance(a, MaskedArray):
136+
if isinstance(a, ma.MaskedArray):
134137
return ma.log10(a)
135138
return np.log10(a)
136139

@@ -141,9 +144,10 @@ class InvertedLog10Transform(Transform):
141144
input_dims = 1
142145
output_dims = 1
143146
is_separable = True
147+
has_inverse = True
144148
base = 10.0
145149

146-
def transform(self, a):
150+
def transform_non_affine(self, a):
147151
return ma.power(10.0, a) / 10.0
148152

149153
def inverted(self):
@@ -152,9 +156,9 @@ def inverted(self):
152156
class Log2Transform(LogTransformBase):
153157
base = 2.0
154158

155-
def transform(self, a):
159+
def transform_non_affine(self, a):
156160
a = self._handle_nonpos(a * 2.0)
157-
if isinstance(a, MaskedArray):
161+
if isinstance(a, ma.MaskedArray):
158162
return ma.log(a) / np.log(2)
159163
return np.log2(a)
160164

@@ -165,9 +169,10 @@ class InvertedLog2Transform(Transform):
165169
input_dims = 1
166170
output_dims = 1
167171
is_separable = True
172+
has_inverse = True
168173
base = 2.0
169174

170-
def transform(self, a):
175+
def transform_non_affine(self, a):
171176
return ma.power(2.0, a) / 2.0
172177

173178
def inverted(self):
@@ -176,9 +181,9 @@ def inverted(self):
176181
class NaturalLogTransform(LogTransformBase):
177182
base = np.e
178183

179-
def transform(self, a):
184+
def transform_non_affine(self, a):
180185
a = self._handle_nonpos(a * np.e)
181-
if isinstance(a, MaskedArray):
186+
if isinstance(a, ma.MaskedArray):
182187
return ma.log(a)
183188
return np.log(a)
184189

@@ -189,9 +194,10 @@ class InvertedNaturalLogTransform(Transform):
189194
input_dims = 1
190195
output_dims = 1
191196
is_separable = True
197+
has_inverse = True
192198
base = np.e
193199

194-
def transform(self, a):
200+
def transform_non_affine(self, a):
195201
return ma.power(np.e, a) / np.e
196202

197203
def inverted(self):
@@ -201,7 +207,8 @@ class LogTransform(Transform):
201207
input_dims = 1
202208
output_dims = 1
203209
is_separable = True
204-
210+
has_inverse = True
211+
205212
def __init__(self, base, nonpos):
206213
Transform.__init__(self)
207214
self.base = base
@@ -210,9 +217,9 @@ def __init__(self, base, nonpos):
210217
else:
211218
self._handle_nonpos = _clip_non_positives
212219

213-
def transform(self, a):
220+
def transform_non_affine(self, a):
214221
a = self._handle_nonpos(a * self.base)
215-
if isinstance(a, MaskedArray):
222+
if isinstance(a, ma.MaskedArray):
216223
return ma.log(a) / np.log(self.base)
217224
return np.log(a) / np.log(self.base)
218225

@@ -223,18 +230,18 @@ class InvertedLogTransform(Transform):
223230
input_dims = 1
224231
output_dims = 1
225232
is_separable = True
226-
233+
has_inverse = True
234+
227235
def __init__(self, base):
228236
Transform.__init__(self)
229237
self.base = base
230238

231-
def transform(self, a):
239+
def transform_non_affine(self, a):
232240
return ma.power(self.base, a) / self.base
233241

234242
def inverted(self):
235243
return LogScale.LogTransform(self.base)
236-
237-
244+
238245
def __init__(self, axis, **kwargs):
239246
"""
240247
*basex*/*basey*:
@@ -318,7 +325,7 @@ class SymmetricalLogTransform(Transform):
318325
output_dims = 1
319326
is_separable = True
320327
has_inverse = True
321-
328+
322329
def __init__(self, base, linthresh, linscale):
323330
Transform.__init__(self)
324331
self.base = base
@@ -327,7 +334,7 @@ def __init__(self, base, linthresh, linscale):
327334
self._linscale_adj = (linscale / (1.0 - self.base ** -1))
328335
self._log_base = np.log(base)
329336

330-
def transform(self, a):
337+
def transform_non_affine(self, a):
331338
sign = np.sign(a)
332339
masked = ma.masked_inside(a, -self.linthresh, self.linthresh, copy=False)
333340
log = sign * self.linthresh * (
@@ -346,7 +353,8 @@ class InvertedSymmetricalLogTransform(Transform):
346353
input_dims = 1
347354
output_dims = 1
348355
is_separable = True
349-
356+
has_inverse = True
357+
350358
def __init__(self, base, linthresh, linscale):
351359
Transform.__init__(self)
352360
symlog = SymmetricalLogScale.SymmetricalLogTransform(base, linthresh, linscale)
@@ -356,7 +364,7 @@ def __init__(self, base, linthresh, linscale):
356364
self.linscale = linscale
357365
self._linscale_adj = (linscale / (1.0 - self.base ** -1))
358366

359-
def transform(self, a):
367+
def transform_non_affine(self, a):
360368
sign = np.sign(a)
361369
masked = ma.masked_inside(a, -self.invlinthresh, self.invlinthresh, copy=False)
362370
exp = sign * self.linthresh * (
@@ -436,17 +444,19 @@ def get_transform(self):
436444
return self._transform
437445

438446

439-
440447
_scale_mapping = {
441448
'linear' : LinearScale,
442449
'log' : LogScale,
443450
'symlog' : SymmetricalLogScale
444451
}
452+
453+
445454
def get_scale_names():
446455
names = _scale_mapping.keys()
447456
names.sort()
448457
return names
449458

459+
450460
def scale_factory(scale, axis, **kwargs):
451461
"""
452462
Return a scale class by name.
@@ -464,6 +474,7 @@ def scale_factory(scale, axis, **kwargs):
464474
scale_factory.__doc__ = dedent(scale_factory.__doc__) % \
465475
{'names': " | ".join(get_scale_names())}
466476

477+
467478
def register_scale(scale_class):
468479
"""
469480
Register a new kind of scale.
@@ -472,6 +483,7 @@ def register_scale(scale_class):
472483
"""
473484
_scale_mapping[scale_class.name] = scale_class
474485

486+
475487
def get_scale_docs():
476488
"""
477489
Helper function for generating docstrings related to scales.
@@ -488,6 +500,7 @@ def get_scale_docs():
488500
docs.append("")
489501
return "\n".join(docs)
490502

503+
491504
docstring.interpd.update(
492505
scale = ' | '.join([repr(x) for x in get_scale_names()]),
493506
scale_docs = get_scale_docs().strip(),
Binary file not shown.

lib/matplotlib/tests/baseline_images/test_scale/log_scales.svg

Lines changed: 4 additions & 3 deletions
Loading

lib/matplotlib/tests/test_scale.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55

66

77
@image_comparison(baseline_images=['log_scales'])
8-
def test_complete():
8+
def test_log_scales():
99
ax = plt.subplot(122, yscale='log', xscale='symlog')
1010

1111
ax.axvline(24.1)
12-
ax.axhline(24.1)
13-
plt.show()
12+
ax.axhline(24.1)

lib/matplotlib/transforms.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,18 +1943,18 @@ def transform_non_affine(self, points):
19431943
y = self._y
19441944

19451945
if x == y and x.input_dims == 2:
1946-
return x.transform(points)
1946+
return x.transform_non_affine(points)
19471947

19481948
if x.input_dims == 2:
1949-
x_points = x.transform(points)[:, 0:1]
1949+
x_points = x.transform_non_affine(points)[:, 0:1]
19501950
else:
1951-
x_points = x.transform(points[:, 0])
1951+
x_points = x.transform_non_affine(points[:, 0])
19521952
x_points = x_points.reshape((len(x_points), 1))
19531953

19541954
if y.input_dims == 2:
1955-
y_points = y.transform(points)[:, 1:]
1955+
y_points = y.transform_non_affine(points)[:, 1:]
19561956
else:
1957-
y_points = y.transform(points[:, 1])
1957+
y_points = y.transform_non_affine(points[:, 1])
19581958
y_points = y_points.reshape((len(y_points), 1))
19591959

19601960
if isinstance(x_points, MaskedArray) or isinstance(y_points, MaskedArray):
@@ -1969,19 +1969,16 @@ def inverted(self):
19691969

19701970
def get_affine(self):
19711971
if self._invalid or self._affine is None:
1972-
if self._x.is_affine and self._y.is_affine:
1973-
if self._x == self._y:
1974-
self._affine = self._x.get_affine()
1975-
else:
1976-
x_mtx = self._x.get_affine().get_matrix()
1977-
y_mtx = self._y.get_affine().get_matrix()
1978-
# This works because we already know the transforms are
1979-
# separable, though normally one would want to set b and
1980-
# c to zero.
1981-
mtx = np.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
1982-
self._affine = Affine2D(mtx)
1972+
if self._x == self._y:
1973+
self._affine = self._x.get_affine()
19831974
else:
1984-
self._affine = IdentityTransform()
1975+
x_mtx = self._x.get_affine().get_matrix()
1976+
y_mtx = self._y.get_affine().get_matrix()
1977+
# This works because we already know the transforms are
1978+
# separable, though normally one would want to set b and
1979+
# c to zero.
1980+
mtx = np.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
1981+
self._affine = Affine2D(mtx)
19851982
self._invalid = 0
19861983
return self._affine
19871984
get_affine.__doc__ = Transform.get_affine.__doc__

0 commit comments

Comments
 (0)