|
18 | 18 | ``ax`` is a :class:`~matplotlib.axes.Axes` instance, and ``fig`` is a |
19 | 19 | :class:`~matplotlib.figure.Figure` instance. |
20 | 20 |
|
21 | | -+-----------+-----------------------------+-----------------------------------+ |
22 | | -|Coordinates|Transformation object |Description | |
23 | | -+-----------+-----------------------------+-----------------------------------+ |
24 | | -|"data" |``ax.transData`` |The coordinate system for the data,| |
25 | | -| | |controlled by xlim and ylim. | |
26 | | -+-----------+-----------------------------+-----------------------------------+ |
27 | | -|"axes" |``ax.transAxes`` |The coordinate system of the | |
28 | | -| | |`~matplotlib.axes.Axes`; (0, 0) | |
29 | | -| | |is bottom left of the axes, and | |
30 | | -| | |(1, 1) is top right of the axes. | |
31 | | -+-----------+-----------------------------+-----------------------------------+ |
32 | | -|"figure" |``fig.transFigure`` |The coordinate system of the | |
33 | | -| | |`.Figure`; (0, 0) is bottom left | |
34 | | -| | |of the figure, and (1, 1) is top | |
35 | | -| | |right of the figure. | |
36 | | -+-----------+-----------------------------+-----------------------------------+ |
37 | | -|"display" |``None``, or |The pixel coordinate system of the | |
38 | | -| |``IdentityTransform()`` |display; (0, 0) is bottom left of | |
39 | | -| | |the display, and (width, height) is| |
40 | | -| | |top right of the display in pixels.| |
41 | | -+-----------+-----------------------------+-----------------------------------+ |
42 | | -|"xaxis", |``ax.get_xaxis_transform()``,|Blended coordinate systems; use | |
43 | | -|"yaxis" |``ax.get_yaxis_transform()`` |data coordinates on one of the axis| |
44 | | -| | |and axes coordinates on the other. | |
45 | | -+-----------+-----------------------------+-----------------------------------+ |
| 21 | ++----------------+-----------------------------+-----------------------------------+ |
| 22 | +|Coordinates |Transformation object |Description | |
| 23 | ++================+=============================+===================================+ |
| 24 | +|"data" |``ax.transData`` |The coordinate system for the data,| |
| 25 | +| | |controlled by xlim and ylim. | |
| 26 | ++----------------+-----------------------------+-----------------------------------+ |
| 27 | +|"axes" |``ax.transAxes`` |The coordinate system of the | |
| 28 | +| | |`~matplotlib.axes.Axes`; (0, 0) | |
| 29 | +| | |is bottom left of the axes, and | |
| 30 | +| | |(1, 1) is top right of the axes. | |
| 31 | ++----------------+-----------------------------+-----------------------------------+ |
| 32 | +|"figure" |``fig.transFigure`` |The coordinate system of the | |
| 33 | +| | |`.Figure`; (0, 0) is bottom left | |
| 34 | +| | |of the figure, and (1, 1) is top | |
| 35 | +| | |right of the figure. | |
| 36 | ++----------------+-----------------------------+-----------------------------------+ |
| 37 | +|"figure-inches" |``fig.dpi_scale_trans`` |The coordinate system of the | |
| 38 | +| | |`.Figure` in inches; (0, 0) is | |
| 39 | +| | |bottom left of the figure, and | |
| 40 | +| | |(width, height) is the top right | |
| 41 | +| | |of the figure in inches. | |
| 42 | ++----------------+-----------------------------+-----------------------------------+ |
| 43 | +|"display" |``None``, or |The pixel coordinate system of the | |
| 44 | +| |``IdentityTransform()`` |display window; (0, 0) is bottom | |
| 45 | +| | |left of the window, and (width, | |
| 46 | +| | |height) is top right of the | |
| 47 | +| | |display window in pixels. | |
| 48 | ++----------------+-----------------------------+-----------------------------------+ |
| 49 | +|"xaxis", |``ax.get_xaxis_transform()``,|Blended coordinate systems; use | |
| 50 | +|"yaxis" |``ax.get_yaxis_transform()`` |data coordinates on one of the axis| |
| 51 | +| | |and axes coordinates on the other. | |
| 52 | ++----------------+-----------------------------+-----------------------------------+ |
46 | 53 |
|
47 | 54 | All of the transformation objects in the table above take inputs in |
48 | | -their coordinate system, and transform the input to the `display` |
49 | | -coordinate system. That is why the `display` coordinate system has |
50 | | -`None` for the `Transformation Object` column -- it already is in |
| 55 | +their coordinate system, and transform the input to the ``display`` |
| 56 | +coordinate system. That is why the ``display`` coordinate system has |
| 57 | +``None`` for the ``Transformation Object`` column -- it already is in |
51 | 58 | display coordinates. The transformations also know how to invert |
52 | | -themselves, to go from `display` back to the native coordinate system. |
| 59 | +themselves, to go from ``display`` back to the native coordinate system. |
53 | 60 | This is particularly useful when processing events from the user |
54 | 61 | interface, which typically occur in display space, and you want to |
55 | 62 | know where the mouse click or key-press occurred in your data |
56 | 63 | coordinate system. |
57 | 64 |
|
| 65 | +Note that specifying objects in ``display`` coordinates will change their |
| 66 | +location if the ``dpi`` of the figure changes. This can cause confusion when |
| 67 | +printing or changing screen resolution, because the object can change location |
| 68 | +and size. Therefore it is most common |
| 69 | +for artists placed in an axes or figure to have their transform set to |
| 70 | +something *other* than the `~.transforms.IdentityTransform()`; the default when |
| 71 | +an artist is placed on an axes using `~.Axes.axes.add_artist` is for the |
| 72 | +transform to be ``ax.transData``. |
| 73 | +
|
58 | 74 | .. _data-coords: |
59 | 75 |
|
60 | 76 | Data coordinates |
|
71 | 87 |
|
72 | 88 | import numpy as np |
73 | 89 | import matplotlib.pyplot as plt |
| 90 | +import matplotlib.patches as mpatches |
74 | 91 |
|
75 | 92 | x = np.arange(0, 10, 0.005) |
76 | 93 | y = np.exp(-x/2.) * np.sin(2*np.pi*x) |
|
143 | 160 | (xdata, ydata), xytext=(-2*offset, offset), textcoords='offset points', |
144 | 161 | bbox=bbox, arrowprops=arrowprops) |
145 | 162 |
|
146 | | - |
147 | 163 | disp = ax.annotate('display = (%.1f, %.1f)' % (xdisplay, ydisplay), |
148 | 164 | (xdisplay, ydisplay), xytext=(0.5*offset, -offset), |
149 | 165 | xycoords='figure pixels', |
150 | 166 | textcoords='offset points', |
151 | 167 | bbox=bbox, arrowprops=arrowprops) |
152 | 168 |
|
153 | | - |
154 | 169 | plt.show() |
155 | 170 |
|
156 | 171 | ############################################################################### |
|
229 | 244 | # move, but the circle will remain fixed because it is not in `data` |
230 | 245 | # coordinates and will always remain at the center of the axes. |
231 | 246 |
|
232 | | -import matplotlib.patches as patches |
233 | | - |
234 | 247 | fig = plt.figure() |
235 | 248 | ax = fig.add_subplot(111) |
236 | 249 | x, y = 10*np.random.rand(2, 1000) |
237 | | -ax.plot(x, y, 'go') # plot some data in data coordinates |
| 250 | +ax.plot(x, y, 'go', alpha=0.2) # plot some data in data coordinates |
238 | 251 |
|
239 | | -circ = patches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes, |
240 | | - facecolor='yellow', alpha=0.5) |
| 252 | +circ = mpatches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes, |
| 253 | + facecolor='blue', alpha=0.75) |
241 | 254 | ax.add_patch(circ) |
242 | 255 | plt.show() |
243 | 256 |
|
|
281 | 294 | # highlight the 1..2 stddev region with a span. |
282 | 295 | # We want x to be in data coordinates and y to |
283 | 296 | # span from 0..1 in axes coords |
284 | | -rect = patches.Rectangle((1, 0), width=1, height=1, |
| 297 | +rect = mpatches.Rectangle((1, 0), width=1, height=1, |
285 | 298 | transform=trans, color='yellow', |
286 | 299 | alpha=0.5) |
287 | 300 |
|
|
303 | 316 | # |
304 | 317 | # trans = ax.get_xaxis_transform() |
305 | 318 | # |
| 319 | +# .. _transforms-fig-scale-dpi: |
| 320 | +# |
| 321 | +# Plotting in physical units |
| 322 | +# ========================== |
| 323 | +# |
| 324 | +# Sometimes we want an object to be a certain physical size on the plot. |
| 325 | +# Here we draw the same circle as above, but in physical units. If done |
| 326 | +# interactively, you can see that changing the size of the figure does |
| 327 | +# not change the offset of the circle from the lower-left corner, |
| 328 | +# does not change its size, and the circle remains a circle regardless of |
| 329 | +# the aspect ratio of the axes. |
| 330 | + |
| 331 | +fig, ax = plt.subplots(figsize=(5, 4)) |
| 332 | +x, y = 10*np.random.rand(2, 1000) |
| 333 | +ax.plot(x, y*10., 'go', alpha=0.2) # plot some data in data coordinates |
| 334 | +# add a circle in fixed-units |
| 335 | +circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans, |
| 336 | + facecolor='blue', alpha=0.75) |
| 337 | +ax.add_patch(circ) |
| 338 | +plt.show() |
| 339 | + |
| 340 | +############################################################################### |
| 341 | +# If we change the figure size, the circle does not change its absolute |
| 342 | +# position and is cropped. |
| 343 | + |
| 344 | +fig, ax = plt.subplots(figsize=(7, 2)) |
| 345 | +x, y = 10*np.random.rand(2, 1000) |
| 346 | +ax.plot(x, y*10., 'go', alpha=0.2) # plot some data in data coordinates |
| 347 | +# add a circle in fixed-units |
| 348 | +circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans, |
| 349 | + facecolor='blue', alpha=0.75) |
| 350 | +ax.add_patch(circ) |
| 351 | +plt.show() |
| 352 | + |
| 353 | +############################################################################### |
| 354 | +# Another use is putting a patch with a set physical dimension around a |
| 355 | +# data point on the axes. Here we add together two transforms. The |
| 356 | +# first sets the scaling of how large the ellipse should be and the second |
| 357 | +# sets its position. The ellipse is then placed at the origin, and then |
| 358 | +# we use the helper transform :class:`~matplotlib.transforms.ScaledTranslation` |
| 359 | +# to move it |
| 360 | +# to the right place in the ``ax.transData`` coordinate system. |
| 361 | +# This helper is instantiated with:: |
| 362 | +# |
| 363 | +# trans = ScaledTranslation(xt, yt, scale_trans) |
| 364 | +# |
| 365 | +# where `xt` and `yt` are the translation offsets, and `scale_trans` is |
| 366 | +# a transformation which scales `xt` and `yt` at transformation time |
| 367 | +# before applying the offsets. |
| 368 | +# |
| 369 | +# Note the use of the plus operator on the transforms below. |
| 370 | +# This code says: first apply the scale transformation ``fig.dpi_scale_trans`` |
| 371 | +# to make the ellipse the proper size, but still centered at (0, 0), |
| 372 | +# and then translate the data to `xdata[0]` and `ydata[0]` in data space. |
| 373 | +# |
| 374 | +# In interactive use, the ellipse stays the same size even if the |
| 375 | +# axes limits are changed via zoom. |
| 376 | +# |
| 377 | + |
| 378 | +fig, ax = plt.subplots() |
| 379 | +xdata, ydata = (0.2, 0.7), (0.5, 0.5) |
| 380 | +ax.plot(xdata, ydata, "o") |
| 381 | +ax.set_xlim((0, 1)) |
| 382 | + |
| 383 | +trans = (fig.dpi_scale_trans + |
| 384 | + transforms.ScaledTranslation(xdata[0], ydata[0], ax.transData)) |
| 385 | + |
| 386 | +# plot an ellipse around the point that is 150 x 130 points in diameter... |
| 387 | +circle = mpatches.Ellipse((0, 0), 150/72, 130/72, angle=40, |
| 388 | + fill=None, transform=trans) |
| 389 | +ax.add_patch(circle) |
| 390 | +plt.show() |
| 391 | + |
| 392 | +############################################################################### |
| 393 | +# .. note:: |
| 394 | +# |
| 395 | +# The order of transformation matters. Here the ellipse |
| 396 | +# is given the right dimensions in display space *first* and then moved |
| 397 | +# in data space to the correct spot. |
| 398 | +# If we had done the ``ScaledTranslation`` first, then |
| 399 | +# ``xdata[0]`` and ``ydata[0]`` would |
| 400 | +# first be transformed to ``display`` coordinates (``[ 358.4 475.2]`` on |
| 401 | +# a 200-dpi monitor) and then those coordinates |
| 402 | +# would be scaled by ``fig.dpi_scale_trans`` pushing the center of |
| 403 | +# the ellipse well off the screen (i.e. ``[ 71680. 95040.]``). |
| 404 | +# |
306 | 405 | # .. _offset-transforms-shadow: |
307 | 406 | # |
308 | 407 | # Using offset transforms to create a shadow effect |
309 | 408 | # ================================================= |
310 | 409 | # |
311 | | -# One use of transformations is to create a new transformation that is |
| 410 | +# Another use of :class:`~matplotlib.transforms.ScaledTranslation` is to create |
| 411 | +# a new transformation that is |
312 | 412 | # offset from another transformation, e.g., to place one object shifted a |
313 | 413 | # bit relative to another object. Typically you want the shift to be in |
314 | 414 | # some physical dimension, like points or inches rather than in data |
|
318 | 418 | # One use for an offset is to create a shadow effect, where you draw one |
319 | 419 | # object identical to the first just to the right of it, and just below |
320 | 420 | # it, adjusting the zorder to make sure the shadow is drawn first and |
321 | | -# then the object it is shadowing above it. The transforms module has a |
322 | | -# helper transformation |
323 | | -# :class:`~matplotlib.transforms.ScaledTranslation`. It is |
324 | | -# instantiated with:: |
| 421 | +# then the object it is shadowing above it. |
325 | 422 | # |
326 | | -# trans = ScaledTranslation(xt, yt, scale_trans) |
327 | | -# |
328 | | -# where `xt` and `yt` are the translation offsets, and `scale_trans` is |
329 | | -# a transformation which scales `xt` and `yt` at transformation time |
330 | | -# before applying the offsets. A typical use case is to use the figure |
331 | | -# ``fig.dpi_scale_trans`` transformation for the `scale_trans` argument, |
332 | | -# to first scale `xt` and `yt` specified in points to `display` space |
333 | | -# before doing the final offset. The dpi and inches offset is a |
334 | | -# common-enough use case that we have a special helper function to |
335 | | -# create it in :func:`matplotlib.transforms.offset_copy`, which returns |
336 | | -# a new transform with an added offset. But in the example below, we'll |
337 | | -# create the offset transform ourselves. Note the use of the plus |
338 | | -# operator in:: |
339 | | -# |
340 | | -# offset = transforms.ScaledTranslation(dx, dy, |
341 | | -# fig.dpi_scale_trans) |
342 | | -# shadow_transform = ax.transData + offset |
343 | | -# |
344 | | -# showing that can chain transformations using the addition operator. |
345 | | -# This code says: first apply the data transformation ``ax.transData`` |
346 | | -# and then translate the data by `dx` and `dy` points. In typography, |
| 423 | +# Here we apply the transforms in the *opposite* order to the use of |
| 424 | +# :class:`~matplotlib.transforms.ScaledTranslation` above. The plot is |
| 425 | +# first made in data units (``ax.transData``) and then shifted by |
| 426 | +# ``dx`` and ``dy`` points using `fig.dpi_scale_trans`. (In typography, |
347 | 427 | # a`point <https://en.wikipedia.org/wiki/Point_%28typography%29>`_ is |
348 | 428 | # 1/72 inches, and by specifying your offsets in points, your figure |
349 | | -# will look the same regardless of the dpi resolution it is saved in. |
| 429 | +# will look the same regardless of the dpi resolution it is saved in.) |
350 | 430 |
|
351 | | -fig = plt.figure() |
352 | | -ax = fig.add_subplot(111) |
| 431 | +fig, ax = plt.subplots() |
353 | 432 |
|
354 | 433 | # make a simple sine wave |
355 | 434 | x = np.arange(0., 2., 0.01) |
|
370 | 449 | ax.set_title('creating a shadow effect with an offset transform') |
371 | 450 | plt.show() |
372 | 451 |
|
| 452 | + |
373 | 453 | ############################################################################### |
| 454 | +# .. note:: |
| 455 | +# |
| 456 | +# The dpi and inches offset is a |
| 457 | +# common-enough use case that we have a special helper function to |
| 458 | +# create it in :func:`matplotlib.transforms.offset_copy`, which returns |
| 459 | +# a new transform with an added offset. So above we could have done:: |
| 460 | +# |
| 461 | +# shadow_transform = transforms.offset_copy(ax.transData, |
| 462 | +# fig=fig, dx, dy, units='inches') |
| 463 | +# |
| 464 | +# |
374 | 465 | # .. _transformation-pipeline: |
375 | 466 | # |
376 | 467 | # The transformation pipeline |
|
0 commit comments