scitex 2.7.0__py3-none-any.whl → 2.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. scitex/__init__.py +6 -2
  2. scitex/__version__.py +1 -1
  3. scitex/audio/README.md +52 -0
  4. scitex/audio/__init__.py +384 -0
  5. scitex/audio/__main__.py +129 -0
  6. scitex/audio/_tts.py +334 -0
  7. scitex/audio/engines/__init__.py +44 -0
  8. scitex/audio/engines/base.py +275 -0
  9. scitex/audio/engines/elevenlabs_engine.py +143 -0
  10. scitex/audio/engines/gtts_engine.py +162 -0
  11. scitex/audio/engines/pyttsx3_engine.py +131 -0
  12. scitex/audio/mcp_server.py +757 -0
  13. scitex/bridge/_helpers.py +1 -1
  14. scitex/bridge/_plt_vis.py +1 -1
  15. scitex/bridge/_stats_vis.py +1 -1
  16. scitex/dev/plt/__init__.py +272 -0
  17. scitex/dev/plt/plot_mpl_axhline.py +28 -0
  18. scitex/dev/plt/plot_mpl_axhspan.py +28 -0
  19. scitex/dev/plt/plot_mpl_axvline.py +28 -0
  20. scitex/dev/plt/plot_mpl_axvspan.py +28 -0
  21. scitex/dev/plt/plot_mpl_bar.py +29 -0
  22. scitex/dev/plt/plot_mpl_barh.py +29 -0
  23. scitex/dev/plt/plot_mpl_boxplot.py +28 -0
  24. scitex/dev/plt/plot_mpl_contour.py +31 -0
  25. scitex/dev/plt/plot_mpl_contourf.py +31 -0
  26. scitex/dev/plt/plot_mpl_errorbar.py +30 -0
  27. scitex/dev/plt/plot_mpl_eventplot.py +28 -0
  28. scitex/dev/plt/plot_mpl_fill.py +30 -0
  29. scitex/dev/plt/plot_mpl_fill_between.py +31 -0
  30. scitex/dev/plt/plot_mpl_hexbin.py +28 -0
  31. scitex/dev/plt/plot_mpl_hist.py +28 -0
  32. scitex/dev/plt/plot_mpl_hist2d.py +28 -0
  33. scitex/dev/plt/plot_mpl_imshow.py +29 -0
  34. scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
  35. scitex/dev/plt/plot_mpl_pie.py +29 -0
  36. scitex/dev/plt/plot_mpl_plot.py +29 -0
  37. scitex/dev/plt/plot_mpl_quiver.py +31 -0
  38. scitex/dev/plt/plot_mpl_scatter.py +28 -0
  39. scitex/dev/plt/plot_mpl_stackplot.py +31 -0
  40. scitex/dev/plt/plot_mpl_stem.py +29 -0
  41. scitex/dev/plt/plot_mpl_step.py +29 -0
  42. scitex/dev/plt/plot_mpl_violinplot.py +28 -0
  43. scitex/dev/plt/plot_sns_barplot.py +29 -0
  44. scitex/dev/plt/plot_sns_boxplot.py +29 -0
  45. scitex/dev/plt/plot_sns_heatmap.py +28 -0
  46. scitex/dev/plt/plot_sns_histplot.py +29 -0
  47. scitex/dev/plt/plot_sns_kdeplot.py +29 -0
  48. scitex/dev/plt/plot_sns_lineplot.py +31 -0
  49. scitex/dev/plt/plot_sns_scatterplot.py +29 -0
  50. scitex/dev/plt/plot_sns_stripplot.py +29 -0
  51. scitex/dev/plt/plot_sns_swarmplot.py +29 -0
  52. scitex/dev/plt/plot_sns_violinplot.py +29 -0
  53. scitex/dev/plt/plot_stx_bar.py +29 -0
  54. scitex/dev/plt/plot_stx_barh.py +29 -0
  55. scitex/dev/plt/plot_stx_box.py +28 -0
  56. scitex/dev/plt/plot_stx_boxplot.py +28 -0
  57. scitex/dev/plt/plot_stx_conf_mat.py +28 -0
  58. scitex/dev/plt/plot_stx_contour.py +31 -0
  59. scitex/dev/plt/plot_stx_ecdf.py +28 -0
  60. scitex/dev/plt/plot_stx_errorbar.py +30 -0
  61. scitex/dev/plt/plot_stx_fill_between.py +31 -0
  62. scitex/dev/plt/plot_stx_fillv.py +28 -0
  63. scitex/dev/plt/plot_stx_heatmap.py +28 -0
  64. scitex/dev/plt/plot_stx_image.py +28 -0
  65. scitex/dev/plt/plot_stx_imshow.py +28 -0
  66. scitex/dev/plt/plot_stx_joyplot.py +28 -0
  67. scitex/dev/plt/plot_stx_kde.py +28 -0
  68. scitex/dev/plt/plot_stx_line.py +28 -0
  69. scitex/dev/plt/plot_stx_mean_ci.py +28 -0
  70. scitex/dev/plt/plot_stx_mean_std.py +28 -0
  71. scitex/dev/plt/plot_stx_median_iqr.py +28 -0
  72. scitex/dev/plt/plot_stx_raster.py +28 -0
  73. scitex/dev/plt/plot_stx_rectangle.py +28 -0
  74. scitex/dev/plt/plot_stx_scatter.py +29 -0
  75. scitex/dev/plt/plot_stx_shaded_line.py +29 -0
  76. scitex/dev/plt/plot_stx_violin.py +28 -0
  77. scitex/dev/plt/plot_stx_violinplot.py +28 -0
  78. scitex/diagram/README.md +197 -0
  79. scitex/diagram/__init__.py +48 -0
  80. scitex/diagram/_compile.py +312 -0
  81. scitex/diagram/_diagram.py +355 -0
  82. scitex/diagram/_presets.py +173 -0
  83. scitex/diagram/_schema.py +182 -0
  84. scitex/diagram/_split.py +278 -0
  85. scitex/fig/__init__.py +352 -0
  86. scitex/{vis → fig}/backend/_parser.py +1 -1
  87. scitex/{vis → fig}/canvas.py +1 -1
  88. scitex/{vis → fig}/editor/__init__.py +5 -2
  89. scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
  90. scitex/{vis → fig}/editor/_defaults.py +70 -5
  91. scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
  92. scitex/{vis → fig}/editor/_qt_editor.py +182 -2
  93. scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
  94. scitex/fig/editor/edit/__init__.py +50 -0
  95. scitex/fig/editor/edit/backend_detector.py +109 -0
  96. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  97. scitex/fig/editor/edit/editor_launcher.py +239 -0
  98. scitex/fig/editor/edit/manual_handler.py +53 -0
  99. scitex/fig/editor/edit/panel_loader.py +232 -0
  100. scitex/fig/editor/edit/path_resolver.py +67 -0
  101. scitex/fig/editor/flask_editor/_bbox.py +1299 -0
  102. scitex/fig/editor/flask_editor/_core.py +1429 -0
  103. scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
  104. scitex/fig/editor/flask_editor/_renderer.py +813 -0
  105. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  106. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  107. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  108. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  109. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  110. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  111. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  112. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  113. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  114. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  115. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  116. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  117. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  118. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  119. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  120. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  121. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  122. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  123. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  124. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  125. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  126. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  127. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  128. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  129. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  130. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  131. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  132. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  133. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  134. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  135. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  136. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  137. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  138. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  139. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  140. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  141. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  142. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  143. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  144. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  145. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  146. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  147. scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
  148. scitex/fig/editor/flask_editor/templates/_html.py +852 -0
  149. scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
  150. scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
  151. scitex/{vis → fig}/io/__init__.py +13 -1
  152. scitex/fig/io/_bundle.py +1058 -0
  153. scitex/{vis → fig}/io/_canvas.py +1 -1
  154. scitex/{vis → fig}/io/_data.py +1 -1
  155. scitex/{vis → fig}/io/_export.py +1 -1
  156. scitex/{vis → fig}/io/_load.py +1 -1
  157. scitex/{vis → fig}/io/_panel.py +1 -1
  158. scitex/{vis → fig}/io/_save.py +1 -1
  159. scitex/{vis → fig}/model/__init__.py +1 -1
  160. scitex/{vis → fig}/model/_annotations.py +1 -1
  161. scitex/{vis → fig}/model/_axes.py +1 -1
  162. scitex/{vis → fig}/model/_figure.py +1 -1
  163. scitex/{vis → fig}/model/_guides.py +1 -1
  164. scitex/{vis → fig}/model/_plot.py +1 -1
  165. scitex/{vis → fig}/model/_styles.py +1 -1
  166. scitex/{vis → fig}/utils/__init__.py +1 -1
  167. scitex/io/__init__.py +22 -26
  168. scitex/io/_bundle.py +493 -0
  169. scitex/io/_flush.py +5 -2
  170. scitex/io/_load.py +98 -0
  171. scitex/io/_load_modules/_H5Explorer.py +5 -2
  172. scitex/io/_load_modules/_canvas.py +2 -2
  173. scitex/io/_load_modules/_image.py +3 -4
  174. scitex/io/_load_modules/_txt.py +4 -2
  175. scitex/io/_metadata.py +34 -324
  176. scitex/io/_metadata_modules/__init__.py +46 -0
  177. scitex/io/_metadata_modules/_embed.py +70 -0
  178. scitex/io/_metadata_modules/_read.py +64 -0
  179. scitex/io/_metadata_modules/_utils.py +79 -0
  180. scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
  181. scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
  182. scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
  183. scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
  184. scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
  185. scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
  186. scitex/io/_metadata_modules/read_metadata_png.py +39 -0
  187. scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
  188. scitex/io/_qr_utils.py +5 -3
  189. scitex/io/_save.py +548 -30
  190. scitex/io/_save_modules/_canvas.py +3 -3
  191. scitex/io/_save_modules/_image.py +5 -9
  192. scitex/io/_save_modules/_tex.py +7 -4
  193. scitex/io/_zip_bundle.py +439 -0
  194. scitex/io/utils/h5_to_zarr.py +11 -9
  195. scitex/msword/__init__.py +255 -0
  196. scitex/msword/profiles.py +357 -0
  197. scitex/msword/reader.py +753 -0
  198. scitex/msword/utils.py +289 -0
  199. scitex/msword/writer.py +362 -0
  200. scitex/plt/__init__.py +5 -2
  201. scitex/plt/_subplots/_AxesWrapper.py +6 -6
  202. scitex/plt/_subplots/_AxisWrapper.py +15 -9
  203. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
  204. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
  205. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
  206. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
  207. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
  208. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
  209. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
  210. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
  211. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
  212. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
  213. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
  214. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
  215. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
  216. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
  217. scitex/plt/_subplots/_FigWrapper.py +6 -6
  218. scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
  219. scitex/plt/_subplots/_export_as_csv.py +35 -5
  220. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
  221. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
  222. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
  223. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
  224. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
  225. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
  226. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
  227. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
  228. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
  229. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
  230. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
  231. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
  232. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
  233. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
  234. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
  235. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
  236. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
  237. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
  238. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
  239. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
  240. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
  241. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
  242. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
  243. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
  244. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
  245. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
  246. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
  247. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
  248. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
  249. scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
  250. scitex/plt/gallery/_generate.py +421 -14
  251. scitex/plt/io/__init__.py +53 -0
  252. scitex/plt/io/_bundle.py +490 -0
  253. scitex/plt/io/_layered_bundle.py +1343 -0
  254. scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
  255. scitex/plt/styles/__init__.py +14 -0
  256. scitex/plt/styles/presets.py +78 -0
  257. scitex/plt/utils/__init__.py +13 -1
  258. scitex/plt/utils/_collect_figure_metadata.py +10 -14
  259. scitex/plt/utils/_configure_mpl.py +6 -18
  260. scitex/plt/utils/_crop.py +32 -14
  261. scitex/plt/utils/_csv_column_naming.py +54 -0
  262. scitex/plt/utils/_figure_mm.py +116 -1
  263. scitex/plt/utils/_hitmap.py +1643 -0
  264. scitex/plt/utils/metadata/__init__.py +25 -0
  265. scitex/plt/utils/metadata/_core.py +9 -10
  266. scitex/plt/utils/metadata/_dimensions.py +6 -3
  267. scitex/plt/utils/metadata/_editable_export.py +405 -0
  268. scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
  269. scitex/schema/__init__.py +109 -16
  270. scitex/schema/_canvas.py +1 -1
  271. scitex/schema/_plot.py +1015 -0
  272. scitex/schema/_stats.py +2 -2
  273. scitex/stats/__init__.py +117 -0
  274. scitex/stats/io/__init__.py +29 -0
  275. scitex/stats/io/_bundle.py +156 -0
  276. scitex/tex/__init__.py +4 -0
  277. scitex/tex/_export.py +890 -0
  278. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
  279. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
  280. scitex/io/memo.md +0 -2827
  281. scitex/plt/REQUESTS.md +0 -191
  282. scitex/plt/_subplots/TODO.md +0 -53
  283. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
  284. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
  285. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
  286. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
  287. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
  288. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
  289. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
  290. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
  291. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
  292. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
  293. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
  294. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
  295. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
  296. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
  297. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
  298. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
  299. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
  300. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
  301. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
  302. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
  303. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
  304. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
  305. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
  306. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
  307. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
  308. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
  309. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
  310. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
  311. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
  312. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
  313. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
  314. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
  315. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
  316. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
  317. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
  318. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
  319. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
  320. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
  321. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
  322. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
  323. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
  324. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
  325. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
  326. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
  327. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
  328. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
  329. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
  330. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
  331. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
  332. scitex/vis/__init__.py +0 -177
  333. scitex/vis/editor/_edit.py +0 -390
  334. scitex/vis/editor/flask_editor/_bbox.py +0 -529
  335. scitex/vis/editor/flask_editor/_core.py +0 -168
  336. scitex/vis/editor/flask_editor/_renderer.py +0 -393
  337. scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
  338. scitex/vis/editor/flask_editor/templates/_html.py +0 -513
  339. scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
  340. scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
  341. /scitex/{vis → fig}/README.md +0 -0
  342. /scitex/{vis → fig}/backend/__init__.py +0 -0
  343. /scitex/{vis → fig}/backend/_export.py +0 -0
  344. /scitex/{vis → fig}/backend/_render.py +0 -0
  345. /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
  346. /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
  347. /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
  348. /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
  349. /scitex/{vis → fig}/io/_directory.py +0 -0
  350. /scitex/{vis → fig}/model/_plot_types.py +0 -0
  351. /scitex/{vis → fig}/utils/_defaults.py +0 -0
  352. /scitex/{vis → fig}/utils/_validate.py +0 -0
  353. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
  354. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
  355. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,570 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-10 (ywatanabe)"
4
+ # File: scitex/plt/utils/metadata/_geometry_extraction.py
5
+
6
+ """
7
+ Geometry extraction utilities for Schema v0.3.
8
+
9
+ This module provides functions to extract shape geometry in axes-local pixel
10
+ coordinates from matplotlib artists for interactive hit-testing in /vis/.
11
+
12
+ All coordinates are in AXES-LOCAL pixels:
13
+ - Origin at axes bounding box top-left
14
+ - X increases right, Y increases down (screen coordinates)
15
+
16
+ Geometry types per artist:
17
+ - line: path_simplified (polyline)
18
+ - scatter: points with hit_radius_px
19
+ - fill_between/polygon: polygon (closed shape)
20
+ - bar/rectangle: rectangles array
21
+ - image/contour: bbox only
22
+ - text: bbox + anchor
23
+ """
24
+
25
+ from typing import Dict, List, Optional, Tuple, Any
26
+ import numpy as np
27
+
28
+
29
+ def extract_axes_bbox_px(ax, fig) -> Dict[str, int]:
30
+ """
31
+ Extract axes bounding box in figure pixel coordinates.
32
+
33
+ Parameters
34
+ ----------
35
+ ax : matplotlib.axes.Axes
36
+ The axes object
37
+ fig : matplotlib.figure.Figure
38
+ The figure object
39
+
40
+ Returns
41
+ -------
42
+ dict
43
+ Bounding box with keys: x0, y0, x1, y1 (figure pixels)
44
+ """
45
+ # Get axes position in figure coordinates (0-1)
46
+ bbox = ax.get_position()
47
+
48
+ # Get figure size in pixels
49
+ fig_width_px = fig.get_figwidth() * fig.dpi
50
+ fig_height_px = fig.get_figheight() * fig.dpi
51
+
52
+ # Convert to figure pixels
53
+ x0 = int(bbox.x0 * fig_width_px)
54
+ y0 = int((1 - bbox.y1) * fig_height_px) # Flip Y for screen coords
55
+ x1 = int(bbox.x1 * fig_width_px)
56
+ y1 = int((1 - bbox.y0) * fig_height_px) # Flip Y for screen coords
57
+
58
+ return {"x0": x0, "y0": y0, "x1": x1, "y1": y1}
59
+
60
+
61
+ def data_to_axes_px(ax, fig, x_data, y_data) -> Tuple[np.ndarray, np.ndarray]:
62
+ """
63
+ Convert data coordinates to axes-local pixel coordinates.
64
+
65
+ Parameters
66
+ ----------
67
+ ax : matplotlib.axes.Axes
68
+ The axes object
69
+ fig : matplotlib.figure.Figure
70
+ The figure object
71
+ x_data : array-like
72
+ X coordinates in data units
73
+ y_data : array-like
74
+ Y coordinates in data units
75
+
76
+ Returns
77
+ -------
78
+ tuple
79
+ (x_px, y_px) in axes-local pixels
80
+ """
81
+ x_data = np.asarray(x_data)
82
+ y_data = np.asarray(y_data)
83
+
84
+ # Get axes bbox in figure pixels
85
+ axes_bbox = extract_axes_bbox_px(ax, fig)
86
+ ax_width = axes_bbox["x1"] - axes_bbox["x0"]
87
+ ax_height = axes_bbox["y1"] - axes_bbox["y0"]
88
+
89
+ # Get data limits
90
+ xlim = ax.get_xlim()
91
+ ylim = ax.get_ylim()
92
+
93
+ # Convert data to normalized axes coordinates (0-1)
94
+ x_norm = (x_data - xlim[0]) / (xlim[1] - xlim[0])
95
+ y_norm = (y_data - ylim[0]) / (ylim[1] - ylim[0])
96
+
97
+ # Convert to axes-local pixels (Y flipped for screen coords)
98
+ x_px = x_norm * ax_width
99
+ y_px = (1 - y_norm) * ax_height # Flip Y
100
+
101
+ return x_px.astype(float), y_px.astype(float)
102
+
103
+
104
+ def extract_line_geometry(line, ax, fig, simplify_threshold: float = 0.5) -> Dict[str, Any]:
105
+ """
106
+ Extract line geometry in axes-local pixels.
107
+
108
+ Parameters
109
+ ----------
110
+ line : matplotlib.lines.Line2D
111
+ The line object
112
+ ax : matplotlib.axes.Axes
113
+ The axes object
114
+ fig : matplotlib.figure.Figure
115
+ The figure object
116
+ simplify_threshold : float
117
+ Maximum error in pixels for path simplification (Douglas-Peucker)
118
+
119
+ Returns
120
+ -------
121
+ dict
122
+ Geometry with: coord_space, bbox, path_simplified, (optional) path
123
+ """
124
+ x_data = line.get_xdata()
125
+ y_data = line.get_ydata()
126
+
127
+ if len(x_data) == 0:
128
+ return {"coord_space": "axes", "bbox": None, "path_simplified": []}
129
+
130
+ x_px, y_px = data_to_axes_px(ax, fig, x_data, y_data)
131
+
132
+ # Build path as list of [x, y] points
133
+ path = [[round(x, 1), round(y, 1)] for x, y in zip(x_px, y_px)]
134
+
135
+ # Simplify path using Douglas-Peucker
136
+ path_simplified = _simplify_path(path, simplify_threshold)
137
+
138
+ # Compute bounding box
139
+ bbox = _compute_bbox(x_px, y_px)
140
+
141
+ return {
142
+ "coord_space": "axes",
143
+ "bbox": bbox,
144
+ "path_simplified": path_simplified,
145
+ "path": None # Full path omitted for performance
146
+ }
147
+
148
+
149
+ def extract_scatter_geometry(collection, ax, fig) -> Dict[str, Any]:
150
+ """
151
+ Extract scatter point geometry in axes-local pixels.
152
+
153
+ Parameters
154
+ ----------
155
+ collection : matplotlib.collections.PathCollection
156
+ The scatter collection
157
+ ax : matplotlib.axes.Axes
158
+ The axes object
159
+ fig : matplotlib.figure.Figure
160
+ The figure object
161
+
162
+ Returns
163
+ -------
164
+ dict
165
+ Geometry with: coord_space, bbox, hit_radius_px, points
166
+ """
167
+ offsets = collection.get_offsets()
168
+
169
+ if len(offsets) == 0:
170
+ return {"coord_space": "axes", "bbox": None, "hit_radius_px": 6.0, "points": []}
171
+
172
+ x_data = offsets[:, 0]
173
+ y_data = offsets[:, 1]
174
+
175
+ x_px, y_px = data_to_axes_px(ax, fig, x_data, y_data)
176
+
177
+ # Get marker sizes (s is area in points^2)
178
+ sizes = collection.get_sizes()
179
+ if len(sizes) == 0:
180
+ sizes = [36] # Default matplotlib marker size
181
+
182
+ # Compute hit radius from average marker size
183
+ # s = area in points^2, radius = sqrt(s/pi) in points
184
+ # Convert points to pixels (roughly 1:1 at 72 dpi, scale with actual dpi)
185
+ avg_size = np.mean(sizes)
186
+ radius_pt = np.sqrt(avg_size / np.pi)
187
+ hit_radius_px = max(radius_pt * fig.dpi / 72, 5.0) # Min 5px for usability
188
+
189
+ # Build points list
190
+ points = [{"x": round(x, 1), "y": round(y, 1)} for x, y in zip(x_px, y_px)]
191
+
192
+ # Compute bounding box
193
+ bbox = _compute_bbox(x_px, y_px)
194
+
195
+ return {
196
+ "coord_space": "axes",
197
+ "bbox": bbox,
198
+ "hit_radius_px": round(hit_radius_px, 1),
199
+ "points": points
200
+ }
201
+
202
+
203
+ def extract_polygon_geometry(collection, ax, fig) -> Dict[str, Any]:
204
+ """
205
+ Extract polygon geometry (fill_between, violin, etc.) in axes-local pixels.
206
+
207
+ Parameters
208
+ ----------
209
+ collection : matplotlib.collections.PolyCollection
210
+ The polygon collection
211
+ ax : matplotlib.axes.Axes
212
+ The axes object
213
+ fig : matplotlib.figure.Figure
214
+ The figure object
215
+
216
+ Returns
217
+ -------
218
+ dict
219
+ Geometry with: coord_space, bbox, polygon
220
+ """
221
+ paths = collection.get_paths()
222
+
223
+ if len(paths) == 0:
224
+ return {"coord_space": "axes", "bbox": None, "polygon": []}
225
+
226
+ # Get vertices from first path
227
+ vertices = paths[0].vertices
228
+
229
+ if len(vertices) == 0:
230
+ return {"coord_space": "axes", "bbox": None, "polygon": []}
231
+
232
+ x_data = vertices[:, 0]
233
+ y_data = vertices[:, 1]
234
+
235
+ x_px, y_px = data_to_axes_px(ax, fig, x_data, y_data)
236
+
237
+ # Build polygon as list of [x, y] points
238
+ polygon = [[round(x, 1), round(y, 1)] for x, y in zip(x_px, y_px)]
239
+
240
+ # Simplify if too many points
241
+ if len(polygon) > 100:
242
+ polygon = _simplify_path(polygon, 1.0)
243
+
244
+ # Compute bounding box
245
+ bbox = _compute_bbox(x_px, y_px)
246
+
247
+ return {
248
+ "coord_space": "axes",
249
+ "bbox": bbox,
250
+ "polygon": polygon
251
+ }
252
+
253
+
254
+ def extract_rectangle_geometry(patch, ax, fig) -> Dict[str, Any]:
255
+ """
256
+ Extract rectangle geometry (bar, etc.) in axes-local pixels.
257
+
258
+ Parameters
259
+ ----------
260
+ patch : matplotlib.patches.Rectangle
261
+ The rectangle patch
262
+ ax : matplotlib.axes.Axes
263
+ The axes object
264
+ fig : matplotlib.figure.Figure
265
+ The figure object
266
+
267
+ Returns
268
+ -------
269
+ dict
270
+ Geometry with: coord_space, bbox (also serves as the rectangle)
271
+ """
272
+ # Get rectangle corners in data coordinates
273
+ x0_data = patch.get_x()
274
+ y0_data = patch.get_y()
275
+ width_data = patch.get_width()
276
+ height_data = patch.get_height()
277
+
278
+ # Convert corners to axes-local pixels
279
+ x_corners = [x0_data, x0_data + width_data]
280
+ y_corners = [y0_data, y0_data + height_data]
281
+
282
+ x_px, _ = data_to_axes_px(ax, fig, x_corners, [y0_data, y0_data])
283
+ _, y_px = data_to_axes_px(ax, fig, [x0_data, x0_data], y_corners)
284
+
285
+ # Build rectangle (note: Y may be flipped)
286
+ x = round(min(x_px), 1)
287
+ y = round(min(y_px), 1)
288
+ width = round(abs(x_px[1] - x_px[0]), 1)
289
+ height = round(abs(y_px[1] - y_px[0]), 1)
290
+
291
+ return {
292
+ "coord_space": "axes",
293
+ "bbox": {"x0": int(x), "y0": int(y), "x1": int(x + width), "y1": int(y + height)},
294
+ "rectangle": {"x": x, "y": y, "width": width, "height": height}
295
+ }
296
+
297
+
298
+ def extract_bar_group_geometry(patches, ax, fig) -> Dict[str, Any]:
299
+ """
300
+ Extract geometry for a group of bar rectangles.
301
+
302
+ Parameters
303
+ ----------
304
+ patches : list
305
+ List of Rectangle patches
306
+ ax : matplotlib.axes.Axes
307
+ The axes object
308
+ fig : matplotlib.figure.Figure
309
+ The figure object
310
+
311
+ Returns
312
+ -------
313
+ dict
314
+ Geometry with: coord_space, bbox, rectangles
315
+ """
316
+ rectangles = []
317
+ all_x = []
318
+ all_y = []
319
+
320
+ for patch in patches:
321
+ geom = extract_rectangle_geometry(patch, ax, fig)
322
+ rect = geom.get("rectangle", {})
323
+ if rect:
324
+ rectangles.append(rect)
325
+ all_x.extend([rect["x"], rect["x"] + rect["width"]])
326
+ all_y.extend([rect["y"], rect["y"] + rect["height"]])
327
+
328
+ if not rectangles:
329
+ return {"coord_space": "axes", "bbox": None, "rectangles": []}
330
+
331
+ # Compute overall bounding box
332
+ bbox = {
333
+ "x0": int(min(all_x)),
334
+ "y0": int(min(all_y)),
335
+ "x1": int(max(all_x)),
336
+ "y1": int(max(all_y))
337
+ }
338
+
339
+ return {
340
+ "coord_space": "axes",
341
+ "bbox": bbox,
342
+ "rectangles": rectangles
343
+ }
344
+
345
+
346
+ def extract_text_geometry(text, ax, fig) -> Dict[str, Any]:
347
+ """
348
+ Extract text geometry in axes-local pixels.
349
+
350
+ Parameters
351
+ ----------
352
+ text : matplotlib.text.Text
353
+ The text object
354
+ ax : matplotlib.axes.Axes
355
+ The axes object
356
+ fig : matplotlib.figure.Figure
357
+ The figure object
358
+
359
+ Returns
360
+ -------
361
+ dict
362
+ Geometry with: coord_space, bbox, anchor
363
+ """
364
+ # Get text position in data coordinates
365
+ x_data, y_data = text.get_position()
366
+
367
+ # Convert to axes-local pixels
368
+ x_px, y_px = data_to_axes_px(ax, fig, [x_data], [y_data])
369
+
370
+ anchor = {"x": float(round(x_px[0], 1)), "y": float(round(y_px[0], 1))}
371
+
372
+ # Try to get text bounding box
373
+ try:
374
+ renderer = fig.canvas.get_renderer()
375
+ bbox_display = text.get_window_extent(renderer=renderer)
376
+
377
+ # Get axes bbox for conversion
378
+ axes_bbox = extract_axes_bbox_px(ax, fig)
379
+
380
+ # Convert display coords to axes-local pixels
381
+ x0 = bbox_display.x0 - axes_bbox["x0"]
382
+ y0 = (fig.get_figheight() * fig.dpi - bbox_display.y1) - axes_bbox["y0"] # Flip Y
383
+ x1 = bbox_display.x1 - axes_bbox["x0"]
384
+ y1 = (fig.get_figheight() * fig.dpi - bbox_display.y0) - axes_bbox["y0"]
385
+
386
+ bbox = {
387
+ "x0": int(round(x0)),
388
+ "y0": int(round(y0)),
389
+ "x1": int(round(x1)),
390
+ "y1": int(round(y1))
391
+ }
392
+ except Exception:
393
+ # Fallback: estimate bbox from anchor
394
+ bbox = {
395
+ "x0": int(anchor["x"] - 20),
396
+ "y0": int(anchor["y"] - 10),
397
+ "x1": int(anchor["x"] + 80),
398
+ "y1": int(anchor["y"] + 10)
399
+ }
400
+
401
+ return {
402
+ "coord_space": "axes",
403
+ "bbox": bbox,
404
+ "anchor": anchor
405
+ }
406
+
407
+
408
+ def extract_image_geometry(image, ax, fig) -> Dict[str, Any]:
409
+ """
410
+ Extract image geometry (imshow, etc.) in axes-local pixels.
411
+
412
+ Parameters
413
+ ----------
414
+ image : matplotlib.image.AxesImage
415
+ The image object
416
+ ax : matplotlib.axes.Axes
417
+ The axes object
418
+ fig : matplotlib.figure.Figure
419
+ The figure object
420
+
421
+ Returns
422
+ -------
423
+ dict
424
+ Geometry with: coord_space, bbox
425
+ """
426
+ extent = image.get_extent()
427
+
428
+ if extent is None:
429
+ # Full axes
430
+ return {
431
+ "coord_space": "axes",
432
+ "bbox": {"x0": 0, "y0": 0, "x1": 100, "y1": 100} # Will be computed
433
+ }
434
+
435
+ x0_data, x1_data, y0_data, y1_data = extent
436
+
437
+ x_px, _ = data_to_axes_px(ax, fig, [x0_data, x1_data], [y0_data, y0_data])
438
+ _, y_px = data_to_axes_px(ax, fig, [x0_data, x0_data], [y0_data, y1_data])
439
+
440
+ bbox = {
441
+ "x0": int(round(min(x_px))),
442
+ "y0": int(round(min(y_px))),
443
+ "x1": int(round(max(x_px))),
444
+ "y1": int(round(max(y_px)))
445
+ }
446
+
447
+ return {
448
+ "coord_space": "axes",
449
+ "bbox": bbox
450
+ }
451
+
452
+
453
+ # =============================================================================
454
+ # Helper functions
455
+ # =============================================================================
456
+
457
+ def _compute_bbox(x_px: np.ndarray, y_px: np.ndarray) -> Dict[str, int]:
458
+ """Compute bounding box from pixel coordinates."""
459
+ return {
460
+ "x0": int(round(np.nanmin(x_px))),
461
+ "y0": int(round(np.nanmin(y_px))),
462
+ "x1": int(round(np.nanmax(x_px))),
463
+ "y1": int(round(np.nanmax(y_px)))
464
+ }
465
+
466
+
467
+ def _simplify_path(path: List[List[float]], tolerance: float) -> List[List[float]]:
468
+ """
469
+ Simplify a path using Douglas-Peucker algorithm.
470
+
471
+ Parameters
472
+ ----------
473
+ path : list
474
+ List of [x, y] points
475
+ tolerance : float
476
+ Maximum perpendicular distance in pixels
477
+
478
+ Returns
479
+ -------
480
+ list
481
+ Simplified path
482
+ """
483
+ if len(path) <= 2:
484
+ return path
485
+
486
+ # Convert to numpy for easier computation
487
+ points = np.array(path)
488
+
489
+ # Find point with maximum distance from line between endpoints
490
+ start = points[0]
491
+ end = points[-1]
492
+
493
+ # Line vector
494
+ line_vec = end - start
495
+ line_len = np.linalg.norm(line_vec)
496
+
497
+ # Handle closed polygons (start ≈ end)
498
+ if line_len < 1e-10:
499
+ # Find point farthest from centroid to split the polygon
500
+ centroid = np.mean(points, axis=0)
501
+ distances_from_centroid = np.linalg.norm(points - centroid, axis=1)
502
+ split_idx = np.argmax(distances_from_centroid)
503
+
504
+ if split_idx == 0 or split_idx == len(points) - 1:
505
+ # Can't split well, return simplified version
506
+ return _simplify_open_path(path, tolerance)
507
+
508
+ # Split and simplify each half
509
+ first_half = [p.tolist() for p in points[:split_idx + 1]]
510
+ second_half = [p.tolist() for p in points[split_idx:]]
511
+
512
+ simplified_first = _simplify_open_path(first_half, tolerance)
513
+ simplified_second = _simplify_open_path(second_half, tolerance)
514
+
515
+ # Join halves (remove duplicate at split point)
516
+ return simplified_first[:-1] + simplified_second
517
+
518
+ return _simplify_open_path(path, tolerance)
519
+
520
+
521
+ def _simplify_open_path(path: List[List[float]], tolerance: float) -> List[List[float]]:
522
+ """
523
+ Simplify an open path using Douglas-Peucker algorithm.
524
+
525
+ Parameters
526
+ ----------
527
+ path : list
528
+ List of [x, y] points (open path, start != end)
529
+ tolerance : float
530
+ Maximum perpendicular distance in pixels
531
+
532
+ Returns
533
+ -------
534
+ list
535
+ Simplified path
536
+ """
537
+ if len(path) <= 2:
538
+ return path
539
+
540
+ points = np.array(path)
541
+ start = points[0]
542
+ end = points[-1]
543
+
544
+ line_vec = end - start
545
+ line_len = np.linalg.norm(line_vec)
546
+
547
+ if line_len < 1e-10:
548
+ return [path[0], path[-1]]
549
+
550
+ line_unit = line_vec / line_len
551
+
552
+ # Perpendicular distances
553
+ point_vecs = points - start
554
+ projections = np.dot(point_vecs, line_unit)
555
+ closest_points = start + np.outer(projections, line_unit)
556
+ distances = np.linalg.norm(points - closest_points, axis=1)
557
+
558
+ max_idx = np.argmax(distances)
559
+ max_dist = distances[max_idx]
560
+
561
+ if max_dist > tolerance:
562
+ # Recurse
563
+ left = _simplify_open_path([p.tolist() for p in points[:max_idx + 1]], tolerance)
564
+ right = _simplify_open_path([p.tolist() for p in points[max_idx:]], tolerance)
565
+ return left[:-1] + right
566
+ else:
567
+ return [path[0], path[-1]]
568
+
569
+
570
+ # EOF