convert.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING, cast
  3. import numpy as np
  4. from contourpy._contourpy import FillType, LineType
  5. import contourpy.array as arr
  6. from contourpy.enum_util import as_fill_type, as_line_type
  7. from contourpy.typecheck import check_filled, check_lines
  8. from contourpy.types import MOVETO, offset_dtype
  9. if TYPE_CHECKING:
  10. import contourpy._contourpy as cpy
  11. def _convert_filled_from_OuterCode(
  12. filled: cpy.FillReturn_OuterCode,
  13. fill_type_to: FillType,
  14. ) -> cpy.FillReturn:
  15. if fill_type_to == FillType.OuterCode:
  16. return filled
  17. elif fill_type_to == FillType.OuterOffset:
  18. return (filled[0], [arr.offsets_from_codes(codes) for codes in filled[1]])
  19. if len(filled[0]) > 0:
  20. points = arr.concat_points(filled[0])
  21. codes = arr.concat_codes(filled[1])
  22. else:
  23. points = None
  24. codes = None
  25. if fill_type_to == FillType.ChunkCombinedCode:
  26. return ([points], [codes])
  27. elif fill_type_to == FillType.ChunkCombinedOffset:
  28. return ([points], [None if codes is None else arr.offsets_from_codes(codes)])
  29. elif fill_type_to == FillType.ChunkCombinedCodeOffset:
  30. outer_offsets = None if points is None else arr.offsets_from_lengths(filled[0])
  31. ret1: cpy.FillReturn_ChunkCombinedCodeOffset = ([points], [codes], [outer_offsets])
  32. return ret1
  33. elif fill_type_to == FillType.ChunkCombinedOffsetOffset:
  34. if codes is None:
  35. ret2: cpy.FillReturn_ChunkCombinedOffsetOffset = ([None], [None], [None])
  36. else:
  37. offsets = arr.offsets_from_codes(codes)
  38. outer_offsets = arr.outer_offsets_from_list_of_codes(filled[1])
  39. ret2 = ([points], [offsets], [outer_offsets])
  40. return ret2
  41. else:
  42. raise ValueError(f"Invalid FillType {fill_type_to}")
  43. def _convert_filled_from_OuterOffset(
  44. filled: cpy.FillReturn_OuterOffset,
  45. fill_type_to: FillType,
  46. ) -> cpy.FillReturn:
  47. if fill_type_to == FillType.OuterCode:
  48. separate_codes = [arr.codes_from_offsets(offsets) for offsets in filled[1]]
  49. return (filled[0], separate_codes)
  50. elif fill_type_to == FillType.OuterOffset:
  51. return filled
  52. if len(filled[0]) > 0:
  53. points = arr.concat_points(filled[0])
  54. offsets = arr.concat_offsets(filled[1])
  55. else:
  56. points = None
  57. offsets = None
  58. if fill_type_to == FillType.ChunkCombinedCode:
  59. return ([points], [None if offsets is None else arr.codes_from_offsets(offsets)])
  60. elif fill_type_to == FillType.ChunkCombinedOffset:
  61. return ([points], [offsets])
  62. elif fill_type_to == FillType.ChunkCombinedCodeOffset:
  63. if offsets is None:
  64. ret1: cpy.FillReturn_ChunkCombinedCodeOffset = ([None], [None], [None])
  65. else:
  66. codes = arr.codes_from_offsets(offsets)
  67. outer_offsets = arr.offsets_from_lengths(filled[0])
  68. ret1 = ([points], [codes], [outer_offsets])
  69. return ret1
  70. elif fill_type_to == FillType.ChunkCombinedOffsetOffset:
  71. if points is None:
  72. ret2: cpy.FillReturn_ChunkCombinedOffsetOffset = ([None], [None], [None])
  73. else:
  74. outer_offsets = arr.outer_offsets_from_list_of_offsets(filled[1])
  75. ret2 = ([points], [offsets], [outer_offsets])
  76. return ret2
  77. else:
  78. raise ValueError(f"Invalid FillType {fill_type_to}")
  79. def _convert_filled_from_ChunkCombinedCode(
  80. filled: cpy.FillReturn_ChunkCombinedCode,
  81. fill_type_to: FillType,
  82. ) -> cpy.FillReturn:
  83. if fill_type_to == FillType.ChunkCombinedCode:
  84. return filled
  85. elif fill_type_to == FillType.ChunkCombinedOffset:
  86. codes = [None if codes is None else arr.offsets_from_codes(codes) for codes in filled[1]]
  87. return (filled[0], codes)
  88. else:
  89. raise ValueError(
  90. f"Conversion from {FillType.ChunkCombinedCode} to {fill_type_to} not supported")
  91. def _convert_filled_from_ChunkCombinedOffset(
  92. filled: cpy.FillReturn_ChunkCombinedOffset,
  93. fill_type_to: FillType,
  94. ) -> cpy.FillReturn:
  95. if fill_type_to == FillType.ChunkCombinedCode:
  96. chunk_codes: list[cpy.CodeArray | None] = []
  97. for points, offsets in zip(*filled):
  98. if points is None:
  99. chunk_codes.append(None)
  100. else:
  101. if TYPE_CHECKING:
  102. assert offsets is not None
  103. chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points))
  104. return (filled[0], chunk_codes)
  105. elif fill_type_to == FillType.ChunkCombinedOffset:
  106. return filled
  107. else:
  108. raise ValueError(
  109. f"Conversion from {FillType.ChunkCombinedOffset} to {fill_type_to} not supported")
  110. def _convert_filled_from_ChunkCombinedCodeOffset(
  111. filled: cpy.FillReturn_ChunkCombinedCodeOffset,
  112. fill_type_to: FillType,
  113. ) -> cpy.FillReturn:
  114. if fill_type_to == FillType.OuterCode:
  115. separate_points = []
  116. separate_codes = []
  117. for points, codes, outer_offsets in zip(*filled):
  118. if points is not None:
  119. if TYPE_CHECKING:
  120. assert codes is not None
  121. assert outer_offsets is not None
  122. separate_points += arr.split_points_by_offsets(points, outer_offsets)
  123. separate_codes += arr.split_codes_by_offsets(codes, outer_offsets)
  124. return (separate_points, separate_codes)
  125. elif fill_type_to == FillType.OuterOffset:
  126. separate_points = []
  127. separate_offsets = []
  128. for points, codes, outer_offsets in zip(*filled):
  129. if points is not None:
  130. if TYPE_CHECKING:
  131. assert codes is not None
  132. assert outer_offsets is not None
  133. separate_points += arr.split_points_by_offsets(points, outer_offsets)
  134. separate_codes = arr.split_codes_by_offsets(codes, outer_offsets)
  135. separate_offsets += [arr.offsets_from_codes(codes) for codes in separate_codes]
  136. return (separate_points, separate_offsets)
  137. elif fill_type_to == FillType.ChunkCombinedCode:
  138. ret1: cpy.FillReturn_ChunkCombinedCode = (filled[0], filled[1])
  139. return ret1
  140. elif fill_type_to == FillType.ChunkCombinedOffset:
  141. all_offsets = [None if codes is None else arr.offsets_from_codes(codes)
  142. for codes in filled[1]]
  143. ret2: cpy.FillReturn_ChunkCombinedOffset = (filled[0], all_offsets)
  144. return ret2
  145. elif fill_type_to == FillType.ChunkCombinedCodeOffset:
  146. return filled
  147. elif fill_type_to == FillType.ChunkCombinedOffsetOffset:
  148. chunk_offsets: list[cpy.OffsetArray | None] = []
  149. chunk_outer_offsets: list[cpy.OffsetArray | None] = []
  150. for codes, outer_offsets in zip(*filled[1:]):
  151. if codes is None:
  152. chunk_offsets.append(None)
  153. chunk_outer_offsets.append(None)
  154. else:
  155. if TYPE_CHECKING:
  156. assert outer_offsets is not None
  157. offsets = arr.offsets_from_codes(codes)
  158. outer_offsets = np.array([np.nonzero(offsets == oo)[0][0] for oo in outer_offsets],
  159. dtype=offset_dtype)
  160. chunk_offsets.append(offsets)
  161. chunk_outer_offsets.append(outer_offsets)
  162. ret3: cpy.FillReturn_ChunkCombinedOffsetOffset = (
  163. filled[0], chunk_offsets, chunk_outer_offsets,
  164. )
  165. return ret3
  166. else:
  167. raise ValueError(f"Invalid FillType {fill_type_to}")
  168. def _convert_filled_from_ChunkCombinedOffsetOffset(
  169. filled: cpy.FillReturn_ChunkCombinedOffsetOffset,
  170. fill_type_to: FillType,
  171. ) -> cpy.FillReturn:
  172. if fill_type_to == FillType.OuterCode:
  173. separate_points = []
  174. separate_codes = []
  175. for points, offsets, outer_offsets in zip(*filled):
  176. if points is not None:
  177. if TYPE_CHECKING:
  178. assert offsets is not None
  179. assert outer_offsets is not None
  180. codes = arr.codes_from_offsets_and_points(offsets, points)
  181. outer_offsets = offsets[outer_offsets]
  182. separate_points += arr.split_points_by_offsets(points, outer_offsets)
  183. separate_codes += arr.split_codes_by_offsets(codes, outer_offsets)
  184. return (separate_points, separate_codes)
  185. elif fill_type_to == FillType.OuterOffset:
  186. separate_points = []
  187. separate_offsets = []
  188. for points, offsets, outer_offsets in zip(*filled):
  189. if points is not None:
  190. if TYPE_CHECKING:
  191. assert offsets is not None
  192. assert outer_offsets is not None
  193. if len(outer_offsets) > 2:
  194. separate_offsets += [offsets[s:e+1] - offsets[s] for s, e in
  195. zip(outer_offsets[:-1], outer_offsets[1:])]
  196. else:
  197. separate_offsets.append(offsets)
  198. separate_points += arr.split_points_by_offsets(points, offsets[outer_offsets])
  199. return (separate_points, separate_offsets)
  200. elif fill_type_to == FillType.ChunkCombinedCode:
  201. chunk_codes: list[cpy.CodeArray | None] = []
  202. for points, offsets, outer_offsets in zip(*filled):
  203. if points is None:
  204. chunk_codes.append(None)
  205. else:
  206. if TYPE_CHECKING:
  207. assert offsets is not None
  208. assert outer_offsets is not None
  209. chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points))
  210. ret1: cpy.FillReturn_ChunkCombinedCode = (filled[0], chunk_codes)
  211. return ret1
  212. elif fill_type_to == FillType.ChunkCombinedOffset:
  213. return (filled[0], filled[1])
  214. elif fill_type_to == FillType.ChunkCombinedCodeOffset:
  215. chunk_codes = []
  216. chunk_outer_offsets: list[cpy.OffsetArray | None] = []
  217. for points, offsets, outer_offsets in zip(*filled):
  218. if points is None:
  219. chunk_codes.append(None)
  220. chunk_outer_offsets.append(None)
  221. else:
  222. if TYPE_CHECKING:
  223. assert offsets is not None
  224. assert outer_offsets is not None
  225. chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points))
  226. chunk_outer_offsets.append(offsets[outer_offsets])
  227. ret2: cpy.FillReturn_ChunkCombinedCodeOffset = (filled[0], chunk_codes, chunk_outer_offsets)
  228. return ret2
  229. elif fill_type_to == FillType.ChunkCombinedOffsetOffset:
  230. return filled
  231. else:
  232. raise ValueError(f"Invalid FillType {fill_type_to}")
  233. def convert_filled(
  234. filled: cpy.FillReturn,
  235. fill_type_from: FillType | str,
  236. fill_type_to: FillType | str,
  237. ) -> cpy.FillReturn:
  238. """Return the specified filled contours converted to a different :class:`~contourpy.FillType`.
  239. Args:
  240. filled (sequence of arrays): Filled contour polygons to convert.
  241. fill_type_from (FillType or str): :class:`~contourpy.FillType` to convert from as enum or
  242. string equivalent.
  243. fill_type_to (FillType or str): :class:`~contourpy.FillType` to convert to as enum or string
  244. equivalent.
  245. Return:
  246. Converted filled contour polygons.
  247. When converting non-chunked fill types (``FillType.OuterCode`` or ``FillType.OuterOffset``) to
  248. chunked ones, all polygons are placed in the first chunk. When converting in the other
  249. direction, all chunk information is discarded. Converting a fill type that is not aware of the
  250. relationship between outer boundaries and contained holes (``FillType.ChunkCombinedCode`` or)
  251. ``FillType.ChunkCombinedOffset``) to one that is will raise a ``ValueError``.
  252. .. versionadded:: 1.2.0
  253. """
  254. fill_type_from = as_fill_type(fill_type_from)
  255. fill_type_to = as_fill_type(fill_type_to)
  256. check_filled(filled, fill_type_from)
  257. if fill_type_from == FillType.OuterCode:
  258. if TYPE_CHECKING:
  259. filled = cast(cpy.FillReturn_OuterCode, filled)
  260. return _convert_filled_from_OuterCode(filled, fill_type_to)
  261. elif fill_type_from == FillType.OuterOffset:
  262. if TYPE_CHECKING:
  263. filled = cast(cpy.FillReturn_OuterOffset, filled)
  264. return _convert_filled_from_OuterOffset(filled, fill_type_to)
  265. elif fill_type_from == FillType.ChunkCombinedCode:
  266. if TYPE_CHECKING:
  267. filled = cast(cpy.FillReturn_ChunkCombinedCode, filled)
  268. return _convert_filled_from_ChunkCombinedCode(filled, fill_type_to)
  269. elif fill_type_from == FillType.ChunkCombinedOffset:
  270. if TYPE_CHECKING:
  271. filled = cast(cpy.FillReturn_ChunkCombinedOffset, filled)
  272. return _convert_filled_from_ChunkCombinedOffset(filled, fill_type_to)
  273. elif fill_type_from == FillType.ChunkCombinedCodeOffset:
  274. if TYPE_CHECKING:
  275. filled = cast(cpy.FillReturn_ChunkCombinedCodeOffset, filled)
  276. return _convert_filled_from_ChunkCombinedCodeOffset(filled, fill_type_to)
  277. elif fill_type_from == FillType.ChunkCombinedOffsetOffset:
  278. if TYPE_CHECKING:
  279. filled = cast(cpy.FillReturn_ChunkCombinedOffsetOffset, filled)
  280. return _convert_filled_from_ChunkCombinedOffsetOffset(filled, fill_type_to)
  281. else:
  282. raise ValueError(f"Invalid FillType {fill_type_from}")
  283. def _convert_lines_from_Separate(
  284. lines: cpy.LineReturn_Separate,
  285. line_type_to: LineType,
  286. ) -> cpy.LineReturn:
  287. if line_type_to == LineType.Separate:
  288. return lines
  289. elif line_type_to == LineType.SeparateCode:
  290. separate_codes = [arr.codes_from_points(line) for line in lines]
  291. return (lines, separate_codes)
  292. elif line_type_to == LineType.ChunkCombinedCode:
  293. if not lines:
  294. ret1: cpy.LineReturn_ChunkCombinedCode = ([None], [None])
  295. else:
  296. points = arr.concat_points(lines)
  297. offsets = arr.offsets_from_lengths(lines)
  298. codes = arr.codes_from_offsets_and_points(offsets, points)
  299. ret1 = ([points], [codes])
  300. return ret1
  301. elif line_type_to == LineType.ChunkCombinedOffset:
  302. if not lines:
  303. ret2: cpy.LineReturn_ChunkCombinedOffset = ([None], [None])
  304. else:
  305. ret2 = ([arr.concat_points(lines)], [arr.offsets_from_lengths(lines)])
  306. return ret2
  307. elif line_type_to == LineType.ChunkCombinedNan:
  308. if not lines:
  309. ret3: cpy.LineReturn_ChunkCombinedNan = ([None],)
  310. else:
  311. ret3 = ([arr.concat_points_with_nan(lines)],)
  312. return ret3
  313. else:
  314. raise ValueError(f"Invalid LineType {line_type_to}")
  315. def _convert_lines_from_SeparateCode(
  316. lines: cpy.LineReturn_SeparateCode,
  317. line_type_to: LineType,
  318. ) -> cpy.LineReturn:
  319. if line_type_to == LineType.Separate:
  320. # Drop codes.
  321. return lines[0]
  322. elif line_type_to == LineType.SeparateCode:
  323. return lines
  324. elif line_type_to == LineType.ChunkCombinedCode:
  325. if not lines[0]:
  326. ret1: cpy.LineReturn_ChunkCombinedCode = ([None], [None])
  327. else:
  328. ret1 = ([arr.concat_points(lines[0])], [arr.concat_codes(lines[1])])
  329. return ret1
  330. elif line_type_to == LineType.ChunkCombinedOffset:
  331. if not lines[0]:
  332. ret2: cpy.LineReturn_ChunkCombinedOffset = ([None], [None])
  333. else:
  334. ret2 = ([arr.concat_points(lines[0])], [arr.offsets_from_lengths(lines[0])])
  335. return ret2
  336. elif line_type_to == LineType.ChunkCombinedNan:
  337. if not lines[0]:
  338. ret3: cpy.LineReturn_ChunkCombinedNan = ([None],)
  339. else:
  340. ret3 = ([arr.concat_points_with_nan(lines[0])],)
  341. return ret3
  342. else:
  343. raise ValueError(f"Invalid LineType {line_type_to}")
  344. def _convert_lines_from_ChunkCombinedCode(
  345. lines: cpy.LineReturn_ChunkCombinedCode,
  346. line_type_to: LineType,
  347. ) -> cpy.LineReturn:
  348. if line_type_to in (LineType.Separate, LineType.SeparateCode):
  349. separate_lines = []
  350. for points, codes in zip(*lines):
  351. if points is not None:
  352. if TYPE_CHECKING:
  353. assert codes is not None
  354. split_at = np.nonzero(codes == MOVETO)[0]
  355. if len(split_at) > 1:
  356. separate_lines += np.split(points, split_at[1:])
  357. else:
  358. separate_lines.append(points)
  359. if line_type_to == LineType.Separate:
  360. return separate_lines
  361. else:
  362. separate_codes = [arr.codes_from_points(line) for line in separate_lines]
  363. return (separate_lines, separate_codes)
  364. elif line_type_to == LineType.ChunkCombinedCode:
  365. return lines
  366. elif line_type_to == LineType.ChunkCombinedOffset:
  367. chunk_offsets = [None if codes is None else arr.offsets_from_codes(codes)
  368. for codes in lines[1]]
  369. return (lines[0], chunk_offsets)
  370. elif line_type_to == LineType.ChunkCombinedNan:
  371. points_nan: list[cpy.PointArray | None] = []
  372. for points, codes in zip(*lines):
  373. if points is None:
  374. points_nan.append(None)
  375. else:
  376. if TYPE_CHECKING:
  377. assert codes is not None
  378. offsets = arr.offsets_from_codes(codes)
  379. points_nan.append(arr.insert_nan_at_offsets(points, offsets))
  380. return (points_nan,)
  381. else:
  382. raise ValueError(f"Invalid LineType {line_type_to}")
  383. def _convert_lines_from_ChunkCombinedOffset(
  384. lines: cpy.LineReturn_ChunkCombinedOffset,
  385. line_type_to: LineType,
  386. ) -> cpy.LineReturn:
  387. if line_type_to in (LineType.Separate, LineType.SeparateCode):
  388. separate_lines = []
  389. for points, offsets in zip(*lines):
  390. if points is not None:
  391. if TYPE_CHECKING:
  392. assert offsets is not None
  393. separate_lines += arr.split_points_by_offsets(points, offsets)
  394. if line_type_to == LineType.Separate:
  395. return separate_lines
  396. else:
  397. separate_codes = [arr.codes_from_points(line) for line in separate_lines]
  398. return (separate_lines, separate_codes)
  399. elif line_type_to == LineType.ChunkCombinedCode:
  400. chunk_codes: list[cpy.CodeArray | None] = []
  401. for points, offsets in zip(*lines):
  402. if points is None:
  403. chunk_codes.append(None)
  404. else:
  405. if TYPE_CHECKING:
  406. assert offsets is not None
  407. chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points))
  408. return (lines[0], chunk_codes)
  409. elif line_type_to == LineType.ChunkCombinedOffset:
  410. return lines
  411. elif line_type_to == LineType.ChunkCombinedNan:
  412. points_nan: list[cpy.PointArray | None] = []
  413. for points, offsets in zip(*lines):
  414. if points is None:
  415. points_nan.append(None)
  416. else:
  417. if TYPE_CHECKING:
  418. assert offsets is not None
  419. points_nan.append(arr.insert_nan_at_offsets(points, offsets))
  420. return (points_nan,)
  421. else:
  422. raise ValueError(f"Invalid LineType {line_type_to}")
  423. def _convert_lines_from_ChunkCombinedNan(
  424. lines: cpy.LineReturn_ChunkCombinedNan,
  425. line_type_to: LineType,
  426. ) -> cpy.LineReturn:
  427. if line_type_to in (LineType.Separate, LineType.SeparateCode):
  428. separate_lines = []
  429. for points in lines[0]:
  430. if points is not None:
  431. separate_lines += arr.split_points_at_nan(points)
  432. if line_type_to == LineType.Separate:
  433. return separate_lines
  434. else:
  435. separate_codes = [arr.codes_from_points(points) for points in separate_lines]
  436. return (separate_lines, separate_codes)
  437. elif line_type_to == LineType.ChunkCombinedCode:
  438. chunk_points: list[cpy.PointArray | None] = []
  439. chunk_codes: list[cpy.CodeArray | None] = []
  440. for points in lines[0]:
  441. if points is None:
  442. chunk_points.append(None)
  443. chunk_codes.append(None)
  444. else:
  445. points, offsets = arr.remove_nan(points)
  446. chunk_points.append(points)
  447. chunk_codes.append(arr.codes_from_offsets_and_points(offsets, points))
  448. return (chunk_points, chunk_codes)
  449. elif line_type_to == LineType.ChunkCombinedOffset:
  450. chunk_points = []
  451. chunk_offsets: list[cpy.OffsetArray | None] = []
  452. for points in lines[0]:
  453. if points is None:
  454. chunk_points.append(None)
  455. chunk_offsets.append(None)
  456. else:
  457. points, offsets = arr.remove_nan(points)
  458. chunk_points.append(points)
  459. chunk_offsets.append(offsets)
  460. return (chunk_points, chunk_offsets)
  461. elif line_type_to == LineType.ChunkCombinedNan:
  462. return lines
  463. else:
  464. raise ValueError(f"Invalid LineType {line_type_to}")
  465. def convert_lines(
  466. lines: cpy.LineReturn,
  467. line_type_from: LineType | str,
  468. line_type_to: LineType | str,
  469. ) -> cpy.LineReturn:
  470. """Return the specified contour lines converted to a different :class:`~contourpy.LineType`.
  471. Args:
  472. lines (sequence of arrays): Contour lines to convert.
  473. line_type_from (LineType or str): :class:`~contourpy.LineType` to convert from as enum or
  474. string equivalent.
  475. line_type_to (LineType or str): :class:`~contourpy.LineType` to convert to as enum or string
  476. equivalent.
  477. Return:
  478. Converted contour lines.
  479. When converting non-chunked line types (``LineType.Separate`` or ``LineType.SeparateCode``) to
  480. chunked ones (``LineType.ChunkCombinedCode``, ``LineType.ChunkCombinedOffset`` or
  481. ``LineType.ChunkCombinedNan``), all lines are placed in the first chunk. When converting in the
  482. other direction, all chunk information is discarded.
  483. .. versionadded:: 1.2.0
  484. """
  485. line_type_from = as_line_type(line_type_from)
  486. line_type_to = as_line_type(line_type_to)
  487. check_lines(lines, line_type_from)
  488. if line_type_from == LineType.Separate:
  489. if TYPE_CHECKING:
  490. lines = cast(cpy.LineReturn_Separate, lines)
  491. return _convert_lines_from_Separate(lines, line_type_to)
  492. elif line_type_from == LineType.SeparateCode:
  493. if TYPE_CHECKING:
  494. lines = cast(cpy.LineReturn_SeparateCode, lines)
  495. return _convert_lines_from_SeparateCode(lines, line_type_to)
  496. elif line_type_from == LineType.ChunkCombinedCode:
  497. if TYPE_CHECKING:
  498. lines = cast(cpy.LineReturn_ChunkCombinedCode, lines)
  499. return _convert_lines_from_ChunkCombinedCode(lines, line_type_to)
  500. elif line_type_from == LineType.ChunkCombinedOffset:
  501. if TYPE_CHECKING:
  502. lines = cast(cpy.LineReturn_ChunkCombinedOffset, lines)
  503. return _convert_lines_from_ChunkCombinedOffset(lines, line_type_to)
  504. elif line_type_from == LineType.ChunkCombinedNan:
  505. if TYPE_CHECKING:
  506. lines = cast(cpy.LineReturn_ChunkCombinedNan, lines)
  507. return _convert_lines_from_ChunkCombinedNan(lines, line_type_to)
  508. else:
  509. raise ValueError(f"Invalid LineType {line_type_from}")