| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- 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
- from matplotlib.transforms import Affine2D
- import pytest
- def test_marker_fillstyle():
- marker_style = markers.MarkerStyle(marker='o', fillstyle='none')
- assert marker_style.get_fillstyle() == 'none'
- assert not marker_style.is_filled()
- @pytest.mark.parametrize('marker', [
- 'o',
- 'x',
- '',
- 'None',
- r'$\frac{1}{2}$',
- "$\u266B$",
- 1,
- markers.TICKLEFT,
- [[-1, 0], [1, 0]],
- np.array([[-1, 0], [1, 0]]),
- Path([[0, 0], [1, 0]], [Path.MOVETO, Path.LINETO]),
- (5, 0), # a pentagon
- (7, 1), # a 7-pointed star
- (5, 2), # asterisk
- (5, 0, 10), # a pentagon, rotated by 10 degrees
- (7, 1, 10), # a 7-pointed star, rotated by 10 degrees
- (5, 2, 10), # asterisk, rotated by 10 degrees
- markers.MarkerStyle('o'),
- ])
- def test_markers_valid(marker):
- # Checking this doesn't fail.
- markers.MarkerStyle(marker)
- @pytest.mark.parametrize('marker', [
- 'square', # arbitrary string
- np.array([[-0.5, 0, 1, 2, 3]]), # 1D array
- (1,),
- (5, 3), # second parameter of tuple must be 0, 1, or 2
- (1, 2, 3, 4),
- ])
- def test_markers_invalid(marker):
- with pytest.raises(ValueError):
- markers.MarkerStyle(marker)
- 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))
- # The bullet mathtext marker is not quite a circle, so this is not a perfect match, but
- # it is close enough to confirm that the text-based marker is centred correctly. But we
- # still need a small tolerance to work around that difference.
- @check_figures_equal(extensions=['png'], tol=1.86)
- def test_text_marker(fig_ref, fig_test):
- ax_ref = fig_ref.add_subplot()
- ax_test = fig_test.add_subplot()
- ax_ref.plot(0, 0, marker=r'o', markersize=100, markeredgewidth=0)
- ax_test.plot(0, 0, marker=r'$\bullet$', markersize=100, markeredgewidth=0)
- @check_figures_equal()
- def test_marker_clipping(fig_ref, fig_test):
- # Plotting multiple markers can trigger different optimized paths in
- # backends, so compare single markers vs multiple to ensure they are
- # clipped correctly.
- marker_count = len(markers.MarkerStyle.markers)
- marker_size = 50
- ncol = 7
- nrow = marker_count // ncol + 1
- width = 2 * marker_size * ncol
- height = 2 * marker_size * nrow * 2
- fig_ref.set_size_inches((width / fig_ref.dpi, height / fig_ref.dpi))
- ax_ref = fig_ref.add_axes([0, 0, 1, 1])
- fig_test.set_size_inches((width / fig_test.dpi, height / fig_ref.dpi))
- ax_test = fig_test.add_axes([0, 0, 1, 1])
- for i, marker in enumerate(markers.MarkerStyle.markers):
- x = i % ncol
- y = i // ncol * 2
- # Singular markers per call.
- ax_ref.plot([x, x], [y, y + 1], c='k', linestyle='-', lw=3)
- ax_ref.plot(x, y, c='k',
- marker=marker, markersize=marker_size, markeredgewidth=10,
- fillstyle='full', markerfacecolor='white')
- ax_ref.plot(x, y + 1, c='k',
- marker=marker, markersize=marker_size, markeredgewidth=10,
- fillstyle='full', markerfacecolor='white')
- # Multiple markers in a single call.
- ax_test.plot([x, x], [y, y + 1], c='k', linestyle='-', lw=3,
- marker=marker, markersize=marker_size, markeredgewidth=10,
- fillstyle='full', markerfacecolor='white')
- ax_ref.set(xlim=(-0.5, ncol), ylim=(-0.5, 2 * nrow))
- ax_test.set(xlim=(-0.5, ncol), ylim=(-0.5, 2 * nrow))
- ax_ref.axis('off')
- ax_test.axis('off')
- def test_marker_init_transforms():
- """Test that initializing marker with transform is a simple addition."""
- marker = markers.MarkerStyle("o")
- t = Affine2D().translate(1, 1)
- t_marker = markers.MarkerStyle("o", transform=t)
- assert marker.get_transform() + t == t_marker.get_transform()
- def test_marker_init_joinstyle():
- marker = markers.MarkerStyle("*")
- styled_marker = markers.MarkerStyle("*", joinstyle="round")
- assert styled_marker.get_joinstyle() == "round"
- assert marker.get_joinstyle() != "round"
- def test_marker_init_captyle():
- marker = markers.MarkerStyle("*")
- styled_marker = markers.MarkerStyle("*", capstyle="round")
- assert styled_marker.get_capstyle() == "round"
- assert marker.get_capstyle() != "round"
- @pytest.mark.parametrize("marker,transform,expected", [
- (markers.MarkerStyle("o"), Affine2D().translate(1, 1),
- Affine2D().translate(1, 1)),
- (markers.MarkerStyle("o", transform=Affine2D().translate(1, 1)),
- Affine2D().translate(1, 1), Affine2D().translate(2, 2)),
- (markers.MarkerStyle("$|||$", transform=Affine2D().translate(1, 1)),
- Affine2D().translate(1, 1), Affine2D().translate(2, 2)),
- (markers.MarkerStyle(
- markers.TICKLEFT, transform=Affine2D().translate(1, 1)),
- Affine2D().translate(1, 1), Affine2D().translate(2, 2)),
- ])
- def test_marker_transformed(marker, transform, expected):
- new_marker = marker.transformed(transform)
- assert new_marker is not marker
- assert new_marker.get_user_transform() == expected
- assert marker._user_transform is not new_marker._user_transform
- def test_marker_rotated_invalid():
- marker = markers.MarkerStyle("o")
- with pytest.raises(ValueError):
- new_marker = marker.rotated()
- with pytest.raises(ValueError):
- new_marker = marker.rotated(deg=10, rad=10)
- @pytest.mark.parametrize("marker,deg,rad,expected", [
- (markers.MarkerStyle("o"), 10, None, Affine2D().rotate_deg(10)),
- (markers.MarkerStyle("o"), None, 0.01, Affine2D().rotate(0.01)),
- (markers.MarkerStyle("o", transform=Affine2D().translate(1, 1)),
- 10, None, Affine2D().translate(1, 1).rotate_deg(10)),
- (markers.MarkerStyle("o", transform=Affine2D().translate(1, 1)),
- None, 0.01, Affine2D().translate(1, 1).rotate(0.01)),
- (markers.MarkerStyle("$|||$", transform=Affine2D().translate(1, 1)),
- 10, None, Affine2D().translate(1, 1).rotate_deg(10)),
- (markers.MarkerStyle(
- markers.TICKLEFT, transform=Affine2D().translate(1, 1)),
- 10, None, Affine2D().translate(1, 1).rotate_deg(10)),
- ])
- def test_marker_rotated(marker, deg, rad, expected):
- new_marker = marker.rotated(deg=deg, rad=rad)
- assert new_marker is not marker
- assert new_marker.get_user_transform() == expected
- assert marker._user_transform is not new_marker._user_transform
- def test_marker_scaled():
- marker = markers.MarkerStyle("1")
- new_marker = marker.scaled(2)
- assert new_marker is not marker
- assert new_marker.get_user_transform() == Affine2D().scale(2)
- assert marker._user_transform is not new_marker._user_transform
- new_marker = marker.scaled(2, 3)
- assert new_marker is not marker
- assert new_marker.get_user_transform() == Affine2D().scale(2, 3)
- assert marker._user_transform is not new_marker._user_transform
- marker = markers.MarkerStyle("1", transform=Affine2D().translate(1, 1))
- new_marker = marker.scaled(2)
- assert new_marker is not marker
- expected = Affine2D().translate(1, 1).scale(2)
- assert new_marker.get_user_transform() == expected
- assert marker._user_transform is not new_marker._user_transform
- def test_alt_transform():
- m1 = markers.MarkerStyle("o", "left")
- m2 = markers.MarkerStyle("o", "left", Affine2D().rotate_deg(90))
- assert m1.get_alt_transform().rotate_deg(90) == m2.get_alt_transform()
|