test_simplification.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. import base64
  2. import io
  3. import platform
  4. import numpy as np
  5. from numpy.testing import assert_array_almost_equal, assert_array_equal
  6. import pytest
  7. from matplotlib.testing.decorators import (
  8. check_figures_equal, image_comparison, remove_ticks_and_titles)
  9. import matplotlib.pyplot as plt
  10. from matplotlib import patches, transforms
  11. from matplotlib.path import Path
  12. # NOTE: All of these tests assume that path.simplify is set to True
  13. # (the default)
  14. @image_comparison(['clipping'], remove_text=True)
  15. def test_clipping():
  16. t = np.arange(0.0, 2.0, 0.01)
  17. s = np.sin(2*np.pi*t)
  18. fig, ax = plt.subplots()
  19. ax.plot(t, s, linewidth=1.0)
  20. ax.set_ylim((-0.20, -0.28))
  21. @image_comparison(['overflow'], remove_text=True,
  22. tol=0 if platform.machine() == 'x86_64' else 0.007)
  23. def test_overflow():
  24. x = np.array([1.0, 2.0, 3.0, 2.0e5])
  25. y = np.arange(len(x))
  26. fig, ax = plt.subplots()
  27. ax.plot(x, y)
  28. ax.set_xlim(2, 6)
  29. @image_comparison(['clipping_diamond'], remove_text=True)
  30. def test_diamond():
  31. x = np.array([0.0, 1.0, 0.0, -1.0, 0.0])
  32. y = np.array([1.0, 0.0, -1.0, 0.0, 1.0])
  33. fig, ax = plt.subplots()
  34. ax.plot(x, y)
  35. ax.set_xlim(-0.6, 0.6)
  36. ax.set_ylim(-0.6, 0.6)
  37. def test_clipping_out_of_bounds():
  38. # Should work on a Path *without* codes.
  39. path = Path([(0, 0), (1, 2), (2, 1)])
  40. simplified = path.cleaned(clip=(10, 10, 20, 20))
  41. assert_array_equal(simplified.vertices, [(0, 0)])
  42. assert simplified.codes == [Path.STOP]
  43. # Should work on a Path *with* codes, and no curves.
  44. path = Path([(0, 0), (1, 2), (2, 1)],
  45. [Path.MOVETO, Path.LINETO, Path.LINETO])
  46. simplified = path.cleaned(clip=(10, 10, 20, 20))
  47. assert_array_equal(simplified.vertices, [(0, 0)])
  48. assert simplified.codes == [Path.STOP]
  49. # A Path with curves does not do any clipping yet.
  50. path = Path([(0, 0), (1, 2), (2, 3)],
  51. [Path.MOVETO, Path.CURVE3, Path.CURVE3])
  52. simplified = path.cleaned()
  53. simplified_clipped = path.cleaned(clip=(10, 10, 20, 20))
  54. assert_array_equal(simplified.vertices, simplified_clipped.vertices)
  55. assert_array_equal(simplified.codes, simplified_clipped.codes)
  56. def test_noise():
  57. np.random.seed(0)
  58. x = np.random.uniform(size=50000) * 50
  59. fig, ax = plt.subplots()
  60. p1 = ax.plot(x, solid_joinstyle='round', linewidth=2.0)
  61. # Ensure that the path's transform takes the new axes limits into account.
  62. fig.canvas.draw()
  63. path = p1[0].get_path()
  64. transform = p1[0].get_transform()
  65. path = transform.transform_path(path)
  66. simplified = path.cleaned(simplify=True)
  67. assert simplified.vertices.size == 25512
  68. def test_antiparallel_simplification():
  69. def _get_simplified(x, y):
  70. fig, ax = plt.subplots()
  71. p1 = ax.plot(x, y)
  72. path = p1[0].get_path()
  73. transform = p1[0].get_transform()
  74. path = transform.transform_path(path)
  75. simplified = path.cleaned(simplify=True)
  76. simplified = transform.inverted().transform_path(simplified)
  77. return simplified
  78. # test ending on a maximum
  79. x = [0, 0, 0, 0, 0, 1]
  80. y = [.5, 1, -1, 1, 2, .5]
  81. simplified = _get_simplified(x, y)
  82. assert_array_almost_equal([[0., 0.5],
  83. [0., -1.],
  84. [0., 2.],
  85. [1., 0.5]],
  86. simplified.vertices[:-2, :])
  87. # test ending on a minimum
  88. x = [0, 0, 0, 0, 0, 1]
  89. y = [.5, 1, -1, 1, -2, .5]
  90. simplified = _get_simplified(x, y)
  91. assert_array_almost_equal([[0., 0.5],
  92. [0., 1.],
  93. [0., -2.],
  94. [1., 0.5]],
  95. simplified.vertices[:-2, :])
  96. # test ending in between
  97. x = [0, 0, 0, 0, 0, 1]
  98. y = [.5, 1, -1, 1, 0, .5]
  99. simplified = _get_simplified(x, y)
  100. assert_array_almost_equal([[0., 0.5],
  101. [0., 1.],
  102. [0., -1.],
  103. [0., 0.],
  104. [1., 0.5]],
  105. simplified.vertices[:-2, :])
  106. # test no anti-parallel ending at max
  107. x = [0, 0, 0, 0, 0, 1]
  108. y = [.5, 1, 2, 1, 3, .5]
  109. simplified = _get_simplified(x, y)
  110. assert_array_almost_equal([[0., 0.5],
  111. [0., 3.],
  112. [1., 0.5]],
  113. simplified.vertices[:-2, :])
  114. # test no anti-parallel ending in middle
  115. x = [0, 0, 0, 0, 0, 1]
  116. y = [.5, 1, 2, 1, 1, .5]
  117. simplified = _get_simplified(x, y)
  118. assert_array_almost_equal([[0., 0.5],
  119. [0., 2.],
  120. [0., 1.],
  121. [1., 0.5]],
  122. simplified.vertices[:-2, :])
  123. # Only consider angles in 0 <= angle <= pi/2, otherwise
  124. # using min/max will get the expected results out of order:
  125. # min/max for simplification code depends on original vector,
  126. # and if angle is outside above range then simplification
  127. # min/max will be opposite from actual min/max.
  128. @pytest.mark.parametrize('angle', [0, np.pi/4, np.pi/3, np.pi/2])
  129. @pytest.mark.parametrize('offset', [0, .5])
  130. def test_angled_antiparallel(angle, offset):
  131. scale = 5
  132. np.random.seed(19680801)
  133. # get 15 random offsets
  134. # TODO: guarantee offset > 0 results in some offsets < 0
  135. vert_offsets = (np.random.rand(15) - offset) * scale
  136. # always start at 0 so rotation makes sense
  137. vert_offsets[0] = 0
  138. # always take the first step the same direction
  139. vert_offsets[1] = 1
  140. # compute points along a diagonal line
  141. x = np.sin(angle) * vert_offsets
  142. y = np.cos(angle) * vert_offsets
  143. # will check these later
  144. x_max = x[1:].max()
  145. x_min = x[1:].min()
  146. y_max = y[1:].max()
  147. y_min = y[1:].min()
  148. if offset > 0:
  149. p_expected = Path([[0, 0],
  150. [x_max, y_max],
  151. [x_min, y_min],
  152. [x[-1], y[-1]],
  153. [0, 0]],
  154. codes=[1, 2, 2, 2, 0])
  155. else:
  156. p_expected = Path([[0, 0],
  157. [x_max, y_max],
  158. [x[-1], y[-1]],
  159. [0, 0]],
  160. codes=[1, 2, 2, 0])
  161. p = Path(np.vstack([x, y]).T)
  162. p2 = p.cleaned(simplify=True)
  163. assert_array_almost_equal(p_expected.vertices,
  164. p2.vertices)
  165. assert_array_equal(p_expected.codes, p2.codes)
  166. def test_sine_plus_noise():
  167. np.random.seed(0)
  168. x = (np.sin(np.linspace(0, np.pi * 2.0, 50000)) +
  169. np.random.uniform(size=50000) * 0.01)
  170. fig, ax = plt.subplots()
  171. p1 = ax.plot(x, solid_joinstyle='round', linewidth=2.0)
  172. # Ensure that the path's transform takes the new axes limits into account.
  173. fig.canvas.draw()
  174. path = p1[0].get_path()
  175. transform = p1[0].get_transform()
  176. path = transform.transform_path(path)
  177. simplified = path.cleaned(simplify=True)
  178. assert simplified.vertices.size == 25240
  179. @image_comparison(['simplify_curve'], remove_text=True, tol=0.017)
  180. def test_simplify_curve():
  181. pp1 = patches.PathPatch(
  182. Path([(0, 0), (1, 0), (1, 1), (np.nan, 1), (0, 0), (2, 0), (2, 2),
  183. (0, 0)],
  184. [Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.CURVE3, Path.CURVE3,
  185. Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY]),
  186. fc="none")
  187. fig, ax = plt.subplots()
  188. ax.add_patch(pp1)
  189. ax.set_xlim((0, 2))
  190. ax.set_ylim((0, 2))
  191. @check_figures_equal()
  192. def test_closed_path_nan_removal(fig_test, fig_ref):
  193. ax_test = fig_test.subplots(2, 2).flatten()
  194. ax_ref = fig_ref.subplots(2, 2).flatten()
  195. # NaN on the first point also removes the last point, because it's closed.
  196. path = Path(
  197. [[-3, np.nan], [3, -3], [3, 3], [-3, 3], [-3, -3]],
  198. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
  199. ax_test[0].add_patch(patches.PathPatch(path, facecolor='none'))
  200. path = Path(
  201. [[-3, np.nan], [3, -3], [3, 3], [-3, 3], [-3, np.nan]],
  202. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO])
  203. ax_ref[0].add_patch(patches.PathPatch(path, facecolor='none'))
  204. # NaN on second-last point should not re-close.
  205. path = Path(
  206. [[-2, -2], [2, -2], [2, 2], [-2, np.nan], [-2, -2]],
  207. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
  208. ax_test[0].add_patch(patches.PathPatch(path, facecolor='none'))
  209. path = Path(
  210. [[-2, -2], [2, -2], [2, 2], [-2, np.nan], [-2, -2]],
  211. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO])
  212. ax_ref[0].add_patch(patches.PathPatch(path, facecolor='none'))
  213. # Test multiple loops in a single path (with same paths as above).
  214. path = Path(
  215. [[-3, np.nan], [3, -3], [3, 3], [-3, 3], [-3, -3],
  216. [-2, -2], [2, -2], [2, 2], [-2, np.nan], [-2, -2]],
  217. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY,
  218. Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
  219. ax_test[1].add_patch(patches.PathPatch(path, facecolor='none'))
  220. path = Path(
  221. [[-3, np.nan], [3, -3], [3, 3], [-3, 3], [-3, np.nan],
  222. [-2, -2], [2, -2], [2, 2], [-2, np.nan], [-2, -2]],
  223. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO,
  224. Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO])
  225. ax_ref[1].add_patch(patches.PathPatch(path, facecolor='none'))
  226. # NaN in first point of CURVE3 should not re-close, and hide entire curve.
  227. path = Path(
  228. [[-1, -1], [1, -1], [1, np.nan], [0, 1], [-1, 1], [-1, -1]],
  229. [Path.MOVETO, Path.LINETO, Path.CURVE3, Path.CURVE3, Path.LINETO,
  230. Path.CLOSEPOLY])
  231. ax_test[2].add_patch(patches.PathPatch(path, facecolor='none'))
  232. path = Path(
  233. [[-1, -1], [1, -1], [1, np.nan], [0, 1], [-1, 1], [-1, -1]],
  234. [Path.MOVETO, Path.LINETO, Path.CURVE3, Path.CURVE3, Path.LINETO,
  235. Path.CLOSEPOLY])
  236. ax_ref[2].add_patch(patches.PathPatch(path, facecolor='none'))
  237. # NaN in second point of CURVE3 should not re-close, and hide entire curve
  238. # plus next line segment.
  239. path = Path(
  240. [[-3, -3], [3, -3], [3, 0], [0, np.nan], [-3, 3], [-3, -3]],
  241. [Path.MOVETO, Path.LINETO, Path.CURVE3, Path.CURVE3, Path.LINETO,
  242. Path.LINETO])
  243. ax_test[2].add_patch(patches.PathPatch(path, facecolor='none'))
  244. path = Path(
  245. [[-3, -3], [3, -3], [3, 0], [0, np.nan], [-3, 3], [-3, -3]],
  246. [Path.MOVETO, Path.LINETO, Path.CURVE3, Path.CURVE3, Path.LINETO,
  247. Path.LINETO])
  248. ax_ref[2].add_patch(patches.PathPatch(path, facecolor='none'))
  249. # NaN in first point of CURVE4 should not re-close, and hide entire curve.
  250. path = Path(
  251. [[-1, -1], [1, -1], [1, np.nan], [0, 0], [0, 1], [-1, 1], [-1, -1]],
  252. [Path.MOVETO, Path.LINETO, Path.CURVE4, Path.CURVE4, Path.CURVE4,
  253. Path.LINETO, Path.CLOSEPOLY])
  254. ax_test[3].add_patch(patches.PathPatch(path, facecolor='none'))
  255. path = Path(
  256. [[-1, -1], [1, -1], [1, np.nan], [0, 0], [0, 1], [-1, 1], [-1, -1]],
  257. [Path.MOVETO, Path.LINETO, Path.CURVE4, Path.CURVE4, Path.CURVE4,
  258. Path.LINETO, Path.CLOSEPOLY])
  259. ax_ref[3].add_patch(patches.PathPatch(path, facecolor='none'))
  260. # NaN in second point of CURVE4 should not re-close, and hide entire curve.
  261. path = Path(
  262. [[-2, -2], [2, -2], [2, 0], [0, np.nan], [0, 2], [-2, 2], [-2, -2]],
  263. [Path.MOVETO, Path.LINETO, Path.CURVE4, Path.CURVE4, Path.CURVE4,
  264. Path.LINETO, Path.LINETO])
  265. ax_test[3].add_patch(patches.PathPatch(path, facecolor='none'))
  266. path = Path(
  267. [[-2, -2], [2, -2], [2, 0], [0, np.nan], [0, 2], [-2, 2], [-2, -2]],
  268. [Path.MOVETO, Path.LINETO, Path.CURVE4, Path.CURVE4, Path.CURVE4,
  269. Path.LINETO, Path.LINETO])
  270. ax_ref[3].add_patch(patches.PathPatch(path, facecolor='none'))
  271. # NaN in third point of CURVE4 should not re-close, and hide entire curve
  272. # plus next line segment.
  273. path = Path(
  274. [[-3, -3], [3, -3], [3, 0], [0, 0], [0, np.nan], [-3, 3], [-3, -3]],
  275. [Path.MOVETO, Path.LINETO, Path.CURVE4, Path.CURVE4, Path.CURVE4,
  276. Path.LINETO, Path.LINETO])
  277. ax_test[3].add_patch(patches.PathPatch(path, facecolor='none'))
  278. path = Path(
  279. [[-3, -3], [3, -3], [3, 0], [0, 0], [0, np.nan], [-3, 3], [-3, -3]],
  280. [Path.MOVETO, Path.LINETO, Path.CURVE4, Path.CURVE4, Path.CURVE4,
  281. Path.LINETO, Path.LINETO])
  282. ax_ref[3].add_patch(patches.PathPatch(path, facecolor='none'))
  283. # Keep everything clean.
  284. for ax in [*ax_test.flat, *ax_ref.flat]:
  285. ax.set(xlim=(-3.5, 3.5), ylim=(-3.5, 3.5))
  286. remove_ticks_and_titles(fig_test)
  287. remove_ticks_and_titles(fig_ref)
  288. @check_figures_equal()
  289. def test_closed_path_clipping(fig_test, fig_ref):
  290. vertices = []
  291. for roll in range(8):
  292. offset = 0.1 * roll + 0.1
  293. # A U-like pattern.
  294. pattern = [
  295. [-0.5, 1.5], [-0.5, -0.5], [1.5, -0.5], [1.5, 1.5], # Outer square
  296. # With a notch in the top.
  297. [1 - offset / 2, 1.5], [1 - offset / 2, offset],
  298. [offset / 2, offset], [offset / 2, 1.5],
  299. ]
  300. # Place the initial/final point anywhere in/out of the clipping area.
  301. pattern = np.roll(pattern, roll, axis=0)
  302. pattern = np.concatenate((pattern, pattern[:1, :]))
  303. vertices.append(pattern)
  304. # Multiple subpaths are used here to ensure they aren't broken by closed
  305. # loop clipping.
  306. codes = np.full(len(vertices[0]), Path.LINETO)
  307. codes[0] = Path.MOVETO
  308. codes[-1] = Path.CLOSEPOLY
  309. codes = np.tile(codes, len(vertices))
  310. vertices = np.concatenate(vertices)
  311. fig_test.set_size_inches((5, 5))
  312. path = Path(vertices, codes)
  313. fig_test.add_artist(patches.PathPatch(path, facecolor='none'))
  314. # For reference, we draw the same thing, but unclosed by using a line to
  315. # the last point only.
  316. fig_ref.set_size_inches((5, 5))
  317. codes = codes.copy()
  318. codes[codes == Path.CLOSEPOLY] = Path.LINETO
  319. path = Path(vertices, codes)
  320. fig_ref.add_artist(patches.PathPatch(path, facecolor='none'))
  321. @image_comparison(['hatch_simplify'], remove_text=True)
  322. def test_hatch():
  323. fig, ax = plt.subplots()
  324. ax.add_patch(plt.Rectangle((0, 0), 1, 1, fill=False, hatch="/"))
  325. ax.set_xlim((0.45, 0.55))
  326. ax.set_ylim((0.45, 0.55))
  327. @image_comparison(['fft_peaks'], remove_text=True)
  328. def test_fft_peaks():
  329. fig, ax = plt.subplots()
  330. t = np.arange(65536)
  331. p1 = ax.plot(abs(np.fft.fft(np.sin(2*np.pi*.01*t)*np.blackman(len(t)))))
  332. # Ensure that the path's transform takes the new axes limits into account.
  333. fig.canvas.draw()
  334. path = p1[0].get_path()
  335. transform = p1[0].get_transform()
  336. path = transform.transform_path(path)
  337. simplified = path.cleaned(simplify=True)
  338. assert simplified.vertices.size == 36
  339. def test_start_with_moveto():
  340. # Should be entirely clipped away to a single MOVETO
  341. data = b"""
  342. ZwAAAAku+v9UAQAA+Tj6/z8CAADpQ/r/KAMAANlO+v8QBAAAyVn6//UEAAC6ZPr/2gUAAKpv+v+8
  343. BgAAm3r6/50HAACLhfr/ewgAAHyQ+v9ZCQAAbZv6/zQKAABepvr/DgsAAE+x+v/lCwAAQLz6/7wM
  344. AAAxx/r/kA0AACPS+v9jDgAAFN36/zQPAAAF6Pr/AxAAAPfy+v/QEAAA6f36/5wRAADbCPv/ZhIA
  345. AMwT+/8uEwAAvh77//UTAACwKfv/uRQAAKM0+/98FQAAlT/7/z0WAACHSvv//RYAAHlV+/+7FwAA
  346. bGD7/3cYAABea/v/MRkAAFF2+//pGQAARIH7/6AaAAA3jPv/VRsAACmX+/8JHAAAHKL7/7ocAAAP
  347. rfv/ah0AAAO4+/8YHgAA9sL7/8QeAADpzfv/bx8AANzY+/8YIAAA0OP7/78gAADD7vv/ZCEAALf5
  348. +/8IIgAAqwT8/6kiAACeD/z/SiMAAJIa/P/oIwAAhiX8/4QkAAB6MPz/HyUAAG47/P+4JQAAYkb8
  349. /1AmAABWUfz/5SYAAEpc/P95JwAAPmf8/wsoAAAzcvz/nCgAACd9/P8qKQAAHIj8/7cpAAAQk/z/
  350. QyoAAAWe/P/MKgAA+aj8/1QrAADus/z/2isAAOO+/P9eLAAA2Mn8/+AsAADM1Pz/YS0AAMHf/P/g
  351. LQAAtur8/10uAACr9fz/2C4AAKEA/f9SLwAAlgv9/8ovAACLFv3/QDAAAIAh/f+1MAAAdSz9/ycx
  352. AABrN/3/mDEAAGBC/f8IMgAAVk39/3UyAABLWP3/4TIAAEFj/f9LMwAANm79/7MzAAAsef3/GjQA
  353. ACKE/f9+NAAAF4/9/+E0AAANmv3/QzUAAAOl/f+iNQAA+a/9/wA2AADvuv3/XDYAAOXF/f+2NgAA
  354. 29D9/w83AADR2/3/ZjcAAMfm/f+7NwAAvfH9/w44AACz/P3/XzgAAKkH/v+vOAAAnxL+//04AACW
  355. Hf7/SjkAAIwo/v+UOQAAgjP+/905AAB5Pv7/JDoAAG9J/v9pOgAAZVT+/606AABcX/7/7zoAAFJq
  356. /v8vOwAASXX+/207AAA/gP7/qjsAADaL/v/lOwAALZb+/x48AAAjof7/VTwAABqs/v+LPAAAELf+
  357. /788AAAHwv7/8TwAAP7M/v8hPQAA9df+/1A9AADr4v7/fT0AAOLt/v+oPQAA2fj+/9E9AADQA///
  358. +T0AAMYO//8fPgAAvRn//0M+AAC0JP//ZT4AAKsv//+GPgAAojr//6U+AACZRf//wj4AAJBQ///d
  359. PgAAh1v///c+AAB+Zv//Dz8AAHRx//8lPwAAa3z//zk/AABih///TD8AAFmS//9dPwAAUJ3//2w/
  360. AABHqP//ej8AAD6z//+FPwAANb7//48/AAAsyf//lz8AACPU//+ePwAAGt///6M/AAAR6v//pj8A
  361. AAj1//+nPwAA/////w=="""
  362. verts = np.frombuffer(base64.decodebytes(data), dtype='<i4')
  363. verts = verts.reshape((len(verts) // 2, 2))
  364. path = Path(verts)
  365. segs = path.iter_segments(transforms.IdentityTransform(),
  366. clip=(0.0, 0.0, 100.0, 100.0))
  367. segs = list(segs)
  368. assert len(segs) == 1
  369. assert segs[0][1] == Path.MOVETO
  370. def test_throw_rendering_complexity_exceeded():
  371. plt.rcParams['path.simplify'] = False
  372. xx = np.arange(2_000_000)
  373. yy = np.random.rand(2_000_000)
  374. yy[1000] = np.nan
  375. fig, ax = plt.subplots()
  376. ax.plot(xx, yy)
  377. with pytest.raises(OverflowError):
  378. fig.savefig(io.BytesIO())
  379. @image_comparison(['clipper_edge'], remove_text=True)
  380. def test_clipper():
  381. dat = (0, 1, 0, 2, 0, 3, 0, 4, 0, 5)
  382. fig = plt.figure(figsize=(2, 1))
  383. fig.subplots_adjust(left=0, bottom=0, wspace=0, hspace=0)
  384. ax = fig.add_axes((0, 0, 1.0, 1.0), ylim=(0, 5), autoscale_on=False)
  385. ax.plot(dat)
  386. ax.xaxis.set_major_locator(plt.MultipleLocator(1))
  387. ax.yaxis.set_major_locator(plt.MultipleLocator(1))
  388. ax.xaxis.set_ticks_position('bottom')
  389. ax.yaxis.set_ticks_position('left')
  390. ax.set_xlim(5, 9)
  391. @image_comparison(['para_equal_perp'], remove_text=True)
  392. def test_para_equal_perp():
  393. x = np.array([0, 1, 2, 1, 0, -1, 0, 1] + [1] * 128)
  394. y = np.array([1, 1, 2, 1, 0, -1, 0, 0] + [0] * 128)
  395. fig, ax = plt.subplots()
  396. ax.plot(x + 1, y + 1)
  397. ax.plot(x + 1, y + 1, 'ro')
  398. @image_comparison(['clipping_with_nans'])
  399. def test_clipping_with_nans():
  400. x = np.linspace(0, 3.14 * 2, 3000)
  401. y = np.sin(x)
  402. x[::100] = np.nan
  403. fig, ax = plt.subplots()
  404. ax.plot(x, y)
  405. ax.set_ylim(-0.25, 0.25)
  406. def test_clipping_full():
  407. p = Path([[1e30, 1e30]] * 5)
  408. simplified = list(p.iter_segments(clip=[0, 0, 100, 100]))
  409. assert simplified == []
  410. p = Path([[50, 40], [75, 65]], [1, 2])
  411. simplified = list(p.iter_segments(clip=[0, 0, 100, 100]))
  412. assert ([(list(x), y) for x, y in simplified] ==
  413. [([50, 40], 1), ([75, 65], 2)])
  414. p = Path([[50, 40]], [1])
  415. simplified = list(p.iter_segments(clip=[0, 0, 100, 100]))
  416. assert ([(list(x), y) for x, y in simplified] ==
  417. [([50, 40], 1)])
  418. def test_simplify_closepoly():
  419. # The values of the vertices in a CLOSEPOLY should always be ignored,
  420. # in favor of the most recent MOVETO's vertex values
  421. paths = [Path([(1, 1), (2, 1), (2, 2), (np.nan, np.nan)],
  422. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]),
  423. Path([(1, 1), (2, 1), (2, 2), (40, 50)],
  424. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])]
  425. expected_path = Path([(1, 1), (2, 1), (2, 2), (1, 1), (1, 1), (0, 0)],
  426. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO,
  427. Path.LINETO, Path.STOP])
  428. for path in paths:
  429. simplified_path = path.cleaned(simplify=True)
  430. assert_array_equal(expected_path.vertices, simplified_path.vertices)
  431. assert_array_equal(expected_path.codes, simplified_path.codes)
  432. # test that a compound path also works
  433. path = Path([(1, 1), (2, 1), (2, 2), (np.nan, np.nan),
  434. (-1, 0), (-2, 0), (-2, 1), (np.nan, np.nan)],
  435. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY,
  436. Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
  437. expected_path = Path([(1, 1), (2, 1), (2, 2), (1, 1),
  438. (-1, 0), (-2, 0), (-2, 1), (-1, 0), (-1, 0), (0, 0)],
  439. [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO,
  440. Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO,
  441. Path.LINETO, Path.STOP])
  442. simplified_path = path.cleaned(simplify=True)
  443. assert_array_equal(expected_path.vertices, simplified_path.vertices)
  444. assert_array_equal(expected_path.codes, simplified_path.codes)
  445. # test for a path with an invalid MOVETO
  446. # CLOSEPOLY with an invalid MOVETO should be ignored
  447. path = Path([(1, 0), (1, -1), (2, -1),
  448. (np.nan, np.nan), (-1, -1), (-2, 1), (-1, 1),
  449. (2, 2), (0, -1)],
  450. [Path.MOVETO, Path.LINETO, Path.LINETO,
  451. Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO,
  452. Path.CLOSEPOLY, Path.LINETO])
  453. expected_path = Path([(1, 0), (1, -1), (2, -1),
  454. (np.nan, np.nan), (-1, -1), (-2, 1), (-1, 1),
  455. (0, -1), (0, -1), (0, 0)],
  456. [Path.MOVETO, Path.LINETO, Path.LINETO,
  457. Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO,
  458. Path.LINETO, Path.LINETO, Path.STOP])
  459. simplified_path = path.cleaned(simplify=True)
  460. assert_array_equal(expected_path.vertices, simplified_path.vertices)
  461. assert_array_equal(expected_path.codes, simplified_path.codes)