From dc81103dec1cf80610011706753ba577092b99c9 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 16 Mar 2020 13:33:20 -0400 Subject: [PATCH] Backport PR #16770: Fix tuple markers --- lib/matplotlib/markers.py | 1 + lib/matplotlib/tests/test_marker.py | 107 ++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index 3a8d0bc7a9ea..0695fdfe2e04 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -347,6 +347,7 @@ def _set_tuple_marker(self): self._joinstyle = 'bevel' else: raise ValueError(f"Unexpected tuple marker: {marker}") + self._transform = Affine2D().scale(0.5).rotate_deg(rotation) def _set_mathtext_path(self): """ diff --git a/lib/matplotlib/tests/test_marker.py b/lib/matplotlib/tests/test_marker.py index 1ef9c18c47fb..e50746165792 100644 --- a/lib/matplotlib/tests/test_marker.py +++ b/lib/matplotlib/tests/test_marker.py @@ -1,6 +1,8 @@ import numpy as np +import matplotlib.pyplot as plt from matplotlib import markers from matplotlib.path import Path +from matplotlib.testing.decorators import check_figures_equal import pytest @@ -26,3 +28,108 @@ def test_marker_path(): path = Path([[0, 0], [1, 0]], [Path.MOVETO, Path.LINETO]) # Checking this doesn't fail. marker_style.set_marker(path) + + +class UnsnappedMarkerStyle(markers.MarkerStyle): + """ + A MarkerStyle where the snap threshold is force-disabled. + + This is used to compare to polygon/star/asterisk markers which do not have + any snap threshold set. + """ + def _recache(self): + super()._recache() + self._snap_threshold = None + + +@check_figures_equal() +def test_poly_marker(fig_test, fig_ref): + ax_test = fig_test.add_subplot() + ax_ref = fig_ref.add_subplot() + + # Note, some reference sizes must be different because they have unit + # *length*, while polygon markers are inscribed in a circle of unit + # *radius*. This introduces a factor of np.sqrt(2), but since size is + # squared, that becomes 2. + size = 20**2 + + # Squares + ax_test.scatter([0], [0], marker=(4, 0, 45), s=size) + ax_ref.scatter([0], [0], marker='s', s=size/2) + + # Diamonds, with and without rotation argument + ax_test.scatter([1], [1], marker=(4, 0), s=size) + ax_ref.scatter([1], [1], marker=UnsnappedMarkerStyle('D'), s=size/2) + ax_test.scatter([1], [1.5], marker=(4, 0, 0), s=size) + ax_ref.scatter([1], [1.5], marker=UnsnappedMarkerStyle('D'), s=size/2) + + # Pentagon, with and without rotation argument + ax_test.scatter([2], [2], marker=(5, 0), s=size) + ax_ref.scatter([2], [2], marker=UnsnappedMarkerStyle('p'), s=size) + ax_test.scatter([2], [2.5], marker=(5, 0, 0), s=size) + ax_ref.scatter([2], [2.5], marker=UnsnappedMarkerStyle('p'), s=size) + + # Hexagon, with and without rotation argument + ax_test.scatter([3], [3], marker=(6, 0), s=size) + ax_ref.scatter([3], [3], marker='h', s=size) + ax_test.scatter([3], [3.5], marker=(6, 0, 0), s=size) + ax_ref.scatter([3], [3.5], marker='h', s=size) + + # Rotated hexagon + ax_test.scatter([4], [4], marker=(6, 0, 30), s=size) + ax_ref.scatter([4], [4], marker='H', s=size) + + # Octagons + ax_test.scatter([5], [5], marker=(8, 0, 22.5), s=size) + ax_ref.scatter([5], [5], marker=UnsnappedMarkerStyle('8'), s=size) + + ax_test.set(xlim=(-0.5, 5.5), ylim=(-0.5, 5.5)) + ax_ref.set(xlim=(-0.5, 5.5), ylim=(-0.5, 5.5)) + + +def test_star_marker(): + # We don't really have a strict equivalent to this marker, so we'll just do + # a smoke test. + size = 20**2 + + fig, ax = plt.subplots() + ax.scatter([0], [0], marker=(5, 1), s=size) + ax.scatter([1], [1], marker=(5, 1, 0), s=size) + ax.set(xlim=(-0.5, 0.5), ylim=(-0.5, 1.5)) + + +# The asterisk marker is really a star with 0-size inner circle, so the ends +# are corners and get a slight bevel. The reference markers are just singular +# lines without corners, so they have no bevel, and we need to add a slight +# tolerance. +@check_figures_equal(tol=1.45) +def test_asterisk_marker(fig_test, fig_ref, request): + ax_test = fig_test.add_subplot() + ax_ref = fig_ref.add_subplot() + + # Note, some reference sizes must be different because they have unit + # *length*, while asterisk markers are inscribed in a circle of unit + # *radius*. This introduces a factor of np.sqrt(2), but since size is + # squared, that becomes 2. + size = 20**2 + + def draw_ref_marker(y, style, size): + # As noted above, every line is doubled. Due to antialiasing, these + # doubled lines make a slight difference in the .png results. + ax_ref.scatter([y], [y], marker=UnsnappedMarkerStyle(style), s=size) + if request.getfixturevalue('ext') == 'png': + ax_ref.scatter([y], [y], marker=UnsnappedMarkerStyle(style), + s=size) + + # Plus + ax_test.scatter([0], [0], marker=(4, 2), s=size) + draw_ref_marker(0, '+', size) + ax_test.scatter([0.5], [0.5], marker=(4, 2, 0), s=size) + draw_ref_marker(0.5, '+', size) + + # Cross + ax_test.scatter([1], [1], marker=(4, 2, 45), s=size) + draw_ref_marker(1, 'x', size/2) + + ax_test.set(xlim=(-0.5, 1.5), ylim=(-0.5, 1.5)) + ax_ref.set(xlim=(-0.5, 1.5), ylim=(-0.5, 1.5))