test_patheffects.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import platform
  2. import numpy as np
  3. from matplotlib.testing.decorators import image_comparison
  4. import matplotlib.pyplot as plt
  5. import matplotlib.patheffects as path_effects
  6. from matplotlib.path import Path
  7. import matplotlib.patches as patches
  8. from matplotlib.backend_bases import RendererBase
  9. from matplotlib.patheffects import PathEffectRenderer
  10. @image_comparison(['patheffect1'], remove_text=True)
  11. def test_patheffect1():
  12. ax1 = plt.subplot()
  13. ax1.imshow([[1, 2], [2, 3]])
  14. txt = ax1.annotate("test", (1., 1.), (0., 0),
  15. arrowprops=dict(arrowstyle="->",
  16. connectionstyle="angle3", lw=2),
  17. size=20, ha="center",
  18. path_effects=[path_effects.withStroke(linewidth=3,
  19. foreground="w")])
  20. txt.arrow_patch.set_path_effects([path_effects.Stroke(linewidth=5,
  21. foreground="w"),
  22. path_effects.Normal()])
  23. pe = [path_effects.withStroke(linewidth=3, foreground="w")]
  24. ax1.grid(True, linestyle="-", path_effects=pe)
  25. @image_comparison(['patheffect2'], remove_text=True, style='mpl20',
  26. tol=0 if platform.machine() == 'x86_64' else 0.06)
  27. def test_patheffect2():
  28. ax2 = plt.subplot()
  29. arr = np.arange(25).reshape((5, 5))
  30. ax2.imshow(arr, interpolation='nearest')
  31. cntr = ax2.contour(arr, colors="k")
  32. cntr.set(path_effects=[path_effects.withStroke(linewidth=3, foreground="w")])
  33. clbls = ax2.clabel(cntr, fmt="%2.0f", use_clabeltext=True)
  34. plt.setp(clbls,
  35. path_effects=[path_effects.withStroke(linewidth=3,
  36. foreground="w")])
  37. @image_comparison(['patheffect3'],
  38. tol=0 if platform.machine() == 'x86_64' else 0.019)
  39. def test_patheffect3():
  40. p1, = plt.plot([1, 3, 5, 4, 3], 'o-b', lw=4)
  41. p1.set_path_effects([path_effects.SimpleLineShadow(),
  42. path_effects.Normal()])
  43. plt.title(
  44. r'testing$^{123}$',
  45. path_effects=[path_effects.withStroke(linewidth=1, foreground="r")])
  46. leg = plt.legend([p1], [r'Line 1$^2$'], fancybox=True, loc='upper left')
  47. leg.legendPatch.set_path_effects([path_effects.withSimplePatchShadow()])
  48. text = plt.text(2, 3, 'Drop test', color='white',
  49. bbox={'boxstyle': 'circle,pad=0.1', 'color': 'red'})
  50. pe = [path_effects.Stroke(linewidth=3.75, foreground='k'),
  51. path_effects.withSimplePatchShadow((6, -3), shadow_rgbFace='blue')]
  52. text.set_path_effects(pe)
  53. text.get_bbox_patch().set_path_effects(pe)
  54. pe = [path_effects.PathPatchEffect(offset=(4, -4), hatch='xxxx',
  55. facecolor='gray'),
  56. path_effects.PathPatchEffect(edgecolor='white', facecolor='black',
  57. lw=1.1)]
  58. t = plt.gcf().text(0.02, 0.1, 'Hatch shadow', fontsize=75, weight=1000,
  59. va='center')
  60. t.set_path_effects(pe)
  61. @image_comparison(['stroked_text.png'])
  62. def test_patheffects_stroked_text():
  63. text_chunks = [
  64. 'A B C D E F G H I J K L',
  65. 'M N O P Q R S T U V W',
  66. 'X Y Z a b c d e f g h i j',
  67. 'k l m n o p q r s t u v',
  68. 'w x y z 0123456789',
  69. r"!@#$%^&*()-=_+[]\;'",
  70. ',./{}|:"<>?'
  71. ]
  72. font_size = 50
  73. ax = plt.axes((0, 0, 1, 1))
  74. for i, chunk in enumerate(text_chunks):
  75. text = ax.text(x=0.01, y=(0.9 - i * 0.13), s=chunk,
  76. fontdict={'ha': 'left', 'va': 'center',
  77. 'size': font_size, 'color': 'white'})
  78. text.set_path_effects([path_effects.Stroke(linewidth=font_size / 10,
  79. foreground='black'),
  80. path_effects.Normal()])
  81. ax.set_xlim(0, 1)
  82. ax.set_ylim(0, 1)
  83. ax.axis('off')
  84. def test_PathEffect_points_to_pixels():
  85. fig = plt.figure(dpi=150)
  86. p1, = plt.plot(range(10))
  87. p1.set_path_effects([path_effects.SimpleLineShadow(),
  88. path_effects.Normal()])
  89. renderer = fig.canvas.get_renderer()
  90. pe_renderer = path_effects.PathEffectRenderer(
  91. p1.get_path_effects(), renderer)
  92. # Confirm that using a path effects renderer maintains point sizes
  93. # appropriately. Otherwise rendered font would be the wrong size.
  94. assert renderer.points_to_pixels(15) == pe_renderer.points_to_pixels(15)
  95. def test_SimplePatchShadow_offset():
  96. pe = path_effects.SimplePatchShadow(offset=(4, 5))
  97. assert pe._offset == (4, 5)
  98. @image_comparison(['collection'], tol=0.03, style='mpl20')
  99. def test_collection():
  100. x, y = np.meshgrid(np.linspace(0, 10, 150), np.linspace(-5, 5, 100))
  101. data = np.sin(x) + np.cos(y)
  102. cs = plt.contour(data)
  103. cs.set(path_effects=[
  104. path_effects.PathPatchEffect(edgecolor='black', facecolor='none', linewidth=12),
  105. path_effects.Stroke(linewidth=5)])
  106. for text in plt.clabel(cs, colors='white'):
  107. text.set_path_effects([path_effects.withStroke(foreground='k',
  108. linewidth=3)])
  109. text.set_bbox({'boxstyle': 'sawtooth', 'facecolor': 'none',
  110. 'edgecolor': 'blue'})
  111. @image_comparison(['tickedstroke'], remove_text=True, extensions=['png'],
  112. tol=0.22) # Increased tolerance due to fixed clipping.
  113. def test_tickedstroke():
  114. fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4))
  115. path = Path.unit_circle()
  116. patch = patches.PathPatch(path, facecolor='none', lw=2, path_effects=[
  117. path_effects.withTickedStroke(angle=-90, spacing=10,
  118. length=1)])
  119. ax1.add_patch(patch)
  120. ax1.axis('equal')
  121. ax1.set_xlim(-2, 2)
  122. ax1.set_ylim(-2, 2)
  123. ax2.plot([0, 1], [0, 1], label=' ',
  124. path_effects=[path_effects.withTickedStroke(spacing=7,
  125. angle=135)])
  126. nx = 101
  127. x = np.linspace(0.0, 1.0, nx)
  128. y = 0.3 * np.sin(x * 8) + 0.4
  129. ax2.plot(x, y, label=' ', path_effects=[path_effects.withTickedStroke()])
  130. ax2.legend()
  131. nx = 101
  132. ny = 105
  133. # Set up survey vectors
  134. xvec = np.linspace(0.001, 4.0, nx)
  135. yvec = np.linspace(0.001, 4.0, ny)
  136. # Set up survey matrices. Design disk loading and gear ratio.
  137. x1, x2 = np.meshgrid(xvec, yvec)
  138. # Evaluate some stuff to plot
  139. g1 = -(3 * x1 + x2 - 5.5)
  140. g2 = -(x1 + 2 * x2 - 4)
  141. g3 = .8 + x1 ** -3 - x2
  142. cg1 = ax3.contour(x1, x2, g1, [0], colors=('k',))
  143. cg1.set(path_effects=[path_effects.withTickedStroke(angle=135)])
  144. cg2 = ax3.contour(x1, x2, g2, [0], colors=('r',))
  145. cg2.set(path_effects=[path_effects.withTickedStroke(angle=60, length=2)])
  146. cg3 = ax3.contour(x1, x2, g3, [0], colors=('b',))
  147. cg3.set(path_effects=[path_effects.withTickedStroke(spacing=7)])
  148. ax3.set_xlim(0, 4)
  149. ax3.set_ylim(0, 4)
  150. @image_comparison(['spaces_and_newlines.png'], remove_text=True)
  151. def test_patheffects_spaces_and_newlines():
  152. ax = plt.subplot()
  153. s1 = " "
  154. s2 = "\nNewline also causes problems"
  155. text1 = ax.text(0.5, 0.75, s1, ha='center', va='center', size=20,
  156. bbox={'color': 'salmon'})
  157. text2 = ax.text(0.5, 0.25, s2, ha='center', va='center', size=20,
  158. bbox={'color': 'thistle'})
  159. text1.set_path_effects([path_effects.Normal()])
  160. text2.set_path_effects([path_effects.Normal()])
  161. def test_patheffects_overridden_methods_open_close_group():
  162. class CustomRenderer(RendererBase):
  163. def __init__(self):
  164. super().__init__()
  165. def open_group(self, s, gid=None):
  166. return "open_group overridden"
  167. def close_group(self, s):
  168. return "close_group overridden"
  169. renderer = PathEffectRenderer([path_effects.Normal()], CustomRenderer())
  170. assert renderer.open_group('s') == "open_group overridden"
  171. assert renderer.close_group('s') == "close_group overridden"