Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
AxisArtist to use standard tick directions and have an option for tick orientation#24553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
1561dcd
e5790aa
f4ba9af
0b6fcba
5f5728d
8f2dae5
bf39e0a
95b9e48
d940fb9
d52056b
a9b049c
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -55,14 +55,20 @@ | ||
axislabel ha right center right center | ||
=================== ====== ======== ====== ======== | ||
Direction of ticks follows the setting in rcParams (default is "out"). To | ||
change it, :: | ||
ax.axis["bottom"].major_ticks.set_tickdir("in") | ||
Ticks can be oriented either normal to the axisline or parallel to the grid | ||
lines. The default is "normal" if "tickdir" is "out", "parallel" otherwise. | ||
To change it, :: | ||
ax.axis["bottom"].major_ticks.set_tick_orientation("normal") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. The line above (which I cannot mark...) should be updated as well? Currently:
| ||
The following attributes can be customized (use the ``set_xxx`` methods): | ||
* `Ticks`: ticksize,tickdir | ||
* `TickLabels`: pad | ||
* `AxisLabel`: pad | ||
""" | ||
@@ -72,6 +78,7 @@ | ||
from operator import methodcaller | ||
import warnings | ||
import numpy as np | ||
@@ -109,17 +116,37 @@ class Ticks(AttributeCopier, Line2D): | ||
Ticks are derived from `.Line2D`, and note that ticks themselves | ||
are markers. Thus, you should use set_mec, set_mew, etc. | ||
To change the tick size (length), use set_ticksize. To change the | ||
direction of the ticks, use set_tickdir ("out" corresponds to the side of | ||
the label, "in" to the opposite side). | ||
""" | ||
# tick_out is mostly deprecated in favor of tickdir. | ||
@_api.delete_parameter("3.7", "tick_out", alternative="tickdir") | ||
def __init__(self, ticksize, tick_out=None, | ||
*, tick_orientation="auto", | ||
axis=None, **kwargs): | ||
self._ticksize = ticksize | ||
self.locs_angles_labels = [] | ||
if "tickdir" in kwargs: | ||
if tick_out is not None: | ||
raise ValueError("tickdir and tick_out" | ||
"cannot be used together") | ||
self.set_tickdir(kwargs.pop("tickdir")) | ||
else: | ||
# The default value for tick_out was False. We changed it to None | ||
# to catch whether it explicily set by the user. "None" may not be | ||
# a good choice of value though. | ||
if tick_out is None: | ||
warnings.warn("The dwfault behavior will change. " | ||
"Explicitly set tickdir parameter if you want.") | ||
tick_out = False | ||
self.set_tickdir({True: "out", False: "in"}[bool(tick_out)]) | ||
self.set_tick_orientation(tick_orientation) | ||
self._axis = axis | ||
if self._axis is not None: | ||
@@ -152,13 +179,61 @@ def get_markeredgecolor(self): | ||
def get_markeredgewidth(self): | ||
return self.get_attribute_from_ref_artist("markeredgewidth") | ||
def set_tickdir(self, tickdir): | ||
_api.check_in_list(self._tick_paths, tickdir=tickdir) | ||
self._tickdir = tickdir | ||
def get_tickdir(self): | ||
return self._tickdir | ||
def set_tick_orientation(self, mode): | ||
""" | ||
Set how tick orientation will be determined. | ||
Parameters | ||
---------- | ||
mode : {"parallel", "normal", "auto"} | ||
'parallel' - ticks along the grid lines | ||
'normal' - ticks normal to axis line. | ||
'auto' - 'normal' if tickdir is 'out' else 'parallel' | ||
""" | ||
_api.check_in_list(["auto", "normal", "parallel"], | ||
tick_orientation=mode) | ||
self._tick_orientation = mode | ||
def get_tick_orientation(self, interpret_auto=True): | ||
""" | ||
Return orientation of ticks. | ||
Parameters | ||
---------- | ||
interpret_auto : bool, default if True | ||
If True and tick_orientation is 'auto', return the | ||
interpreted value ('normal' if dicktir is out, else 'parallel). | ||
If False, return 'auto' as 'auto'. | ||
Returns | ||
------- | ||
tick_orientation : {"parallel", "normal", "auto"} | ||
""" | ||
tick_orientation = self._tick_orientation | ||
if tick_orientation == "auto" and interpret_auto: | ||
tick_orientation = ("normal" if self._tickdir == "out" | ||
else "parallel") | ||
return tick_orientation | ||
def set_tick_out(self, b): | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Maybe we should deprecate these now? ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. At what version do we want it to be deprecated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I'd say 3.7. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I think this could fit in our normal warn for one release, remove the next pattern, but would defer to you if you wanted to put the new version in for a cycle before we started (or pushed the removal date out further). | ||
"""Set whether ticks are drawn inside or outside the axes.""" | ||
self.set_tickdir({True: "out", False: "in"}[bool(b)]) | ||
def get_tick_out(self): | ||
"""Return whether ticks are drawn inside or outside the axes.""" | ||
if self._tickdir == "out": | ||
return True | ||
elif self._tickdir == "in": | ||
return False | ||
raise ValueError(f"tickdir is {self._tickdir}") | ||
def set_ticksize(self, ticksize): | ||
"""Set length of the ticks in points.""" | ||
@@ -171,7 +246,11 @@ def get_ticksize(self): | ||
def set_locs_angles(self, locs_angles): | ||
self.locs_angles = locs_angles | ||
_tick_paths = { | ||
"out": Path([[0, 0], [-1, 0]]), | ||
"in": Path([[0, 0], [1, 0]]), | ||
"inout": Path([[-1/2, 0], [1/2, 0]]), | ||
} | ||
def draw(self, renderer): | ||
if not self.get_visible(): | ||
@@ -185,15 +264,14 @@ def draw(self, renderer): | ||
path_trans = self.get_transform() | ||
marker_transform = (Affine2D() | ||
.scale(renderer.points_to_pixels(self._ticksize))) | ||
tick_path = self._tick_paths[self._tickdir] | ||
for loc, angle in self.locs_angles: | ||
locs = path_trans.transform_non_affine(np.array([loc])) | ||
if self.axes and not self.axes.viewLim.contains(*locs[0]): | ||
continue | ||
renderer.draw_markers( | ||
gc,tick_path, | ||
marker_transform + Affine2D().rotate_deg(angle), | ||
Path(locs), path_trans.get_affine()) | ||
@@ -868,12 +946,14 @@ def _init_ticks(self, **kwargs): | ||
kwargs.get( | ||
"major_tick_size", | ||
mpl.rcParams[f"{axis_name}tick.major.size"]), | ||
axis=self.axis, transform=trans, | ||
tickdir=mpl.rcParams[f"{axis_name}tick.direction"]) | ||
self.minor_ticks = Ticks( | ||
kwargs.get( | ||
"minor_tick_size", | ||
mpl.rcParams[f"{axis_name}tick.minor.size"]), | ||
axis=self.axis, transform=trans, | ||
tickdir=mpl.rcParams[f"{axis_name}tick.direction"]) | ||
size = mpl.rcParams[f"{axis_name}tick.labelsize"] | ||
self.major_ticklabels = TickLabels( | ||
@@ -895,7 +975,7 @@ def _init_ticks(self, **kwargs): | ||
"minor_tick_pad", mpl.rcParams[f"{axis_name}tick.minor.pad"]), | ||
) | ||
def _get_tick_info(self, tick_iter, tick_orientation="parallel"): | ||
""" | ||
Return a pair of: | ||
@@ -909,9 +989,16 @@ def _get_tick_info(self, tick_iter): | ||
for loc, angle_normal, angle_tangent, label in tick_iter: | ||
angle_label = angle_tangent - 90 + ticklabel_add_angle | ||
if tick_orientation == "parallel": # tick along the gridlines | ||
angle_tick = ( | ||
angle_normal | ||
if 90 <= (angle_label - angle_normal) % 360 <= 270 | ||
else angle_normal + 180) | ||
elif tick_orientation == "normal": # tick normal to axisline. | ||
angle_tick = 180+angle_label | ||
else: | ||
raise ValueError( | ||
f"Unsupported tick_orientation of {tick_orientation}") | ||
ticks_loc_angle.append([loc, angle_tick]) | ||
ticklabels_loc_angle_label.append([loc, angle_label, label]) | ||
@@ -925,24 +1012,31 @@ def _update_ticks(self, renderer=None): | ||
renderer = self.figure._get_renderer() | ||
dpi_cor = renderer.points_to_pixels(1.) | ||
multiplier = ( | ||
self.major_ticks.get_visible() | ||
* {"out": 1, "inout": .5, "in": 0}[self.major_ticks._tickdir]) | ||
self.major_ticklabels._external_pad = \ | ||
multiplier * self.major_ticks._ticksize * dpi_cor | ||
self.minor_ticklabels._external_pad = \ | ||
multiplier * self.major_ticks._ticksize * dpi_cor | ||
majortick_iter, minortick_iter = \ | ||
self._axis_artist_helper.get_tick_iterators(self.axes) | ||
tick_orientation = self.major_ticks.get_tick_orientation( | ||
interpret_auto=True) | ||
tick_loc_angle, ticklabel_loc_angle_label = \ | ||
self._get_tick_info(majortick_iter, tick_orientation) | ||
self.major_ticks.set_locs_angles(tick_loc_angle) | ||
self.major_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label) | ||
tick_orientation = self.minor_ticks.get_tick_orientation( | ||
interpret_auto=True) | ||
tick_loc_angle, ticklabel_loc_angle_label = \ | ||
self._get_tick_info(minortick_iter, tick_orientation) | ||
self.minor_ticks.set_locs_angles(tick_loc_angle) | ||
self.minor_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label) | ||
@@ -1005,15 +1099,30 @@ def _update_label(self, renderer): | ||
if not self.label.get_visible(): | ||
return | ||
# We calculate the pad size for the axislabel. | ||
if self._ticklabel_add_angle != self._axislabel_add_angle: | ||
# If ticklabels and axislabel are on different side, we only | ||
# consider the padding for the ticks only. | ||
# "in", "out" and "inout" are relative to the ticks. Therefore, "in" | ||
# means that axislabel and ticks are on the same side while | ||
# ticklabels are on the other side. | ||
ticksizes = [] | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Can you add a test that uses this path? (And ideally "all" the newly introduced options.) Edit: one way to at least test the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Yes, I will do. | ||
# ticksize of the major_ticks | ||
ticksizes.append( | ||
self.major_ticks.get_visible() | ||
* {"out": 0, "inout": .5, "in": 1}[self.major_ticks._tickdir] | ||
* self.major_ticks._ticksize | ||
) | ||
ticksizes.append( | ||
self.minor_ticks.get_visible() | ||
* {"out": 0, "inout": .5, "in": 1}[self.minor_ticks._tickdir] | ||
* self.minor_ticks._ticksize | ||
) | ||
axislabel_pad = max(ticksizes) | ||
else: | ||
# If ticklabels and axislabel are on the same side, we use values | ||
# from the the ticklabels. | ||
axislabel_pad = max(self.major_ticklabels._axislabel_pad, | ||
self.minor_ticklabels._axislabel_pad) | ||
Uh oh!
There was an error while loading.Please reload this page.