From 4b78bbad4f75cf8ffbc976b2fc78b4f410f9997f Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Fri, 7 Apr 2023 11:31:43 +0200 Subject: [PATCH] Add rcParams for Axes creation --- doc/users/next_whats_new/axes_rcparams.rst | 18 +++++++++++++ lib/matplotlib/axes/_base.py | 6 ++--- lib/matplotlib/mpl-data/matplotlibrc | 15 +++++++++-- lib/matplotlib/rcsetup.py | 20 +++++++++++++++ lib/mpl_toolkits/mplot3d/axes3d.py | 30 ++++++++++++++-------- 5 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 doc/users/next_whats_new/axes_rcparams.rst diff --git a/doc/users/next_whats_new/axes_rcparams.rst b/doc/users/next_whats_new/axes_rcparams.rst new file mode 100644 index 000000000000..5561e35a49eb --- /dev/null +++ b/doc/users/next_whats_new/axes_rcparams.rst @@ -0,0 +1,18 @@ +New rcParams for Axes creation +------------------------------ + +A number of rcParams are introduced to control parts of the Axes creation. +For a standard 2D Axes, the following are introduced: + +* :rc:`axes.adjustable`, see `.Axes.set_adjustable` +* :rc:`axes.anchor`, see `.Axes.set_anchor` +* :rc:`axes.aspect`, see `.Axes.set_aspect` +* :rc:`axes.box_aspect`, see `.Axes.set_box_aspect` + +There are separate parameters for 3D Axes, including an additional 3D-specific one: + +* :rc:`axes3d.adjustable`, see `.Axes.set_adjustable` +* :rc:`axes3d.anchor`, see `.Axes.set_anchor` +* :rc:`axes3d.aspect`, see `.Axes3D.set_aspect` +* :rc:`axes3d.box_aspect`, see `.Axes3D.set_box_aspect` +* :rc:`axes3d.proj_type`, see `.Axes3D.set_proj_type` diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 2c3d93fa8d8d..eaf51c5925c6 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -646,9 +646,9 @@ def __init__(self, fig, raise ValueError('Width and height specified must be non-negative') self._originalPosition = self._position.frozen() self.axes = self - self._aspect = 'auto' - self._adjustable = 'box' - self._anchor = 'C' + self._aspect = mpl.rcParams['axes.aspect'] + self._adjustable = mpl.rcParams['axes.adjustable'] + self._anchor = mpl.rcParams['axes.anchor'] self._stale_viewlims = {name: False for name in self._axis_names} self._sharex = sharex self._sharey = sharey diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index a5e36cba22cc..f4cbb06c2e6b 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -425,8 +425,19 @@ #axes.autolimit_mode: data # If "data", use axes.xmargin and axes.ymargin as is. # If "round_numbers", after application of margins, axis # limits are further expanded to the nearest "round" number. -#polaraxes.grid: True # display grid on polar axes -#axes3d.grid: True # display grid on 3D axes +#axes.adjustable: box # {box, adjustable} +#axes.anchor: C # {C, E, N, S, W, NE, NW, SE, SW} or two-tuple of floats +#axes.aspect: auto # {equal, auto} or a number +#axes.box_aspect: None # None or a number + +#polaraxes.grid: True # display grid on polar axes + +#axes3d.grid: True # display grid on 3D axes +#axes3d.adjustable: box # {box, adjustable} +#axes3d.anchor: C # {C, E, N, S, W, NE, NW, SE, SW} or two-tuple of floats +#axes3d.aspect: auto +#axes3d.box_aspect: 4, 4, 3 # three floats: x, y, z +#axes3d.proj_type: persp # {persp, ortho} #axes3d.xaxis.panecolor: (0.95, 0.95, 0.95, 0.5) # background pane on 3D axes #axes3d.yaxis.panecolor: (0.90, 0.90, 0.90, 0.5) # background pane on 3D axes diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index a9bebd209077..ba8e5869998c 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -347,6 +347,17 @@ def validate_aspect(s): raise ValueError('not a valid aspect specification') from e +def validate_anchor(s): + if s in ('C', 'E', 'N', 'S', 'W', 'NE', 'NW', 'SE', 'SW'): + return s + if isinstance(s, tuple): + try: + return (float(s[0]), float(s[1])) + except ValueError as e: + raise ValueError('not a valid anchor specification') from e + raise ValueError(f'not a valid anchor specification: {s!r}') + + def validate_fontsize_None(s): if s is None or s == 'None': return None @@ -1015,9 +1026,18 @@ def _convert_validator_spec(key, conv): "axes.xmargin": _range_validators["0 <= x <= 1"], # margin added to xaxis "axes.ymargin": _range_validators["0 <= x <= 1"], # margin added to yaxis 'axes.zmargin': _range_validators["0 <= x <= 1"], # margin added to zaxis + "axes.adjustable": ["box", "datalim"], + "axes.anchor": validate_anchor, + "axes.aspect": validate_aspect, # equal, auto, a number + "axes.box_aspect": validate_float_or_None, "polaraxes.grid": validate_bool, # display polar grid or not "axes3d.grid": validate_bool, # display 3d grid + "axes3d.adjustable": ["box", "datalim"], + "axes3d.anchor": validate_anchor, + "axes3d.aspect": ["auto", "equal", "equalxy", "equalxz", "equalyz"], + "axes3d.proj_type": ["persp", "ortho"], + "axes3d.box_aspect": _listify_validator(validate_float, n=3), "axes3d.xaxis.panecolor": validate_color, # 3d background pane "axes3d.yaxis.panecolor": validate_color, # 3d background pane diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index bd96321aa26f..0e897b030260 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -64,7 +64,7 @@ class Axes3D(Axes): def __init__( self, fig, rect=None, *args, - elev=30, azim=-60, roll=0, sharez=None, proj_type='persp', + elev=30, azim=-60, roll=0, sharez=None, proj_type=None, box_aspect=None, computed_zorder=True, focal_length=None, **kwargs): """ @@ -89,8 +89,12 @@ def __init__( scene to rotate counter-clockwise. sharez : Axes3D, optional Other Axes to share z-limits with. - proj_type : {'persp', 'ortho'} - The projection type, default 'persp'. + proj_type : {'persp', 'ortho'}, optional + The projection type, default :rc:`axes3d.proj_type`. + + .. versionadded:: 3.8 + rcParam added, in earlier versions, the default is 'persp'. + box_aspect : 3-tuple of floats, default: None Changes the physical dimensions of the Axes3D, such that the ratio of the axis lengths in display units is x:y:z. @@ -124,7 +128,7 @@ def __init__( self.initial_azim = azim self.initial_elev = elev self.initial_roll = roll - self.set_proj_type(proj_type, focal_length) + self.set_proj_type(proj_type or mpl.rcParams["axes3d.proj_type"], focal_length) self.computed_zorder = computed_zorder self.xy_viewLim = Bbox.unit() @@ -148,6 +152,12 @@ def __init__( 'Use fig.add_axes(ax) instead.' ) + if 'aspect' not in kwargs: + kwargs['aspect'] = mpl.rcParams["axes3d.aspect"] + if 'adjustable' not in kwargs: + kwargs['adjustable'] = mpl.rcParams["axes3d.adjustable"] + if 'anchor' not in kwargs: + kwargs['anchor'] = mpl.rcParams["axes3d.anchor"] super().__init__( fig, rect, frameon=True, box_aspect=box_aspect, *args, **kwargs ) @@ -376,10 +386,10 @@ def set_box_aspect(self, aspect, *, zoom=1): """ Set the Axes box aspect. - The box aspect is the ratio of height to width in display - units for each face of the box when viewed perpendicular to - that face. This is not to be confused with the data aspect (see - `~.Axes3D.set_aspect`). The default ratios are 4:4:3 (x:y:z). + The box aspect is the ratio of height to width in display units for each face + of the box when viewed perpendicular to that face. This is not to be confused + with the data aspect (see `~.Axes3D.set_aspect`). The default ratios are + :rc:`axes3d.box_aspect` (x, y, z). To simulate having equal aspect in data space, set the box aspect to match your data range in each dimension. @@ -391,7 +401,7 @@ def set_box_aspect(self, aspect, *, zoom=1): aspect : 3-tuple of floats or None Changes the physical dimensions of the Axes3D, such that the ratio of the axis lengths in display units is x:y:z. - If None, defaults to (4, 4, 3). + If None, defaults to :rc:`axes3d.box_aspect`. zoom : float, default: 1 Control overall size of the Axes3D in the figure. Must be > 0. @@ -400,7 +410,7 @@ def set_box_aspect(self, aspect, *, zoom=1): raise ValueError(f'Argument zoom = {zoom} must be > 0') if aspect is None: - aspect = np.asarray((4, 4, 3), dtype=float) + aspect = np.asarray(mpl.rcParams['axes3d.box_aspect'], dtype=float) else: aspect = np.asarray(aspect, dtype=float) _api.check_shape((3,), aspect=aspect)