-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathshared.py
More file actions
167 lines (155 loc) · 7.44 KB
/
shared.py
File metadata and controls
167 lines (155 loc) · 7.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/env python
"""
An axes used to jointly format Cartesian and polar axes.
"""
# NOTE: We could define these in base.py but idea is projection-specific formatters
# should never be defined on the base class. Might add to this class later anyway.
import numpy as np
from ..config import rc
from ..internals import ic # noqa: F401
from ..internals import _pop_kwargs
from ..utils import _fontsize_to_pt, _not_none, units
class _SharedAxes(object):
"""
Mix-in class with methods shared between `~proplot.axes.CartesianAxes`
and `~proplot.axes.PolarAxes`.
"""
@staticmethod
def _min_max_lim(key, min_=None, max_=None, lim=None):
"""
Translate and standardize minimum, maximum, and limit keyword arguments.
"""
if lim is None:
lim = (None, None)
if not np.iterable(lim) or not len(lim) == 2:
raise ValueError(f'Invalid {key}{lim!r}. Must be 2-tuple of values.')
min_ = _not_none(**{f'{key}min': min_, f'{key}lim_0': lim[0]})
max_ = _not_none(**{f'{key}max': max_, f'{key}lim_1': lim[1]})
return min_, max_
def _update_background(
self, x=None, tickwidth=None, tickwidthratio=None, **kwargs
):
"""
Update the background patch and spines.
"""
# Update the background patch
kw_face, kw_edge = rc._get_background_props(**kwargs)
self.patch.update(kw_face)
if x is None:
opts = self.spines
elif x == 'x':
opts = ('bottom', 'top', 'inner', 'polar')
else:
opts = ('left', 'right', 'start', 'end')
for opt in opts:
self.spines.get(opt, {}).update(kw_edge)
# Update the tick colors
axis = 'both' if x is None else x
x = _not_none(x, 'x')
obj = getattr(self, x + 'axis')
edgecolor = kw_edge.get('edgecolor', None)
if edgecolor is not None:
self.tick_params(axis=axis, which='both', color=edgecolor)
# Update the tick widths
# NOTE: Only use 'linewidth' if it was explicitly passed. Do not
# include 'linewidth' inferred from rc['axes.linewidth'] setting.
kwmajor = getattr(obj, '_major_tick_kw', {}) # graceful fallback if API changes
kwminor = getattr(obj, '_minor_tick_kw', {})
if 'linewidth' in kwargs:
tickwidth = _not_none(tickwidth, kwargs['linewidth'])
tickwidth = _not_none(tickwidth, rc.find('tick.width', context=True))
tickwidthratio = _not_none(tickwidthratio, rc.find('tick.widthratio', context=True)) # noqa: E501
tickwidth_prev = kwmajor.get('width', rc[x + 'tick.major.width'])
if tickwidth_prev == 0:
tickwidthratio_prev = rc['tick.widthratio'] # no other way of knowing
else:
tickwidthratio_prev = kwminor.get('width', rc[x + 'tick.minor.width']) / tickwidth_prev # noqa: E501
for which in ('major', 'minor'):
kwticks = {}
if tickwidth is not None or tickwidthratio is not None:
tickwidth = _not_none(tickwidth, tickwidth_prev)
kwticks['width'] = tickwidth = units(tickwidth, 'pt')
if tickwidth == 0: # avoid unnecessary padding
kwticks['size'] = 0
elif which == 'minor':
tickwidthratio = _not_none(tickwidthratio, tickwidthratio_prev)
kwticks['width'] *= tickwidthratio
self.tick_params(axis=axis, which=which, **kwticks)
def _update_ticks(
self, x, *, grid=None, gridminor=None, gridpad=None, gridcolor=None,
ticklen=None, ticklenratio=None, tickdir=None, tickcolor=None,
labeldir=None, labelpad=None, labelcolor=None, labelsize=None, labelweight=None,
):
"""
Update the gridlines and labels. Set `gridpad` to ``True`` to use grid padding.
"""
# Filter out text properties
axis = 'both' if x is None else x
kwtext = rc._get_ticklabel_props(axis)
kwtext_extra = _pop_kwargs(kwtext, 'weight', 'family')
kwtext = {'label' + key: value for key, value in kwtext.items()}
if labelcolor is not None:
kwtext['labelcolor'] = labelcolor
if labelsize is not None:
kwtext['labelsize'] = labelsize
if labelweight is not None:
kwtext_extra['weight'] = labelweight
# Apply tick settings with tick_params when possible
x = _not_none(x, 'x')
obj = getattr(self, x + 'axis')
kwmajor = getattr(obj, '_major_tick_kw', {}) # graceful fallback if API changes
kwminor = getattr(obj, '_minor_tick_kw', {})
ticklen_prev = kwmajor.get('size', rc[x + 'tick.major.size'])
if ticklen_prev == 0:
ticklenratio_prev = rc['tick.lenratio'] # no other way of knowing
else:
ticklenratio_prev = kwminor.get('size', rc[x + 'tick.minor.size']) / ticklen_prev # noqa: E501
for b, which in zip((grid, gridminor), ('major', 'minor')):
# Tick properties
# NOTE: Must make 'tickcolor' overwrite 'labelcolor' or else 'color'
# passed to __init__ will not apply correctly. Annoying but unavoidable
kwticks = rc._get_tickline_props(axis, which=which)
if labelpad is not None:
kwticks['pad'] = labelpad
if tickcolor is not None:
kwticks['color'] = tickcolor
if ticklen is not None or ticklenratio is not None:
ticklen = _not_none(ticklen, ticklen_prev)
kwticks['size'] = ticklen = units(ticklen, 'pt')
if ticklen > 0 and which == 'minor':
ticklenratio = _not_none(ticklenratio, ticklenratio_prev)
kwticks['size'] *= ticklenratio
if gridpad: # use grid.labelpad instead of tick.labelpad
kwticks.pop('pad', None)
pad = rc.find('grid.labelpad', context=True)
if pad is not None:
kwticks['pad'] = units(pad, 'pt')
# Tick direction properties
# NOTE: These have no x and y-specific versions but apply here anyway
if labeldir == 'in': # put tick labels inside the plot
tickdir = 'in'
kwticks.setdefault(
'pad',
- rc[f'{axis}tick.major.size']
- _not_none(labelpad, rc[f'{axis}tick.major.pad'])
- _fontsize_to_pt(rc[f'{axis}tick.labelsize'])
)
if tickdir is not None:
kwticks['direction'] = tickdir
# Gridline properties
# NOTE: Internally ax.grid() passes gridOn to ax.tick_params() but this
# is undocumented and might have weird side effects. Just use ax.grid()
b = rc._get_gridline_bool(b, axis=axis, which=which)
if b is not None:
self.grid(b, axis=axis, which=which)
kwlines = rc._get_gridline_props(which=which)
if 'axisbelow' in kwlines:
self.set_axisbelow(kwlines.pop('axisbelow'))
if gridcolor is not None:
kwlines['grid_color'] = gridcolor
# Apply tick and gridline properties
self.tick_params(axis=axis, which=which, **kwticks, **kwlines, **kwtext)
# Apply settings that can't be controlled with tick_params
if kwtext_extra:
for lab in obj.get_ticklabels():
lab.update(kwtext_extra)