| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | import six | |
| 5 | 5 | ||
| 6 | 6 | from collections import OrderedDict, namedtuple | |
| 7 | + import contextlib | ||
| 7 | 8 | from functools import wraps | |
| 8 | 9 | import inspect | |
| 9 | 10 | import re | |
@@ -120,6 +121,22 @@ def __init__(self): | |||
| 120 | 121 | self._path_effects = rcParams['path.effects'] | |
| 121 | 122 | self._sticky_edges = _XYPair([], []) | |
| 122 | 123 | ||
| 124 | + # When plotting in log-scale, force the use of clip mode instead of | ||
| 125 | + # mask. The typical (internal) use case is log-scaled bar plots and | ||
| 126 | + # error bars. Ideally we'd want BarContainers / ErrorbarContainers | ||
| 127 | + # to have their own show() method which takes care of the patching, | ||
| 128 | + # but right now Containers are not taken into account during | ||
| 129 | + # the draw; instead their components (in the case of bar plots, | ||
| 130 | + # these are Rectangle patches; in the case of ErrorbarContainers, | ||
| 131 | + # LineCollections) are drawn individually, so tracking the force_clip | ||
| 132 | + # state must be done by the component artists. Note that handling of | ||
| 133 | + # _force_clip_in_log_scale must be done by the individual artists' | ||
| 134 | + # draw implementation; right now only Patches and Collections support | ||
| 135 | + # it. The `_forcing_clip_in_log_scale` decorator may be helpful to | ||
| 136 | + # implement such support, it should typically be applied around a call | ||
| 137 | + # to `transform_path_non_affine`. | ||
| 138 | + self._force_clip_in_log_scale = False | ||
| 139 | + | ||
| 123 | 140 | def __getstate__(self): | |
| 124 | 141 | d = self.__dict__.copy() | |
| 125 | 142 | # remove the unpicklable remove method, this will get re-added on load | |
@@ -779,6 +796,21 @@ def draw(self, renderer, *args, **kwargs): | |||
| 779 | 796 | return | |
| 780 | 797 | self.stale = False | |
| 781 | 798 | ||
| 799 | + @contextlib.contextmanager | ||
| 800 | + def _forcing_clip_in_log_scale(self): | ||
| 801 | + # See _force_clip_in_log_scale for explanation. | ||
| 802 | + fvs = {} | ||
| 803 | + if self._force_clip_in_log_scale and self.axes: | ||
| 804 | + for axis in self.axes._get_axis_list(): | ||
| 805 | + if axis.get_scale() == "log": | ||
| 806 | + fvs[axis] = axis._scale._transform._fill_value | ||
| 807 | + axis._scale._transform._fill_value = 1e-300 | ||
| 808 | + try: | ||
| 809 | + yield | ||
| 810 | + finally: | ||
| 811 | + for axis, fv in fvs.items(): | ||
| 812 | + axis._scale._transform._fill_value = fv | ||
| 813 | + | ||
| 782 | 814 | def set_alpha(self, alpha): | |
| 783 | 815 | """ | |
| 784 | 816 | Set the alpha value used for blending - not supported on | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2158,6 +2158,7 @@ def bar(self, *args, **kwargs): | |||
| 2158 | 2158 | r.sticky_edges.y.append(b) | |
| 2159 | 2159 | elif orientation == 'horizontal': | |
| 2160 | 2160 | r.sticky_edges.x.append(l) | |
| 2161 | + r._force_clip_in_log_scale = True | ||
| 2161 | 2162 | self.add_patch(r) | |
| 2162 | 2163 | patches.append(r) | |
| 2163 | 2164 | ||
@@ -3054,7 +3055,9 @@ def extract_err(err, data): | |||
| 3054 | 3055 | if xlolims.any(): | |
| 3055 | 3056 | yo, _ = xywhere(y, right, xlolims & everymask) | |
| 3056 | 3057 | lo, ro = xywhere(x, right, xlolims & everymask) | |
| 3057 | - barcols.append(self.hlines(yo, lo, ro, **eb_lines_style)) | ||
| 3058 | + ebs = self.hlines(yo, lo, ro, **eb_lines_style) | ||
| 3059 | + ebs._force_clip_in_log_scale = True | ||
| 3060 | + barcols.append(ebs) | ||
| 3058 | 3061 | rightup, yup = xywhere(right, y, xlolims & everymask) | |
| 3059 | 3062 | if self.xaxis_inverted(): | |
| 3060 | 3063 | marker = mlines.CARETLEFTBASE | |
@@ -3093,7 +3096,9 @@ def extract_err(err, data): | |||
| 3093 | 3096 | if noylims.any(): | |
| 3094 | 3097 | xo, _ = xywhere(x, lower, noylims & everymask) | |
| 3095 | 3098 | lo, uo = xywhere(lower, upper, noylims & everymask) | |
| 3096 | - barcols.append(self.vlines(xo, lo, uo, **eb_lines_style)) | ||
| 3099 | + ebs = self.vlines(xo, lo, uo, **eb_lines_style) | ||
| 3100 | + ebs._force_clip_in_log_scale = True | ||
| 3101 | + barcols.append(ebs) | ||
| 3097 | 3102 | if capsize > 0: | |
| 3098 | 3103 | caplines.append(mlines.Line2D(xo, lo, marker='_', | |
| 3099 | 3104 | **eb_cap_style)) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -230,8 +230,9 @@ def _prepare_points(self): | |||
| 230 | 230 | offsets = np.column_stack([xs, ys]) | |
| 231 | 231 | ||
| 232 | 232 | if not transform.is_affine: | |
| 233 | - paths = [transform.transform_path_non_affine(path) | ||
| 234 | - for path in paths] | ||
| 233 | + with self._forcing_clip_in_log_scale(): | ||
| 234 | + paths = [transform.transform_path_non_affine(path) | ||
| 235 | + for path in paths] | ||
| 235 | 236 | transform = transform.get_affine() | |
| 236 | 237 | if not transOffset.is_affine: | |
| 237 | 238 | offsets = transOffset.transform_non_affine(offsets) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -564,7 +564,10 @@ def draw(self, renderer): | |||
| 564 | 564 | ||
| 565 | 565 | path = self.get_path() | |
| 566 | 566 | transform = self.get_transform() | |
| 567 | - tpath = transform.transform_path_non_affine(path) | ||
| 567 | + | ||
| 568 | + with self._forcing_clip_in_log_scale(): | ||
| 569 | + tpath = transform.transform_path_non_affine(path) | ||
| 570 | + | ||
| 568 | 571 | affine = transform.get_affine() | |
| 569 | 572 | ||
| 570 | 573 | if self.get_path_effects(): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5319,3 +5319,20 @@ def test_barh_signature(args, kwargs, warning_count): | |||
| 5319 | 5319 | def test_zero_linewidth(): | |
| 5320 | 5320 | # Check that setting a zero linewidth doesn't error | |
| 5321 | 5321 | plt.plot([0, 1], [0, 1], ls='--', lw=0) | |
| 5322 | + | ||
| 5323 | + | ||
| 5324 | + @pytest.mark.parametrize("plotter", | ||
| 5325 | + [lambda ax: ax.bar([0, 1], [1, 2]), | ||
| 5326 | + lambda ax: ax.errorbar([0, 1], [2, 2], [1, 3])]) | ||
| 5327 | + def test_clipped_log_zero(plotter): | ||
| 5328 | + fig, ax = plt.subplots() | ||
| 5329 | + plotter(ax) | ||
| 5330 | + ax.set_yscale("log") | ||
| 5331 | + png1 = io.BytesIO() | ||
| 5332 | + fig.savefig(png1, format="png") | ||
| 5333 | + fig, ax = plt.subplots() | ||
| 5334 | + plotter(ax) | ||
| 5335 | + ax.set_yscale("log", nonposy="clip") | ||
| 5336 | + png2 = io.BytesIO() | ||
| 5337 | + fig.savefig(png2, format="png") | ||
| 5338 | + assert png1.getvalue() == png2.getvalue() | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments