Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ebf2282

Browse files
committed
Make cursor text precision actually correspond to pointing precision.
Currently, the cursor text (x=..., y=...) is typically displayed with much, much more precision than the mouse cursor has (typically, one pixel); IOW the last digits displayed are typically meaningless. Make ScalarFormatter.format_data_short convert the inter-pixel distance to data space and compute the corresponding number of significant digits to use (and don't drop the final zeroes if any, as they are now, well, significant). (A similar change could be applied to other formatters, e.g. LogFormatter.)
1 parent 053703f commit ebf2282

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

lib/matplotlib/tests/test_image.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ def test_cursor_data():
351351

352352
@pytest.mark.parametrize(
353353
"data, text_without_colorbar, text_with_colorbar", [
354-
([[10001, 10000]], "[1e+04]", "[10001]"),
354+
([[10001, 10000]], "[1e+04]", "[10001.000]"),
355355
([[.123, .987]], "[0.123]", "[0.123]"),
356356
])
357357
def test_format_cursor_data(data, text_without_colorbar, text_with_colorbar):

lib/matplotlib/tests/test_ticker.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,9 @@ class TestScalarFormatter:
512512
(True, (6, 6), (-1e5, 1e5), 6, False),
513513
]
514514

515-
@pytest.mark.parametrize('unicode_minus, result',
516-
[(True, "\N{MINUS SIGN}1"), (False, "-1")])
515+
@pytest.mark.parametrize(
516+
'unicode_minus, result',
517+
[(True, "\N{MINUS SIGN}1.000"), (False, "-1.000")])
517518
def test_unicode_minus(self, unicode_minus, result):
518519
mpl.rcParams['axes.unicode_minus'] = unicode_minus
519520
assert (
@@ -558,6 +559,16 @@ def test_scilimits(self, sci_type, scilimits, lim, orderOfMag, fewticks):
558559
tmp_form.set_locs(ax.yaxis.get_majorticklocs())
559560
assert orderOfMag == tmp_form.orderOfMagnitude
560561

562+
def test_cursor_precision(self):
563+
fig, ax = plt.subplots()
564+
ax.set_xlim(-1, 1) # Pointing precision of 0.001.
565+
fmt = ax.xaxis.get_major_formatter().format_data_short
566+
assert fmt(0) == "0.000"
567+
assert fmt(0.0123) == "0.012"
568+
assert fmt(0.123) == "0.123"
569+
assert fmt(1.23) == "1.230"
570+
assert fmt(12.3) == "12.300"
571+
561572

562573
class FakeAxis:
563574
"""Allow Formatter to be called without having a "full" plot set up."""

lib/matplotlib/ticker.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,39 @@ def set_powerlimits(self, lims):
587587

588588
def format_data_short(self, value):
589589
# docstring inherited
590+
if isinstance(value, np.ma.MaskedArray) and value.mask:
591+
return ""
592+
if self.axis.__name__ in ["xaxis", "yaxis"]:
593+
if self.axis.__name__ == "xaxis":
594+
axis_trf = self.axis.axes.get_xaxis_transform()
595+
axis_inv_trf = axis_trf.inverted()
596+
screen_xy = axis_trf.transform((value, 0))
597+
neighbor_values = axis_inv_trf.transform(
598+
screen_xy + [[-1, 0], [+1, 0]])[:, 0]
599+
else: # yaxis:
600+
axis_trf = self.axis.axes.get_yaxis_transform()
601+
axis_inv_trf = axis_trf.inverted()
602+
screen_xy = axis_trf.transform((0, value))
603+
neighbor_values = axis_inv_trf.transform(
604+
screen_xy + [[0, -1], [0, +1]])[:, 1]
605+
delta = abs(neighbor_values - value).max()
606+
else:
607+
# Rough approximation: no more than 1e4 pixels.
608+
delta = self.axis.get_view_interval() / 1e4
609+
# If e.g. value = 45.67 and delta = 0.02, then we want to round to
610+
# 2 digits after the decimal point (floor(log10(0.02)) = -2);
611+
# 45.67 contributes 2 digits before the decimal point
612+
# (floor(log10(45.67)) + 1 = 2): the total is 4 significant digits.
613+
# A value of 0 contributes 1 "digit" before the decimal point.
614+
sig_digits = max(
615+
0,
616+
(math.floor(math.log10(abs(value))) + 1 if value else 1)
617+
- math.floor(math.log10(delta)))
618+
fmt = f"%-#.{sig_digits}g"
590619
return (
591-
"" if isinstance(value, np.ma.MaskedArray) and value.mask else
592620
self.fix_minus(
593-
locale.format_string("%-12g", (value,)) if self._useLocale else
594-
"%-12g" % value))
621+
locale.format_string(fmt, (value,)) if self._useLocale else
622+
fmt % value))
595623

596624
def format_data(self, value):
597625
# docstring inherited

0 commit comments

Comments
 (0)