Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ca664a1

Browse filesBrowse files
marker-transforms
1 parent b42d6d9 commit ca664a1
Copy full SHA for ca664a1

File tree

Expand file treeCollapse file tree

3 files changed

+100
-13
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+100
-13
lines changed

‎examples/lines_bars_and_markers/scatter_piecharts.py

Copy file name to clipboardExpand all lines: examples/lines_bars_and_markers/scatter_piecharts.py
+57-7Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
Scatter plot with pie chart markers
44
===================================
55
6-
This example makes custom 'pie charts' as the markers for a scatter plot.
7-
8-
Thanks to Manuel Metz for the example.
6+
This example shows two methods to make custom 'pie charts' as the markers
7+
for a scatter plot.
98
"""
109

10+
##########################################################################
11+
# Manually creating marker vertices
12+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
#
14+
1115
import numpy as np
1216
import matplotlib.pyplot as plt
1317

14-
# first define the ratios
18+
# first define the cumulative ratios
1519
r1 = 0.2 # 20%
1620
r2 = r1 + 0.4 # 40%
1721

@@ -36,10 +40,54 @@
3640
s3 = np.abs(xy3).max()
3741

3842
fig, ax = plt.subplots()
39-
ax.scatter(range(3), range(3), marker=xy1, s=s1**2 * sizes, facecolor='blue')
40-
ax.scatter(range(3), range(3), marker=xy2, s=s2**2 * sizes, facecolor='green')
41-
ax.scatter(range(3), range(3), marker=xy3, s=s3**2 * sizes, facecolor='red')
43+
ax.scatter(range(3), range(3), marker=xy1, s=s1**2 * sizes, facecolor='C0')
44+
ax.scatter(range(3), range(3), marker=xy2, s=s2**2 * sizes, facecolor='C1')
45+
ax.scatter(range(3), range(3), marker=xy3, s=s3**2 * sizes, facecolor='C2')
46+
47+
plt.show()
48+
49+
50+
##########################################################################
51+
# Using wedges as markers
52+
# ~~~~~~~~~~~~~~~~~~~~~~~
53+
#
54+
# An alternative is to create custom markers from the `~.path.Path` of a
55+
# `~.patches.Wedge`, which might be more versatile.
56+
#
57+
58+
import numpy as np
59+
import matplotlib.pyplot as plt
60+
from matplotlib.patches import Wedge
61+
from matplotlib.markers import MarkerStyle
62+
63+
# first define the ratios
64+
r1 = 0.2 # 20%
65+
r2 = r1 + 0.3 # 50%
66+
r3 = 1 - r1 - r2 # 30%
67+
68+
def markers_from_ratios(ratios, width=1):
69+
markers = []
70+
angles = 360*np.concatenate(([0], np.cumsum(ratios)))
71+
for i in range(len(angles)-1):
72+
# create a Wedge within the unit square in between the given angles...
73+
w = Wedge((0, 0), 0.5, angles[i], angles[i+1], width=width/2)
74+
# ... and create a custom Marker from its path.
75+
markers.append(MarkerStyle(w.get_path(), normalize_path=False))
76+
return markers
77+
78+
# define some sizes of the scatter marker
79+
sizes = np.array([100, 200, 400, 800])
80+
# collect the markers and some colors
81+
markers = markers_from_ratios([r1, r2, r3], width=0.6)
82+
colors = plt.cm.tab10.colors[:len(markers)]
83+
84+
fig, ax = plt.subplots()
85+
86+
for marker, color in zip(markers, colors):
87+
ax.scatter(range(len(sizes)), range(len(sizes)), marker=marker, s=sizes,
88+
edgecolor="none", facecolor=color)
4289

90+
ax.margins(0.1)
4391
plt.show()
4492

4593
#############################################################################
@@ -55,3 +103,5 @@
55103
import matplotlib
56104
matplotlib.axes.Axes.scatter
57105
matplotlib.pyplot.scatter
106+
matplotlib.patches.Wedge
107+
matplotlib.markers.MarkerStyle

‎lib/matplotlib/markers.py

Copy file name to clipboardExpand all lines: lib/matplotlib/markers.py
+19-6Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ class MarkerStyle:
201201
# TODO: Is this ever used as a non-constant?
202202
_point_size_reduction = 0.5
203203

204-
def __init__(self, marker=None, fillstyle=None):
204+
def __init__(self, marker=None, fillstyle=None, normalize_path=True):
205205
"""
206206
Attributes
207207
----------
@@ -213,12 +213,19 @@ def __init__(self, marker=None, fillstyle=None):
213213
214214
Parameters
215215
----------
216-
marker : str or array-like, optional, default: None
216+
marker : str, array-like, `~.path.Path`, or `~.markers.MarkerStyle`,
217+
default: None
217218
See the descriptions of possible markers in the module docstring.
218219
219220
fillstyle : str, optional, default: 'full'
220221
'full', 'left", 'right', 'bottom', 'top', 'none'
222+
223+
normalize_path : bool, default: True
224+
Whether to normalize a custom paths that are provided as array of
225+
vertices or `~.path.Path`. If True, the marker is normalized to the
226+
unit square.
221227
"""
228+
self._normalize = normalize_path
222229
self._marker_function = None
223230
self.set_fillstyle(fillstyle)
224231
self.set_marker(marker)
@@ -302,6 +309,13 @@ def get_path(self):
302309

303310
def get_transform(self):
304311
return self._transform.frozen()
312+
313+
def set_transform(self, transform):
314+
"""
315+
Sets the transform of the marker. This is the transform by which the
316+
marker path is transformed.
317+
"""
318+
self._transform = transform
305319

306320
def get_alt_path(self):
307321
return self._alt_path
@@ -316,8 +330,9 @@ def _set_nothing(self):
316330
self._filled = False
317331

318332
def _set_custom_marker(self, path):
319-
rescale = np.max(np.abs(path.vertices)) # max of x's and y's.
320-
self._transform = Affine2D().scale(0.5 / rescale)
333+
if self._normalize:
334+
rescale = np.max(np.abs(path.vertices)) # max of x's and y's.
335+
self._transform = Affine2D().scale(0.5 / rescale)
321336
self._path = path
322337

323338
def _set_path_marker(self):
@@ -349,8 +364,6 @@ def _set_tuple_marker(self):
349364
def _set_mathtext_path(self):
350365
"""
351366
Draws mathtext markers '$...$' using TextPath object.
352-
353-
Submitted by tcb
354367
"""
355368
from matplotlib.text import TextPath
356369

‎lib/matplotlib/tests/test_marker.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_marker.py
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import numpy as np
22
from matplotlib import markers
33
from matplotlib.path import Path
4+
from matplotlib.transforms import Affine2D
5+
import matplotlib.pyplot as plt
46

7+
from matplotlib.testing.decorators import check_figures_equal
58
import pytest
69

710

@@ -26,3 +29,24 @@ def test_marker_path():
2629
path = Path([[0, 0], [1, 0]], [Path.MOVETO, Path.LINETO])
2730
# Checking this doesn't fail.
2831
marker_style.set_marker(path)
32+
33+
34+
@check_figures_equal(extensions=["png"])
35+
def test_marker_normalization(fig_test, fig_ref):
36+
plt.style.use("mpl20")
37+
38+
ax = fig_ref.subplots()
39+
ax.margins(0.3)
40+
ax.scatter([0, 1], [0, 0], s=400, marker="s", c="C2")
41+
42+
ax = fig_test.subplots()
43+
ax.margins(0.3)
44+
# test normalize
45+
p = Path([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]], closed=True)
46+
p1 = p.transformed(Affine2D().translate(-.5, -.5).scale(20))
47+
m1 = markers.MarkerStyle(p1, normalize_path=False)
48+
ax.scatter([0], [0], s=1, marker=m1, c="C2")
49+
# test transform
50+
m2 = markers.MarkerStyle("s")
51+
m2.set_transform(m2.get_transform() + Affine2D().scale(20))
52+
ax.scatter([1], [0], s=1, marker=m2, c="C2")

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.