mpl_util.py 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. from __future__ import annotations
  2. from itertools import pairwise
  3. from typing import TYPE_CHECKING, cast
  4. import matplotlib.path as mpath
  5. import numpy as np
  6. from contourpy import FillType, LineType
  7. from contourpy.array import codes_from_offsets
  8. if TYPE_CHECKING:
  9. from contourpy._contourpy import FillReturn, LineReturn, LineReturn_Separate
  10. def filled_to_mpl_paths(filled: FillReturn, fill_type: FillType) -> list[mpath.Path]:
  11. if fill_type in (FillType.OuterCode, FillType.ChunkCombinedCode):
  12. paths = [mpath.Path(points, codes) for points, codes in zip(*filled) if points is not None]
  13. elif fill_type in (FillType.OuterOffset, FillType.ChunkCombinedOffset):
  14. paths = [mpath.Path(points, codes_from_offsets(offsets))
  15. for points, offsets in zip(*filled) if points is not None]
  16. elif fill_type == FillType.ChunkCombinedCodeOffset:
  17. paths = []
  18. for points, codes, outer_offsets in zip(*filled):
  19. if points is None:
  20. continue
  21. points = np.split(points, outer_offsets[1:-1])
  22. codes = np.split(codes, outer_offsets[1:-1])
  23. paths += [mpath.Path(p, c) for p, c in zip(points, codes)]
  24. elif fill_type == FillType.ChunkCombinedOffsetOffset:
  25. paths = []
  26. for points, offsets, outer_offsets in zip(*filled):
  27. if points is None:
  28. continue
  29. for i in range(len(outer_offsets)-1):
  30. offs = offsets[outer_offsets[i]:outer_offsets[i+1]+1]
  31. pts = points[offs[0]:offs[-1]]
  32. paths += [mpath.Path(pts, codes_from_offsets(offs - offs[0]))]
  33. else:
  34. raise RuntimeError(f"Conversion of FillType {fill_type} to MPL Paths is not implemented")
  35. return paths
  36. def lines_to_mpl_paths(lines: LineReturn, line_type: LineType) -> list[mpath.Path]:
  37. if line_type == LineType.Separate:
  38. if TYPE_CHECKING:
  39. lines = cast(LineReturn_Separate, lines)
  40. paths = []
  41. for line in lines:
  42. # Drawing as Paths so that they can be closed correctly.
  43. closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1]
  44. paths.append(mpath.Path(line, closed=closed))
  45. elif line_type in (LineType.SeparateCode, LineType.ChunkCombinedCode):
  46. paths = [mpath.Path(points, codes) for points, codes in zip(*lines) if points is not None]
  47. elif line_type == LineType.ChunkCombinedOffset:
  48. paths = []
  49. for points, offsets in zip(*lines):
  50. if points is None:
  51. continue
  52. for i in range(len(offsets)-1):
  53. line = points[offsets[i]:offsets[i+1]]
  54. closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1]
  55. paths.append(mpath.Path(line, closed=closed))
  56. elif line_type == LineType.ChunkCombinedNan:
  57. paths = []
  58. for points in lines[0]:
  59. if points is None:
  60. continue
  61. nan_offsets = np.nonzero(np.isnan(points[:, 0]))[0]
  62. nan_offsets = np.concatenate([[-1], nan_offsets, [len(points)]])
  63. for s, e in pairwise(nan_offsets):
  64. line = points[s+1:e]
  65. closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1]
  66. paths.append(mpath.Path(line, closed=closed))
  67. else:
  68. raise RuntimeError(f"Conversion of LineType {line_type} to MPL Paths is not implemented")
  69. return paths