|
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