@@ -129,7 +129,9 @@ def __init__(
129129
130130self .xy_viewLim = Bbox .unit ()
131131self .zz_viewLim = Bbox .unit ()
132- self .xy_dataLim = Bbox .unit ()
132+ xymargin = 0.05 * 10 / 11 # match mpl3.7 appearance
133+ self .xy_dataLim = Bbox ([[xymargin ,xymargin ],
134+ [1 - xymargin ,1 - xymargin ]])
133135# z-limits are encoded in the x-component of the Bbox, y is un-used
134136self .zz_dataLim = Bbox .unit ()
135137
@@ -157,6 +159,9 @@ def __init__(
157159self .set_axis_on ()
158160self .M = None
159161
162+ self ._view_margin = 1 / 48 # default value to match mpl3.7
163+ self .autoscale_view ()
164+
160165# func used to format z -- fall back on major formatters
161166self .fmt_zdata = None
162167
@@ -336,7 +341,8 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
336341self .set_ylim3d ,
337342self .set_zlim3d )):
338343if i in ax_indices :
339- set_lim (mean [i ]- deltas [i ]/ 2. ,mean [i ]+ deltas [i ]/ 2. )
344+ set_lim (mean [i ]- deltas [i ]/ 2. ,mean [i ]+ deltas [i ]/ 2. ,
345+ auto = None ,view_margin = self ._view_margin )
340346else :# 'box'
341347# Change the box aspect such that the ratio of the length of
342348# the unmodified axis to the length of the diagonal
@@ -404,8 +410,8 @@ def set_box_aspect(self, aspect, *, zoom=1):
404410else :
405411aspect = np .asarray (aspect ,dtype = float )
406412_api .check_shape ((3 ,),aspect = aspect )
407- # default scale tuned to match thempl32 appearance.
408- aspect *= 1.8294640721620434 * zoom / np .linalg .norm (aspect )
413+ # default scale tuned to match thempl3.2 appearance.
414+ aspect *= 1.8294640721620434 * 25 / 24 * zoom / np .linalg .norm (aspect )
409415
410416self ._box_aspect = aspect
411417self .stale = True
@@ -575,17 +581,17 @@ def autoscale(self, enable=True, axis='both', tight=None):
575581scalez = True
576582else :
577583if axis in ['x' ,'both' ]:
578- self .set_autoscalex_on (bool ( enable ) )
584+ self .set_autoscalex_on (enable )
579585scalex = self .get_autoscalex_on ()
580586else :
581587scalex = False
582588if axis in ['y' ,'both' ]:
583- self .set_autoscaley_on (bool ( enable ) )
589+ self .set_autoscaley_on (enable )
584590scaley = self .get_autoscaley_on ()
585591else :
586592scaley = False
587593if axis in ['z' ,'both' ]:
588- self .set_autoscalez_on (bool ( enable ) )
594+ self .set_autoscalez_on (enable )
589595scalez = self .get_autoscalez_on ()
590596else :
591597scalez = False
@@ -610,8 +616,8 @@ def auto_scale_xyz(self, X, Y, Z=None, had_data=None):
610616# Let autoscale_view figure out how to use this data.
611617self .autoscale_view ()
612618
613- def autoscale_view (self ,tight = None ,scalex = True , scaley = True ,
614- scalez = True ):
619+ def autoscale_view (self ,tight = None ,
620+ scalex = True , scaley = True , scalez = True ):
615621"""
616622 Autoscale the view limits using the data limits.
617623
@@ -643,7 +649,7 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True,
643649x1 += delta
644650if not _tight :
645651x0 ,x1 = xlocator .view_limits (x0 ,x1 )
646- self .set_xbound (x0 ,x1 )
652+ self .set_xbound (x0 ,x1 , self . _view_margin )
647653
648654if scaley and self .get_autoscaley_on ():
649655y0 ,y1 = self .xy_dataLim .intervaly
@@ -655,7 +661,7 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True,
655661y1 += delta
656662if not _tight :
657663y0 ,y1 = ylocator .view_limits (y0 ,y1 )
658- self .set_ybound (y0 ,y1 )
664+ self .set_ybound (y0 ,y1 , self . _view_margin )
659665
660666if scalez and self .get_autoscalez_on ():
661667z0 ,z1 = self .zz_dataLim .intervalx
@@ -667,7 +673,7 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True,
667673z1 += delta
668674if not _tight :
669675z0 ,z1 = zlocator .view_limits (z0 ,z1 )
670- self .set_zbound (z0 ,z1 )
676+ self .set_zbound (z0 ,z1 , self . _view_margin )
671677
672678def get_w_lims (self ):
673679"""Get 3D world limits."""
@@ -676,28 +682,116 @@ def get_w_lims(self):
676682minz ,maxz = self .get_zlim3d ()
677683return minx ,maxx ,miny ,maxy ,minz ,maxz
678684
679- # set_xlim, set_ylim are directly inherited from base Axes.
685+ def _set_bound3d (self ,get_bound ,set_lim ,axis_inverted ,
686+ lower = None ,upper = None ,view_margin = None ):
687+ """
688+ Set 3D axis bounds.
689+ """
690+ if upper is None and np .iterable (lower ):
691+ lower ,upper = lower
692+
693+ old_lower ,old_upper = get_bound ()
694+ if lower is None :
695+ lower = old_lower
696+ if upper is None :
697+ upper = old_upper
698+
699+ set_lim (sorted ((lower ,upper ),reverse = bool (axis_inverted ())),
700+ auto = None ,view_margin = view_margin )
701+
702+ def set_xbound (self ,lower = None ,upper = None ,view_margin = None ):
703+ # docstring inherited
704+ self ._set_bound3d (self .get_xbound ,self .set_xlim ,self .xaxis_inverted ,
705+ upper ,lower ,view_margin )
706+
707+ def set_ybound (self ,lower = None ,upper = None ,view_margin = None ):
708+ # docstring inherited
709+ self ._set_bound3d (self .get_ybound ,self .set_ylim ,self .yaxis_inverted ,
710+ upper ,lower ,view_margin )
711+
712+ def set_zbound (self ,lower = None ,upper = None ,view_margin = None ):
713+ """
714+ Set the lower and upper numerical bounds of the z-axis.
715+ This method will honor axis inversion regardless of parameter order.
716+ It will not change the autoscaling setting (`.get_autoscaley_on()`).
717+ Parameters
718+ ----------
719+ lower, upper : float or None
720+ The lower and upper bounds. If *None*, the respective axis bound
721+ is not modified.
722+ view_margin : float or None
723+ The margin to apply to the bounds. If *None*, the margin is handled
724+ by set_zlim.
725+ See Also
726+ --------
727+ get_zbound
728+ get_zlim, set_zlim
729+ invert_zaxis, zaxis_inverted
730+ """
731+ self ._set_bound3d (self .get_zbound ,self .set_zlim ,self .zaxis_inverted ,
732+ upper ,lower ,view_margin )
733+
734+ def _set_lim3d (self ,axis ,lower = None ,upper = None ,* ,emit = True ,
735+ auto = False ,view_margin = None ,axmin = None ,axmax = None ):
736+ """
737+ Set 3D axis limits.
738+ See `.Axes.set_ylim` for full documentation
739+ """
740+ if upper is None :
741+ if np .iterable (lower ):
742+ lower ,upper = lower
743+ else :
744+ raise ValueError ("Must provide an upper bound." )
745+ if lower is None :
746+ raise ValueError ("Must provide a lower bound." )
747+ if axmin is not None :
748+ if lower is not None :
749+ raise TypeError ("Cannot pass both 'lower' and 'min'" )
750+ lower = axmin
751+ if axmax is not None :
752+ if upper is not None :
753+ raise TypeError ("Cannot pass both 'upper' and 'max'" )
754+ upper = axmax
755+ if view_margin is None :
756+ if mpl .rcParams ['axes3d.automargin' ]:
757+ view_margin = self ._view_margin
758+ else :
759+ view_margin = 0
760+ delta = (upper - lower )* view_margin
761+ lower -= delta
762+ upper += delta
763+ return axis ._set_lim (lower ,upper ,emit = emit ,auto = auto )
764+
765+ def set_xlim (self ,left = None ,right = None ,* ,emit = True ,auto = False ,
766+ view_margin = None ,xmin = None ,xmax = None ):
767+ """
768+ Set 3D x limits.
769+ See `.Axes.set_xlim` for full documentation
770+ """
771+ return self ._set_lim3d (self .xaxis ,left ,right ,emit = emit ,auto = auto ,
772+ view_margin = view_margin ,axmin = xmin ,axmax = xmax )
773+
774+ def set_ylim (self ,bottom = None ,top = None ,* ,emit = True ,auto = False ,
775+ view_margin = None ,ymin = None ,ymax = None ):
776+ """
777+ Set 3D y limits.
778+ See `.Axes.set_ylim` for full documentation
779+ """
780+ return self ._set_lim3d (self .yaxis ,bottom ,top ,emit = emit ,auto = auto ,
781+ view_margin = view_margin ,axmin = ymin ,axmax = ymax )
782+
680783def set_zlim (self ,bottom = None ,top = None ,* ,emit = True ,auto = False ,
681- zmin = None ,zmax = None ):
784+ view_margin = None , zmin = None ,zmax = None ):
682785"""
683786 Set 3D z limits.
684787
685788 See `.Axes.set_ylim` for full documentation
686789 """
687- if top is None and np .iterable (bottom ):
688- bottom ,top = bottom
689- if zmin is not None :
690- if bottom is not None :
691- raise TypeError ("Cannot pass both 'bottom' and 'zmin'" )
692- bottom = zmin
693- if zmax is not None :
694- if top is not None :
695- raise TypeError ("Cannot pass both 'top' and 'zmax'" )
696- top = zmax
697- return self .zaxis ._set_lim (bottom ,top ,emit = emit ,auto = auto )
698-
699- set_xlim3d = maxes .Axes .set_xlim
700- set_ylim3d = maxes .Axes .set_ylim
790+ return self ._set_lim3d (self .zaxis ,bottom ,top ,emit = emit ,auto = auto ,
791+ view_margin = view_margin ,axmin = zmin ,axmax = zmax )
792+
793+ set_xlim3d = set_xlim
794+ set_ylim3d = set_ylim
701795set_zlim3d = set_zlim
702796
703797def get_xlim (self ):
@@ -982,6 +1076,15 @@ def clear(self):
9821076self ._zmargin = mpl .rcParams ['axes.zmargin' ]
9831077else :
9841078self ._zmargin = 0.
1079+
1080+ xymargin = 0.05 * 10 / 11 # match mpl3.7 appearance
1081+ self .xy_dataLim = Bbox ([[xymargin ,xymargin ],
1082+ [1 - xymargin ,1 - xymargin ]])
1083+ # z-limits are encoded in the x-component of the Bbox, y is un-used
1084+ self .zz_dataLim = Bbox .unit ()
1085+ self ._view_margin = 1 / 48 # default value to match mpl3.7
1086+ self .autoscale_view ()
1087+
9851088self .grid (mpl .rcParams ['axes3d.grid' ])
9861089
9871090def _button_press (self ,event ):
@@ -1162,9 +1265,9 @@ def drag_pan(self, button, key, x, y):
11621265dz = (maxz - minz )* duvw_projected [2 ]
11631266
11641267# Set the new axis limits
1165- self .set_xlim3d (minx + dx ,maxx + dx )
1166- self .set_ylim3d (miny + dy ,maxy + dy )
1167- self .set_zlim3d (minz + dz ,maxz + dz )
1268+ self .set_xlim3d (minx + dx ,maxx + dx , auto = None )
1269+ self .set_ylim3d (miny + dy ,maxy + dy , auto = None )
1270+ self .set_zlim3d (minz + dz ,maxz + dz , auto = None )
11681271
11691272def _calc_view_axes (self ,eye ):
11701273"""
@@ -1301,9 +1404,9 @@ def _scale_axis_limits(self, scale_x, scale_y, scale_z):
13011404dz = (maxz - minz )* scale_z
13021405
13031406# Set the scaled axis limits
1304- self .set_xlim3d (cx - dx / 2 ,cx + dx / 2 )
1305- self .set_ylim3d (cy - dy / 2 ,cy + dy / 2 )
1306- self .set_zlim3d (cz - dz / 2 ,cz + dz / 2 )
1407+ self .set_xlim3d (cx - dx / 2 ,cx + dx / 2 , auto = None )
1408+ self .set_ylim3d (cy - dy / 2 ,cy + dy / 2 , auto = None )
1409+ self .set_zlim3d (cz - dz / 2 ,cz + dz / 2 , auto = None )
13071410
13081411def set_zlabel (self ,zlabel ,fontdict = None ,labelpad = None ,** kwargs ):
13091412"""
@@ -1392,26 +1495,6 @@ def get_zbound(self):
13921495else :
13931496return top ,bottom
13941497
1395- def set_zbound (self ,lower = None ,upper = None ):
1396- """
1397- Set the lower and upper numerical bounds of the z-axis.
1398-
1399- This method will honor axes inversion regardless of parameter order.
1400- It will not change the autoscaling setting (`.get_autoscalez_on()`).
1401- """
1402- if upper is None and np .iterable (lower ):
1403- lower ,upper = lower
1404-
1405- old_lower ,old_upper = self .get_zbound ()
1406- if lower is None :
1407- lower = old_lower
1408- if upper is None :
1409- upper = old_upper
1410-
1411- self .set_zlim (sorted ((lower ,upper ),
1412- reverse = bool (self .zaxis_inverted ())),
1413- auto = None )
1414-
14151498def text (self ,x ,y ,z ,s ,zdir = None ,** kwargs ):
14161499"""
14171500 Add text to the plot.