1
1
"""
2
- =============================
2
+ ============================
3
3
Scale invariant angle marker
4
- =============================
4
+ ============================
5
5
6
- This example shows how to create a scale invariant angle marker.
7
- It is often useful to mark angles between lines or inside shapes with a
8
- circular arc. While matplotlib provides an `~.patches.Arc`, an inherent problem
9
- when directly using it for such purpose is that an arc being circular in
10
- data space is not necessarily circular in display space. Also, the arc's radius
11
- is often best defined in a coordinate system which is independent on the actual
12
- data coordinates - at least if you want to be able to freely zoom into your
13
- plot without the marker growing to infinity.
6
+ This example shows how to create a scale invariant angle marker. It is often
7
+ useful to mark angles between lines or inside shapes with a circular arc. While
8
+ Matplotlib provides an `~.patches.Arc`, an inherent problem when directly using
9
+ it for such purposes is that an arc being circular in data space is not
10
+ necessarily circular in display space. Also, the arc's radius is often best
11
+ defined in a coordinate system which is independent of the actual data
12
+ coordinates - at least if you want to be able to freely zoom into your plot
13
+ without the marker growing to infinity.
14
14
15
- This calls for a solution where the arc's center is defined in data space,
16
- but its radius in a physical unit like points or pixels, or as a ratio of the
17
- axes dimension. The following ``AngleMarker`` class provides such solution.
15
+ This calls for a solution where the arc's center is defined in data space, but
16
+ its radius in a physical unit like points or pixels, or as a ratio of the Axes
17
+ dimension. The following ``AngleMarker`` class provides such solution.
18
18
19
19
The example below serves two purposes:
20
20
21
- * It provides a read -to-use solution for the problem of easily drawing angles
21
+ * It provides a ready -to-use solution for the problem of easily drawing angles
22
22
in graphs.
23
- * It shows how to subclass a matplotlib artist to enhance its functionality, as
24
- well as giving a hands-on example on how to use matplotlib's
25
- :doc:`transform system </tutorials/advanced/transforms_tutorial>`.
23
+ * It shows how to subclass a Matplotlib artist to enhance its functionality, as
24
+ well as giving a hands-on example on how to use Matplotlib's :doc:`transform
25
+ system </tutorials/advanced/transforms_tutorial>`.
26
26
27
27
If mainly interested in the former, you may copy the below class and jump to
28
28
the :ref:`angle-marker-usage` section.
32
32
# AngleMarker class
33
33
# ~~~~~~~~~~~~~~~~~
34
34
# The essential idea here is to subclass `~.patches.Arc` and set its transform
35
- # to the `~.transforms.IdentityTransform`. The parameters of the arc are hence
35
+ # to the `~.transforms.IdentityTransform`, making the parameters of the arc
36
36
# defined in pixel space.
37
- # We then override the ``Arc``'s attributes ``_center``,
38
- # ``theta1``, `` theta2``, ``width`` and ``height`` and make them properties.
39
- # They are coupled to internal methods that calculate the respective
40
- # parameters each time the attribute is accessed and thereby ensure that the
41
- # arc in pixel space stays synchronized with the input points and size.
42
- # For example, each time the arc's drawing method would query its
43
- # ``_center`` attribute, instead of receiving the same number all over again,
44
- # it will instead receive the result of the ``get_center_in_pixels`` method we
45
- # defined in the subclass. This method transforms the center in data
46
- # coordinates to pixels via the axes transform ``ax.transData``. The
47
- # size and the angles are calculated in a similar fashion, such that the arc
48
- # changes its shape automatically when e.g. zooming or panning interactively.
37
+ # We then override the ``Arc``'s attributes ``_center``, ``theta1``,
38
+ # ``theta2``, ``width`` and ``height`` and make them properties, coupling to
39
+ # internal methods that calculate the respective parameters each time the
40
+ # attribute is accessed and thereby ensuring that the arc in pixel space stays
41
+ # synchronized with the input points and size.
42
+ # For example, each time the arc's drawing method would query its ``_center``
43
+ # attribute, instead of receiving the same number all over again, it will
44
+ # instead receive the result of the ``get_center_in_pixels`` method we defined
45
+ # in the subclass. This method transforms the center in data coordinates to
46
+ # pixels via the Axes transform ``ax.transData``. The size and the angles are
47
+ # calculated in a similar fashion, such that the arc changes its shape
48
+ # automatically when e.g. zooming or panning interactively.
49
49
#
50
50
# The functionality of this class allows to annotate the arc with a text. This
51
- # text is a `~.text.Annotation` stored in an attribute ``text``.
52
- # Since the arc's position and radius are defined only at draw time, we need to
53
- # update the text's position accordingly. This is done by reimplementing the
54
- # ``Arc``'s `` draw()`` method to let it call an updating method for the text.
51
+ # text is a `~.text.Annotation` stored in an attribute ``text``. Since the
52
+ # arc's position and radius are defined only at draw time, we need to update
53
+ # the text's position accordingly. This is done by reimplementing the ``Arc``'s
54
+ # ``draw()`` method to let it call an updating method for the text.
55
55
#
56
- # The arc and the text will be added to the provided axes at instantiation: it
56
+ # The arc and the text will be added to the provided Axes at instantiation: it
57
57
# is hence not strictly necessary to keep a reference to it.
58
58
59
59
@@ -71,40 +71,40 @@ class AngleMarker(Arc):
71
71
def __init__ (self , xy , p1 , p2 , size = 75 , unit = "points" , ax = None ,
72
72
text = "" , textposition = "inside" , text_kw = {}, ** kwargs ):
73
73
"""
74
- Params
75
- ------
74
+ Parameters
75
+ ----------
76
76
xy, p1, p2 : tuple or array of two floats
77
- center position and two points. Angle marker is drawn between the
78
- two vectors connecting p1 and p2 with xy , respectively. Units
77
+ Center position and two points. Angle marker is drawn between the
78
+ two vectors connecting *p1* and *p2* with *xy* , respectively. Units
79
79
are data coordinates.
80
80
81
81
size : float
82
- diameter of the angle marker in units specified by `` unit`` .
82
+ Diameter of the angle marker in units specified by * unit* .
83
83
84
84
unit : string
85
- One of the following strings to specify the unit of `` size`` :
86
- * "pixels" : pixels
87
- * "points" : points, use points instead of pixels to not have a
88
- dependence of the dpi
89
- * "axes width", "axes height" : relative units of axes
90
- width, height
91
- * "axes min", "axes max" : minimum or maximum of relative axes
92
- width, height
85
+ One of the following strings to specify the unit of * size* :
86
+
87
+ * "pixels": pixels
88
+ * "points": points, use points instead of pixels to not have a
89
+ dependence on the DPI
90
+ * "axes width", "axes height": relative units of Axes width, height
91
+ * "axes min", "axes max": minimum or maximum of relative Axes
92
+ width, height
93
93
94
94
ax : `matplotlib.axes.Axes`
95
- The axes to add the angle marker to
95
+ The Axes to add the angle marker to.
96
96
97
97
text : string
98
98
The text to mark the angle with.
99
99
100
- textposition : [ "inside", "outside", "edge"]
100
+ textposition : { "inside", "outside", "edge"}
101
101
Whether to show the text in- or outside the arc. "edge" can be used
102
102
for custom positions anchored at the arc's edge.
103
103
104
104
text_kw : dict
105
105
Dictionary of arguments passed to the Annotation.
106
106
107
- kwargs :
107
+ ** kwargs
108
108
Further parameters are passed to `matplotlib.patches.Arc`. Use this
109
109
to specify, color, linewidth etc. of the arc.
110
110
@@ -218,14 +218,13 @@ def R(a, r, w, h):
218
218
# Usage
219
219
# ~~~~~
220
220
#
221
- # Required arguments to ``AngleMarker`` are the center of the arc, ``xy``,
222
- # and two points, such that the arc spans between the two vectors
223
- # connecting p1 and p2 with xy, respectively. Those are given in data
224
- # coordinates.
225
- # Further arguments are the ``size`` of the arc and its ``unit``.
226
- # Additionally, a ``text`` can be specified, that will be drawn either in- or
227
- # outside of the arc, according to the value of ``textposition``.
228
- # Usage of those arguments is shown below.
221
+ # Required arguments to ``AngleMarker`` are the center of the arc, *xy*, and
222
+ # two points, such that the arc spans between the two vectors connecting *p1*
223
+ # and *p2* with *xy*, respectively. Those are given in data coordinates.
224
+ # Further arguments are the *size* of the arc and its *unit*. Additionally, a
225
+ # *text* can be specified, that will be drawn either in- or outside of the arc,
226
+ # according to the value of *textposition*. Usage of those arguments is shown
227
+ # below.
229
228
230
229
fig , (ax1 , ax2 , ax3 ) = plt .subplots (nrows = 3 , sharex = True , figsize = (7 , 7 ),
231
230
gridspec_kw = dict (height_ratios = (3 , 1 , 1 )))
@@ -249,7 +248,7 @@ def R(a, r, w, h):
249
248
am4 = AngleMarker (center , p2 [0 ], p1 [1 ], ax = ax1 , size = 35 , text = r"$\theta$" )
250
249
251
250
252
- # Showcase some styling options for the angle arc, as well as the text
251
+ # Showcase some styling options for the angle arc, as well as the text.
253
252
p = [(6.0 , 400 ), (5.3 , 410 ), (5.6 , 300 )]
254
253
ax1 .plot (* zip (* p ))
255
254
am5 = AngleMarker (p [1 ], p [0 ], p [2 ], ax = ax1 , size = 40 , text = r"$\Phi$" ,
@@ -258,14 +257,14 @@ def R(a, r, w, h):
258
257
259
258
260
259
#### SUBPLOT 2 ####
261
- # Helper function to draw angle easily
260
+ # Helper function to draw angle easily.
262
261
def plot_angle (ax , pos , angle , length = 0.95 , acol = "C0" , ** kwargs ):
263
262
vec2 = np .array ([np .cos (np .deg2rad (angle )), np .sin (np .deg2rad (angle ))])
264
263
xy = np .c_ [[length , 0 ], [0 , 0 ], vec2 * length ].T + np .array (pos )
265
264
ax .plot (* xy .T , color = acol )
266
265
return AngleMarker (pos , xy [0 ], xy [2 ], ax = ax , ** kwargs )
267
266
268
- # Showcase different textpositions
267
+ # Showcase different text positions.
269
268
kw = dict (size = 75 , unit = "points" , text = r"$60°$" )
270
269
271
270
am6 = plot_angle (ax2 , (2.0 , 0 ), 60 , textposition = "inside" , ** kw )
@@ -276,8 +275,8 @@ def plot_angle(ax, pos, angle, length=0.95, acol="C0", **kwargs):
276
275
text_kw = dict (xytext = (30 , 20 ), arrowprops = dict (arrowstyle = "->" ,
277
276
connectionstyle = "arc3,rad=-0.2" )), ** kw )
278
277
279
- ax2 .annotate ("textpostion " , xy = (.02 , .9 ), xycoords = "axes fraction" ,
280
- bbox = dict (boxstyle = "round" , fc = "w" ), ha = "left" )
278
+ ax2 .annotate ("textposition " , xy = (.02 , 1 ), xycoords = "axes fraction" ,
279
+ bbox = dict (boxstyle = "round" , fc = "w" ), ha = "left" , va = "center " )
281
280
for x , text in zip ([2.0 , 3.5 , 5.0 , 6.5 ], ['"inside"' , '"outside"' , '"edge"' ,
282
281
'"edge", custom arrow' ]):
283
282
ax2 .annotate (text , xy = (x , 0 ), xycoords = ax2 .get_xaxis_transform (),
@@ -293,14 +292,13 @@ def plot_angle(ax, pos, angle, length=0.95, acol="C0", **kwargs):
293
292
am12 = plot_angle (ax3 , (5.0 , 0 ), 60 , size = 0.25 , unit = "axes min" , ** kw )
294
293
am13 = plot_angle (ax3 , (6.5 , 0 ), 60 , size = 0.25 , unit = "axes max" , ** kw )
295
294
296
- ax3 .annotate ("unit" , xy = (.02 , .9 ), xycoords = "axes fraction" ,
297
- bbox = dict (boxstyle = "round" , fc = "w" ), ha = "left" )
295
+ ax3 .annotate ("unit" , xy = (.02 , 1 ), xycoords = "axes fraction" ,
296
+ bbox = dict (boxstyle = "round" , fc = "w" ), ha = "left" , va = "center " )
298
297
for x , text in zip ([2.0 , 3.5 , 5.0 , 6.5 ], ['"pixels"' , '"points"' ,
299
298
'"axes min"' , '"axes max"' ]):
300
299
ax3 .annotate (text , xy = (x , 0 ), xycoords = ax3 .get_xaxis_transform (),
301
300
bbox = dict (boxstyle = "round" , fc = "w" ), ha = "left" , fontsize = 8 )
302
301
303
- fig .tight_layout ()
304
302
plt .show ()
305
303
306
304
0 commit comments