From fd98f2813e3b9a1723ccddbf8ccf408b5ea9aa77 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 25 Aug 2015 23:27:27 -0400 Subject: [PATCH 1/2] FIX: double z-axis draw in mplot3D When we moved `Axes.draw` to use `Axes.get_children` to get the initial list of artists to draw the zaxis was now in this list (where as it was not previously). The 3D axes use `_axison = False` as `Axes3D` manages the drawing of the axis objects (which must happen before any of the artists). In `Axes.draw` there is a special case to remove the x and y axis from the draw list if `not _axison`. This change is to add a `_get_axis_list` method to the `Axes` base class and override this in the `Axes3D`. This list is looped over to remove all of the `axis` objects that when the axises should not be shown. Closes #4971 --- lib/matplotlib/axes/_base.py | 7 +++++-- lib/mpl_toolkits/mplot3d/axes3d.py | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index e3d50b93897c..303f26ab7bdb 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2049,6 +2049,9 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True): y0, y1 = ylocator.view_limits(y0, y1) self.set_ybound(y0, y1) + def _get_axis_list(self): + return (self.xaxis, self.yaxis) + # Drawing @allow_rasterization @@ -2090,8 +2093,8 @@ def draw(self, renderer=None, inframe=False): self.xaxis.set_zorder(2.5) self.yaxis.set_zorder(2.5) else: - artists.remove(self.xaxis) - artists.remove(self.yaxis) + for _axis in self._get_axis_list(): + artists.remove(_axis) if inframe: artists.remove(self.title) diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 3542c85d6ddd..7225c9e44270 100755 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -192,26 +192,29 @@ def set_top_view(self): def _init_axis(self): '''Init 3D axes; overrides creation of regular X/Y axes''' self.w_xaxis = axis3d.XAxis('x', self.xy_viewLim.intervalx, - self.xy_dataLim.intervalx, self) + self.xy_dataLim.intervalx, self) self.xaxis = self.w_xaxis self.w_yaxis = axis3d.YAxis('y', self.xy_viewLim.intervaly, - self.xy_dataLim.intervaly, self) + self.xy_dataLim.intervaly, self) self.yaxis = self.w_yaxis self.w_zaxis = axis3d.ZAxis('z', self.zz_viewLim.intervalx, - self.zz_dataLim.intervalx, self) + self.zz_dataLim.intervalx, self) self.zaxis = self.w_zaxis for ax in self.xaxis, self.yaxis, self.zaxis: ax.init3d() def get_children(self): - return [self.zaxis,] + Axes.get_children(self) + return [self.zaxis, ] + Axes.get_children(self) + + def _get_axis_list(self): + return super(Axes3D, self)._get_axis_list() + (self.zaxis, ) def unit_cube(self, vals=None): minx, maxx, miny, maxy, minz, maxz = vals or self.get_w_lims() xs, ys, zs = ([minx, maxx, maxx, minx, minx, maxx, maxx, minx], - [miny, miny, maxy, maxy, miny, miny, maxy, maxy], - [minz, minz, minz, minz, maxz, maxz, maxz, maxz]) + [miny, miny, maxy, maxy, miny, miny, maxy, maxy], + [minz, minz, minz, minz, maxz, maxz, maxz, maxz]) return list(zip(xs, ys, zs)) def tunit_cube(self, vals=None, M=None): @@ -1718,7 +1721,7 @@ def plot_wireframe(self, X, Y, Z, *args, **kwargs): The `rstride` and `cstride` kwargs set the stride used to sample the input data to generate the graph. If either is 0 - the input data in not sampled along this direction producing a + the input data in not sampled along this direction producing a 3D line plot rather than a wireframe plot. ========== ================================================ @@ -2438,7 +2441,7 @@ def bar3d(self, x, y, z, dx, dy, dz, color='b', self.add_collection(col) self.auto_scale_xyz((minx, maxx), (miny, maxy), (minz, maxz), had_data) - + return col def set_title(self, label, fontdict=None, loc='center', **kwargs): From d874f13d6e774456b32572eab81261c803062d12 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 25 Aug 2015 23:48:40 -0400 Subject: [PATCH 2/2] FIX: jumping axes The first time the Axes3D is drawn the x,y,z axis are drawn before the aspect has been adjusted so they are wrong (the double zaxis the previous commit removed was actually the correct one). This copies the relevant code up to the Axes3D level. This is not great as there is code-duplication, but the 'correct' solution is to make the Axes.draw method much more pluggable which is a very large job. --- lib/mpl_toolkits/mplot3d/axes3d.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 7225c9e44270..b1032f45091e 100755 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -247,6 +247,18 @@ def draw(self, renderer): self.axesPatch.draw(renderer) self._frameon = False + # first, set the aspect + # this is duplicated from `axes._base._AxesBase.draw` + # but must be called before any of the artist are drawn as + # it adjusts the view limits and the size of the bounding box + # of the axes + locator = self.get_axes_locator() + if locator: + pos = locator(self, renderer) + self.apply_aspect(pos) + else: + self.apply_aspect() + # add the projection matrix to the renderer self.M = self.get_proj() renderer.M = self.M