Skip to content

Commit fcfea77

Browse files
authored
Merge pull request #20914 from deep-jkl/19195-rotated-markers
19195 rotated markers
2 parents d297e41 + 7101e3c commit fcfea77

File tree

6 files changed

+380
-21
lines changed

6 files changed

+380
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
New customization of MarkerStyle
2+
--------------------------------
3+
4+
New MarkerStyle parameters allow control of join style and cap style, and for
5+
the user to supply a transformation to be applied to the marker (e.g. a rotation).
6+
7+
.. plot::
8+
:include-source: true
9+
10+
import matplotlib.pyplot as plt
11+
from matplotlib.markers import MarkerStyle
12+
from matplotlib.transforms import Affine2D
13+
fig, ax = plt.subplots(figsize=(6, 1))
14+
fig.suptitle('New markers', fontsize=14)
15+
for col, (size, rot) in enumerate(zip([2, 5, 10], [0, 45, 90])):
16+
t = Affine2D().rotate_deg(rot).scale(size)
17+
ax.plot(col, 0, marker=MarkerStyle("*", transform=t))
18+
ax.axis("off")
19+
ax.set_xlim(-0.1, 2.4)

examples/lines_bars_and_markers/marker_reference.py

+95
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
.. redirect-from:: /gallery/shapes_and_collections/marker_path
2020
"""
2121

22+
from matplotlib.markers import MarkerStyle
2223
import matplotlib.pyplot as plt
2324
from matplotlib.lines import Line2D
25+
from matplotlib.transforms import Affine2D
2426

2527

2628
text_style = dict(horizontalalignment='right', verticalalignment='center',
@@ -159,3 +161,96 @@ def split_list(a_list):
159161
format_axes(ax)
160162

161163
plt.show()
164+
165+
###############################################################################
166+
# Advanced marker modifications with transform
167+
# ============================================
168+
#
169+
# Markers can be modified by passing a transform to the MarkerStyle
170+
# constructor. Following example shows how a supplied rotation is applied to
171+
# several marker shapes.
172+
173+
common_style = {k: v for k, v in filled_marker_style.items() if k != 'marker'}
174+
angles = [0, 10, 20, 30, 45, 60, 90]
175+
176+
fig, ax = plt.subplots()
177+
fig.suptitle('Rotated markers', fontsize=14)
178+
179+
ax.text(-0.5, 0, 'Filled marker', **text_style)
180+
for x, theta in enumerate(angles):
181+
t = Affine2D().rotate_deg(theta)
182+
ax.plot(x, 0, marker=MarkerStyle('o', 'left', t), **common_style)
183+
184+
ax.text(-0.5, 1, 'Un-filled marker', **text_style)
185+
for x, theta in enumerate(angles):
186+
t = Affine2D().rotate_deg(theta)
187+
ax.plot(x, 1, marker=MarkerStyle('1', 'left', t), **common_style)
188+
189+
ax.text(-0.5, 2, 'Equation marker', **text_style)
190+
for x, theta in enumerate(angles):
191+
t = Affine2D().rotate_deg(theta)
192+
eq = r'$\frac{1}{x}$'
193+
ax.plot(x, 2, marker=MarkerStyle(eq, 'left', t), **common_style)
194+
195+
for x, theta in enumerate(angles):
196+
ax.text(x, 2.5, f"{theta}°", horizontalalignment="center")
197+
format_axes(ax)
198+
199+
fig.tight_layout()
200+
plt.show()
201+
202+
###############################################################################
203+
# Setting marker cap style and join style
204+
# =======================================
205+
#
206+
# Markers have default cap and join styles, but these can be
207+
# customized when creating a MarkerStyle.
208+
209+
from matplotlib.markers import JoinStyle, CapStyle
210+
211+
marker_inner = dict(markersize=35,
212+
markerfacecolor='tab:blue',
213+
markerfacecoloralt='lightsteelblue',
214+
markeredgecolor='brown',
215+
markeredgewidth=8,
216+
)
217+
218+
marker_outer = dict(markersize=35,
219+
markerfacecolor='tab:blue',
220+
markerfacecoloralt='lightsteelblue',
221+
markeredgecolor='white',
222+
markeredgewidth=1,
223+
)
224+
225+
fig, ax = plt.subplots()
226+
fig.suptitle('Marker CapStyle', fontsize=14)
227+
fig.subplots_adjust(left=0.1)
228+
229+
for y, cap_style in enumerate(CapStyle):
230+
ax.text(-0.5, y, cap_style.name, **text_style)
231+
for x, theta in enumerate(angles):
232+
t = Affine2D().rotate_deg(theta)
233+
m = MarkerStyle('1', transform=t, capstyle=cap_style)
234+
ax.plot(x, y, marker=m, **marker_inner)
235+
ax.plot(x, y, marker=m, **marker_outer)
236+
ax.text(x, len(CapStyle) - .5, f'{theta}°', ha='center')
237+
format_axes(ax)
238+
plt.show()
239+
240+
###############################################################################
241+
# Modifying the join style:
242+
243+
fig, ax = plt.subplots()
244+
fig.suptitle('Marker JoinStyle', fontsize=14)
245+
fig.subplots_adjust(left=0.05)
246+
247+
for y, join_style in enumerate(JoinStyle):
248+
ax.text(-0.5, y, join_style.name, **text_style)
249+
for x, theta in enumerate(angles):
250+
t = Affine2D().rotate_deg(theta)
251+
m = MarkerStyle('*', transform=t, joinstyle=join_style)
252+
ax.plot(x, y, marker=m, **marker_inner)
253+
ax.text(x, len(JoinStyle) - .5, f'{theta}°', ha='center')
254+
format_axes(ax)
255+
256+
plt.show()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
==============================================
3+
Mapping marker properties to multivariate data
4+
==============================================
5+
6+
This example shows how to use different properties of markers to plot
7+
multivariate datasets. Here we represent a successful baseball throw as a
8+
smiley face with marker size mapped to the skill of thrower, marker rotation to
9+
the take-off angle, and thrust to the marker color.
10+
"""
11+
12+
import numpy as np
13+
import matplotlib.pyplot as plt
14+
from matplotlib.markers import MarkerStyle
15+
from matplotlib.transforms import Affine2D
16+
from matplotlib.textpath import TextPath
17+
from matplotlib.colors import Normalize
18+
19+
SUCCESS_SYMBOLS = [
20+
TextPath((0, 0), "☹"),
21+
TextPath((0, 0), "😒"),
22+
TextPath((0, 0), "☺"),
23+
]
24+
25+
N = 25
26+
np.random.seed(42)
27+
skills = np.random.uniform(5, 80, size=N) * 0.1 + 5
28+
takeoff_angles = np.random.normal(0, 90, N)
29+
thrusts = np.random.uniform(size=N)
30+
successfull = np.random.randint(0, 3, size=N)
31+
positions = np.random.normal(size=(N, 2)) * 5
32+
data = zip(skills, takeoff_angles, thrusts, successfull, positions)
33+
34+
cmap = plt.cm.get_cmap("plasma")
35+
fig, ax = plt.subplots()
36+
fig.suptitle("Throwing success", size=14)
37+
for skill, takeoff, thrust, mood, pos in data:
38+
t = Affine2D().scale(skill).rotate_deg(takeoff)
39+
m = MarkerStyle(SUCCESS_SYMBOLS[mood], transform=t)
40+
ax.plot(pos[0], pos[1], marker=m, color=cmap(thrust))
41+
fig.colorbar(plt.cm.ScalarMappable(norm=Normalize(0, 1), cmap=cmap),
42+
ax=ax, label="Normalized Thrust [a.u.]")
43+
ax.set_xlabel("X position [m]")
44+
ax.set_ylabel("Y position [m]")
45+
46+
plt.show()

lib/matplotlib/lines.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,10 @@ def __init__(self, xdata, ydata,
370370
self.set_color(color)
371371
if marker is None:
372372
marker = 'none' # Default.
373-
self._marker = MarkerStyle(marker, fillstyle)
373+
if not isinstance(marker, MarkerStyle):
374+
self._marker = MarkerStyle(marker, fillstyle)
375+
else:
376+
self._marker = marker
374377

375378
self._markevery = None
376379
self._markersize = None

0 commit comments

Comments
 (0)