test_bbox_tight.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. from io import BytesIO
  2. import platform
  3. import numpy as np
  4. from matplotlib.testing.decorators import image_comparison
  5. import matplotlib.pyplot as plt
  6. import matplotlib.path as mpath
  7. import matplotlib.patches as mpatches
  8. from matplotlib.ticker import FuncFormatter
  9. @image_comparison(['bbox_inches_tight'], remove_text=True,
  10. savefig_kwarg={'bbox_inches': 'tight'})
  11. def test_bbox_inches_tight():
  12. #: Test that a figure saved using bbox_inches='tight' is clipped correctly
  13. data = [[66386, 174296, 75131, 577908, 32015],
  14. [58230, 381139, 78045, 99308, 160454],
  15. [89135, 80552, 152558, 497981, 603535],
  16. [78415, 81858, 150656, 193263, 69638],
  17. [139361, 331509, 343164, 781380, 52269]]
  18. col_labels = row_labels = [''] * 5
  19. rows = len(data)
  20. ind = np.arange(len(col_labels)) + 0.3 # the x locations for the groups
  21. cell_text = []
  22. width = 0.4 # the width of the bars
  23. yoff = np.zeros(len(col_labels))
  24. # the bottom values for stacked bar chart
  25. fig, ax = plt.subplots(1, 1)
  26. for row in range(rows):
  27. ax.bar(ind, data[row], width, bottom=yoff, align='edge', color='b')
  28. yoff = yoff + data[row]
  29. cell_text.append([''])
  30. plt.xticks([])
  31. plt.xlim(0, 5)
  32. plt.legend([''] * 5, loc=(1.2, 0.2))
  33. fig.legend([''] * 5, bbox_to_anchor=(0, 0.2), loc='lower left')
  34. # Add a table at the bottom of the axes
  35. cell_text.reverse()
  36. plt.table(cellText=cell_text, rowLabels=row_labels, colLabels=col_labels,
  37. loc='bottom')
  38. @image_comparison(['bbox_inches_tight_suptile_legend'],
  39. savefig_kwarg={'bbox_inches': 'tight'},
  40. tol=0 if platform.machine() == 'x86_64' else 0.02)
  41. def test_bbox_inches_tight_suptile_legend():
  42. plt.plot(np.arange(10), label='a straight line')
  43. plt.legend(bbox_to_anchor=(0.9, 1), loc='upper left')
  44. plt.title('Axis title')
  45. plt.suptitle('Figure title')
  46. # put an extra long y tick on to see that the bbox is accounted for
  47. def y_formatter(y, pos):
  48. if int(y) == 4:
  49. return 'The number 4'
  50. else:
  51. return str(y)
  52. plt.gca().yaxis.set_major_formatter(FuncFormatter(y_formatter))
  53. plt.xlabel('X axis')
  54. @image_comparison(['bbox_inches_tight_suptile_non_default.png'],
  55. savefig_kwarg={'bbox_inches': 'tight'},
  56. tol=0.1) # large tolerance because only testing clipping.
  57. def test_bbox_inches_tight_suptitle_non_default():
  58. fig, ax = plt.subplots()
  59. fig.suptitle('Booo', x=0.5, y=1.1)
  60. @image_comparison(['bbox_inches_tight_layout.png'], remove_text=True,
  61. style='mpl20',
  62. savefig_kwarg=dict(bbox_inches='tight', pad_inches='layout'))
  63. def test_bbox_inches_tight_layout_constrained():
  64. fig, ax = plt.subplots(layout='constrained')
  65. fig.get_layout_engine().set(h_pad=0.5)
  66. ax.set_aspect('equal')
  67. def test_bbox_inches_tight_layout_notconstrained(tmp_path):
  68. # pad_inches='layout' should be ignored when not using constrained/
  69. # compressed layout. Smoke test that savefig doesn't error in this case.
  70. fig, ax = plt.subplots()
  71. fig.savefig(tmp_path / 'foo.png', bbox_inches='tight', pad_inches='layout')
  72. @image_comparison(['bbox_inches_tight_clipping'],
  73. remove_text=True, savefig_kwarg={'bbox_inches': 'tight'})
  74. def test_bbox_inches_tight_clipping():
  75. # tests bbox clipping on scatter points, and path clipping on a patch
  76. # to generate an appropriately tight bbox
  77. plt.scatter(np.arange(10), np.arange(10))
  78. ax = plt.gca()
  79. ax.set_xlim(0, 5)
  80. ax.set_ylim(0, 5)
  81. # make a massive rectangle and clip it with a path
  82. patch = mpatches.Rectangle([-50, -50], 100, 100,
  83. transform=ax.transData,
  84. facecolor='blue', alpha=0.5)
  85. path = mpath.Path.unit_regular_star(5).deepcopy()
  86. path.vertices *= 0.25
  87. patch.set_clip_path(path, transform=ax.transAxes)
  88. plt.gcf().artists.append(patch)
  89. @image_comparison(['bbox_inches_tight_raster'], tol=0.15, # For Ghostscript 10.06+.
  90. remove_text=True, savefig_kwarg={'bbox_inches': 'tight'})
  91. def test_bbox_inches_tight_raster():
  92. """Test rasterization with tight_layout"""
  93. fig, ax = plt.subplots()
  94. ax.plot([1.0, 2.0], rasterized=True)
  95. def test_only_on_non_finite_bbox():
  96. fig, ax = plt.subplots()
  97. ax.annotate("", xy=(0, float('nan')))
  98. ax.set_axis_off()
  99. # we only need to test that it does not error out on save
  100. fig.savefig(BytesIO(), bbox_inches='tight', format='png')
  101. def test_tight_pcolorfast():
  102. fig, ax = plt.subplots()
  103. ax.pcolorfast(np.arange(4).reshape((2, 2)))
  104. ax.set(ylim=(0, .1))
  105. buf = BytesIO()
  106. fig.savefig(buf, bbox_inches="tight")
  107. buf.seek(0)
  108. height, width, _ = plt.imread(buf).shape
  109. # Previously, the bbox would include the area of the image clipped out by
  110. # the axes, resulting in a very tall image given the y limits of (0, 0.1).
  111. assert width > height
  112. def test_noop_tight_bbox():
  113. from PIL import Image
  114. x_size, y_size = (10, 7)
  115. dpi = 100
  116. # make the figure just the right size up front
  117. fig = plt.figure(frameon=False, dpi=dpi, figsize=(x_size/dpi, y_size/dpi))
  118. ax = fig.add_axes((0, 0, 1, 1))
  119. ax.set_axis_off()
  120. ax.xaxis.set_visible(False)
  121. ax.yaxis.set_visible(False)
  122. data = np.arange(x_size * y_size).reshape(y_size, x_size)
  123. ax.imshow(data, rasterized=True)
  124. # When a rasterized Artist is included, a mixed-mode renderer does
  125. # additional bbox adjustment. It should also be a no-op, and not affect the
  126. # next save.
  127. fig.savefig(BytesIO(), bbox_inches='tight', pad_inches=0, format='pdf')
  128. out = BytesIO()
  129. fig.savefig(out, bbox_inches='tight', pad_inches=0)
  130. out.seek(0)
  131. im = np.asarray(Image.open(out))
  132. assert (im[:, :, 3] == 255).all()
  133. assert not (im[:, :, :3] == 255).all()
  134. assert im.shape == (7, 10, 4)
  135. @image_comparison(['bbox_inches_fixed_aspect'], extensions=['png'],
  136. remove_text=True, savefig_kwarg={'bbox_inches': 'tight'})
  137. def test_bbox_inches_fixed_aspect():
  138. with plt.rc_context({'figure.constrained_layout.use': True}):
  139. fig, ax = plt.subplots()
  140. ax.plot([0, 1])
  141. ax.set_xlim(0, 1)
  142. ax.set_aspect('equal')