scitex 2.8.1__py3-none-any.whl → 2.10.0__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 (415) hide show
  1. scitex/__init__.py +15 -7
  2. scitex/__version__.py +1 -2
  3. scitex/_install_guide.py +250 -0
  4. scitex/_optional_deps.py +206 -39
  5. scitex/ai/_gen_ai/_Groq.py +2 -4
  6. scitex/ai/_gen_ai/_OpenAI.py +5 -2
  7. scitex/ai/_gen_ai/_Perplexity.py +20 -6
  8. scitex/audio/__init__.py +24 -15
  9. scitex/audio/_cross_process_lock.py +139 -0
  10. scitex/audio/_mcp_handlers.py +256 -0
  11. scitex/audio/_mcp_tool_schemas.py +203 -0
  12. scitex/audio/engines/elevenlabs_engine.py +5 -2
  13. scitex/audio/mcp_server.py +98 -457
  14. scitex/bridge/__init__.py +30 -19
  15. scitex/bridge/_figrecipe.py +245 -0
  16. scitex/bridge/_helpers.py +2 -1
  17. scitex/bridge/_plt_vis.py +23 -10
  18. scitex/bridge/_stats_plt.py +18 -5
  19. scitex/bridge/_stats_vis.py +16 -2
  20. scitex/browser/__init__.py +84 -44
  21. scitex/browser/automation/__init__.py +5 -1
  22. scitex/browser/core/BrowserMixin.py +17 -4
  23. scitex/browser/core/__init__.py +11 -2
  24. scitex/browser/remote/CaptchaHandler.py +1 -1
  25. scitex/browser/remote/ZenRowsAPIClient.py +1 -1
  26. scitex/capture/grid.py +487 -0
  27. scitex/capture/mcp_handlers.py +401 -0
  28. scitex/capture/mcp_tool_defs.py +192 -0
  29. scitex/capture/mcp_tools.py +241 -0
  30. scitex/capture/mcp_utils.py +30 -0
  31. scitex/cli/convert.py +421 -0
  32. scitex/cli/main.py +6 -4
  33. scitex/datetime/__init__.py +46 -0
  34. scitex/datetime/_linspace.py +100 -0
  35. scitex/datetime/_normalize_timestamp.py +306 -0
  36. scitex/db/_delete_duplicates.py +4 -4
  37. scitex/db/_sqlite3/_delete_duplicates.py +11 -2
  38. scitex/dev/plt/__init__.py +61 -62
  39. scitex/dev/plt/demo_plotters/__init__.py +0 -0
  40. scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
  41. scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
  42. scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
  43. scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
  44. scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
  45. scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
  46. scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
  47. scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
  48. scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
  49. scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
  50. scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
  51. scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
  52. scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
  53. scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
  54. scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
  55. scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
  56. scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
  57. scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
  58. scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
  59. scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
  60. scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
  61. scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
  62. scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
  63. scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
  64. scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
  65. scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
  66. scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
  67. scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
  68. scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
  69. scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
  70. scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
  71. scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
  72. scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
  73. scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
  74. scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
  75. scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
  76. scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
  77. scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
  78. scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
  79. scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
  80. scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
  81. scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
  82. scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
  83. scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
  84. scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
  85. scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
  86. scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
  87. scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
  88. scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
  89. scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
  90. scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
  91. scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
  92. scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
  93. scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
  94. scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
  95. scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
  96. scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
  97. scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
  98. scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
  99. scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
  100. scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
  101. scitex/dev/plt/mpl/get_dir_ax.py +46 -0
  102. scitex/dev/plt/mpl/get_signatures.py +176 -0
  103. scitex/dev/plt/mpl/get_signatures_details.py +522 -0
  104. scitex/dict/_pop_keys.py +1 -7
  105. scitex/dsp/__init__.py +15 -10
  106. scitex/dsp/add_noise.py +5 -2
  107. scitex/dsp/example.py +35 -22
  108. scitex/dsp/filt.py +8 -3
  109. scitex/dsp/reference.py +3 -2
  110. scitex/dsp/utils/__init__.py +2 -1
  111. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  112. scitex/dt/__init__.py +39 -2
  113. scitex/errors.py +82 -521
  114. scitex/fig/__init__.py +4 -4
  115. scitex/fig/editor/edit/panel_loader.py +1 -1
  116. scitex/fig/io/_bundle.py +7 -7
  117. scitex/fts/README.md +262 -0
  118. scitex/fts/TODO.md +66 -0
  119. scitex/fts/__init__.py +90 -0
  120. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  121. scitex/fts/_bundle/_FTS.py +657 -0
  122. scitex/fts/_bundle/__init__.py +38 -0
  123. scitex/fts/_bundle/_children.py +216 -0
  124. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  125. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  126. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  127. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  128. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  129. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  130. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  131. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  132. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  133. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  134. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  135. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  136. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  137. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  138. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  139. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  140. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  141. scitex/fts/_bundle/_loader.py +134 -0
  142. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  143. scitex/fts/_bundle/_saver.py +269 -0
  144. scitex/fts/_bundle/_storage.py +200 -0
  145. scitex/fts/_bundle/_utils/__init__.py +55 -0
  146. scitex/fts/_bundle/_utils/_const.py +26 -0
  147. scitex/fts/_bundle/_utils/_errors.py +73 -0
  148. scitex/fts/_bundle/_utils/_generate.py +21 -0
  149. scitex/fts/_bundle/_utils/_types.py +76 -0
  150. scitex/fts/_bundle/_validation.py +434 -0
  151. scitex/fts/_bundle/_zipbundle.py +165 -0
  152. scitex/fts/_fig/__init__.py +22 -0
  153. scitex/fts/_fig/_backend/__init__.py +53 -0
  154. scitex/fts/_fig/_backend/_export.py +165 -0
  155. scitex/fts/_fig/_backend/_parser.py +188 -0
  156. scitex/fts/_fig/_backend/_render.py +538 -0
  157. scitex/fts/_fig/_composite.py +345 -0
  158. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  159. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  160. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  161. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  162. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  163. scitex/fts/_fig/_editor/__init__.py +14 -0
  164. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  165. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  166. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  167. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  168. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  169. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  170. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  171. scitex/fts/_fig/_editor/_defaults.py +300 -0
  172. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  173. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  174. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  175. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  176. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  177. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  178. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  179. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  180. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  181. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  182. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  183. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  184. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  185. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  186. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  187. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  188. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  189. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  190. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  191. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  192. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  193. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  194. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  195. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  196. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  197. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  198. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  199. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  200. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  201. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  202. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  203. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  204. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  205. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  206. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  207. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  208. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  209. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  210. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  211. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  212. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  213. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  214. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  215. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  216. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  217. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  218. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  219. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  220. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  221. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  222. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  223. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  224. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  225. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  226. scitex/fts/_fig/_models/_Annotations.py +115 -0
  227. scitex/fts/_fig/_models/_Axes.py +152 -0
  228. scitex/fts/_fig/_models/_Figure.py +138 -0
  229. scitex/fts/_fig/_models/_Guides.py +104 -0
  230. scitex/fts/_fig/_models/_Plot.py +123 -0
  231. scitex/fts/_fig/_models/_Styles.py +245 -0
  232. scitex/fts/_fig/_models/__init__.py +80 -0
  233. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  234. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  235. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  236. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  237. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  238. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  239. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  240. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  241. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  242. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  243. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  244. scitex/fts/_fig/_utils/__init__.py +129 -0
  245. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  246. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  247. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  248. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  249. scitex/fts/_fig/_utils/_get_template.py +178 -0
  250. scitex/fts/_fig/_utils/_normalize.py +73 -0
  251. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  252. scitex/fts/_fig/_utils/_validate.py +197 -0
  253. scitex/fts/_kinds/__init__.py +45 -0
  254. scitex/fts/_kinds/_figure/__init__.py +19 -0
  255. scitex/fts/_kinds/_figure/_composite.py +345 -0
  256. scitex/fts/_kinds/_plot/__init__.py +25 -0
  257. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  258. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  259. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  260. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  261. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  262. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  263. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  264. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  265. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  266. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  267. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  268. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  269. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  270. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  271. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  272. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  273. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  274. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  275. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  276. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  277. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  278. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  279. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  280. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  281. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  282. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  283. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  284. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  285. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  286. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  287. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  288. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  289. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  290. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  291. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  292. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  293. scitex/fts/_kinds/_shape/__init__.py +141 -0
  294. scitex/fts/_kinds/_stats/__init__.py +56 -0
  295. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  296. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  297. scitex/fts/_kinds/_table/__init__.py +72 -0
  298. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  299. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  300. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  301. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  302. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  303. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  304. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  305. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  306. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  307. scitex/fts/_kinds/_text/__init__.py +77 -0
  308. scitex/fts/_schemas/data_info.schema.json +75 -0
  309. scitex/fts/_schemas/encoding.schema.json +90 -0
  310. scitex/fts/_schemas/node.schema.json +145 -0
  311. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  312. scitex/fts/_schemas/stats.schema.json +132 -0
  313. scitex/fts/_schemas/theme.schema.json +141 -0
  314. scitex/fts/_stats/__init__.py +48 -0
  315. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  316. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  317. scitex/fts/_tables/__init__.py +65 -0
  318. scitex/fts/_tables/_latex/__init__.py +93 -0
  319. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  320. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  321. scitex/fts/_tables/_latex/_export.py +279 -0
  322. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  323. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  324. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  325. scitex/fts/_tables/_latex/_utils.py +369 -0
  326. scitex/fts/_tables/_latex/_validator.py +445 -0
  327. scitex/gen/__init__.py +66 -25
  328. scitex/gen/misc.py +28 -0
  329. scitex/io/__init__.py +47 -32
  330. scitex/io/_load.py +87 -36
  331. scitex/io/_load_modules/__init__.py +10 -7
  332. scitex/io/_load_modules/_pandas.py +6 -1
  333. scitex/io/_save.py +299 -1556
  334. scitex/io/_save_modules/__init__.py +76 -19
  335. scitex/io/_save_modules/_figure_utils.py +90 -0
  336. scitex/io/_save_modules/_image_csv.py +497 -0
  337. scitex/io/_save_modules/_legends.py +91 -0
  338. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  339. scitex/io/_save_modules/_pltz_stx.py +536 -0
  340. scitex/io/_save_modules/_stx_bundle.py +104 -0
  341. scitex/io/_save_modules/_symlink.py +96 -0
  342. scitex/io/_save_modules/_yaml.py +1 -1
  343. scitex/io/_save_modules/_zarr.py +64 -18
  344. scitex/io/bundle/README.md +212 -0
  345. scitex/io/bundle/__init__.py +110 -0
  346. scitex/io/{_bundle.py → bundle/_core.py} +168 -97
  347. scitex/io/bundle/_nested.py +713 -0
  348. scitex/io/bundle/_types.py +74 -0
  349. scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
  350. scitex/io/utils/h5_to_zarr.py +1 -1
  351. scitex/logging/__init__.py +108 -13
  352. scitex/logging/_errors.py +508 -0
  353. scitex/logging/_formatters.py +30 -6
  354. scitex/logging/_warnings.py +261 -0
  355. scitex/plt/__init__.py +4 -1
  356. scitex/plt/_figrecipe.py +236 -0
  357. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  358. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  359. scitex/plt/_subplots/_FigWrapper.py +15 -0
  360. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  361. scitex/plt/_subplots/_export_as_csv.py +11 -0
  362. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  365. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  366. scitex/plt/_subplots/_fonts.py +71 -0
  367. scitex/plt/_subplots/_mm_layout.py +282 -0
  368. scitex/plt/gallery/__init__.py +99 -2
  369. scitex/plt/styles/_plot_postprocess.py +3 -1
  370. scitex/plt/utils/_configure_mpl.py +16 -19
  371. scitex/repro/_RandomStateManager.py +13 -8
  372. scitex/resource/__init__.py +19 -1
  373. scitex/resource/_utils/_get_env_info.py +13 -25
  374. scitex/schema/__init__.py +149 -160
  375. scitex/schema/_encoding.py +273 -0
  376. scitex/schema/_figure_elements.py +406 -0
  377. scitex/schema/_theme.py +360 -0
  378. scitex/schema/_validation.py +0 -98
  379. scitex/scholar/__init__.py +56 -14
  380. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  381. scitex/scholar/auth/__init__.py +11 -2
  382. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  383. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  384. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  385. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  386. scitex/scholar/config/ScholarConfig.py +1 -1
  387. scitex/scholar/core/Scholar.py +1 -1
  388. scitex/session/_decorator.py +18 -16
  389. scitex/session/_lifecycle.py +9 -11
  390. scitex/session/template.py +9 -8
  391. scitex/sh/test_sh.py +72 -0
  392. scitex/sh/test_sh_simple.py +61 -0
  393. scitex/stats/__init__.py +221 -97
  394. scitex/stats/_schema.py +21 -22
  395. scitex/stats/descriptive/_circular.py +212 -351
  396. scitex/stats/descriptive/_describe.py +81 -132
  397. scitex/stats/descriptive/_nan.py +205 -433
  398. scitex/stats/descriptive/_real.py +127 -141
  399. scitex/str/_format_plot_text.py +5 -5
  400. scitex/str/_latex.py +26 -84
  401. scitex/str/_latex_fallback.py +53 -47
  402. scitex/web/_search_pubmed.py +5 -4
  403. scitex/writer/tests/test_diff_between.py +451 -0
  404. scitex/writer/tests/test_document_section.py +311 -0
  405. scitex/writer/tests/test_document_workflow.py +393 -0
  406. scitex/writer/tests/test_writer.py +361 -0
  407. scitex/writer/tests/test_writer_integration.py +303 -0
  408. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
  409. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/RECORD +412 -97
  410. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  411. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  412. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  413. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
  414. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
  415. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_models/_plot_types/_image.py
4
+
5
+ """Image and heatmap configurations."""
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any, List, Optional, Union
9
+
10
+
11
+ @dataclass
12
+ class ImshowConfig:
13
+ """Image display configuration."""
14
+
15
+ img: Any
16
+ cmap: Optional[str] = "viridis"
17
+ aspect: str = "auto"
18
+ interpolation: Optional[str] = None
19
+ vmin: Optional[float] = None
20
+ vmax: Optional[float] = None
21
+ origin: str = "upper"
22
+ id: Optional[str] = None
23
+
24
+
25
+ @dataclass
26
+ class ContourConfig:
27
+ """Contour plot configuration."""
28
+
29
+ x: List[float]
30
+ y: List[float]
31
+ z: Any
32
+ levels: Optional[Union[int, List[float]]] = None
33
+ filled: bool = False
34
+ cmap: Optional[str] = None
35
+ colors: Optional[str] = None
36
+ linewidths: Optional[float] = None
37
+ alpha: Optional[float] = None
38
+ vmin: Optional[float] = None
39
+ vmax: Optional[float] = None
40
+ id: Optional[str] = None
41
+
42
+
43
+ @dataclass
44
+ class HeatmapConfig:
45
+ """Heatmap configuration (scitex.plt.ax.stx_heatmap)."""
46
+
47
+ data: Any
48
+ x_labels: Optional[List[str]] = None
49
+ y_labels: Optional[List[str]] = None
50
+ cbar_label: Optional[str] = None
51
+ cmap: str = "viridis"
52
+ vmin: Optional[float] = None
53
+ vmax: Optional[float] = None
54
+ show_annot: bool = False
55
+ value_format: str = "{x:.2f}"
56
+ id: Optional[str] = None
57
+
58
+
59
+ __all__ = ["ImshowConfig", "ContourConfig", "HeatmapConfig"]
60
+
61
+ # EOF
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_models/_plot_types/_line.py
4
+
5
+ """Line plot configurations."""
6
+
7
+ from dataclasses import dataclass
8
+ from typing import List, Optional
9
+
10
+
11
+ @dataclass
12
+ class LinePlotConfig:
13
+ """Line plot configuration."""
14
+
15
+ x: List[float]
16
+ y: List[float]
17
+ color: Optional[str] = None
18
+ linewidth: Optional[float] = None
19
+ linestyle: Optional[str] = "-"
20
+ marker: Optional[str] = None
21
+ markersize: Optional[float] = None
22
+ alpha: Optional[float] = None
23
+ label: Optional[str] = None
24
+ id: Optional[str] = None
25
+
26
+
27
+ @dataclass
28
+ class PlotLineConfig:
29
+ """Plot line configuration (scitex.plt.ax.stx_line)."""
30
+
31
+ y: List[float]
32
+ x: Optional[List[float]] = None
33
+ color: Optional[str] = None
34
+ linewidth_mm: Optional[float] = None
35
+ linestyle: Optional[str] = "-"
36
+ label: Optional[str] = None
37
+ id: Optional[str] = None
38
+
39
+
40
+ @dataclass
41
+ class ShadedLineConfig:
42
+ """Shaded line configuration (scitex.plt.ax.stx_shaded_line)."""
43
+
44
+ x: List[float]
45
+ y_lower: List[float]
46
+ y_middle: List[float]
47
+ y_upper: List[float]
48
+ color: Optional[str] = None
49
+ alpha: Optional[float] = 0.3
50
+ linewidth_mm: Optional[float] = None
51
+ label: Optional[str] = None
52
+ id: Optional[str] = None
53
+
54
+
55
+ __all__ = ["LinePlotConfig", "PlotLineConfig", "ShadedLineConfig"]
56
+
57
+ # EOF
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_models/_plot_types/_scatter.py
4
+
5
+ """Scatter plot configurations."""
6
+
7
+ from dataclasses import dataclass
8
+ from typing import List, Optional, Union
9
+
10
+
11
+ @dataclass
12
+ class ScatterPlotConfig:
13
+ """Scatter plot configuration."""
14
+
15
+ x: List[float]
16
+ y: List[float]
17
+ color: Optional[str] = None
18
+ size_mm: Optional[float] = None
19
+ s: Optional[float] = None
20
+ marker: Optional[str] = "o"
21
+ alpha: Optional[float] = None
22
+ cmap: Optional[str] = None
23
+ c: Optional[Union[str, List[float]]] = None
24
+ label: Optional[str] = None
25
+ id: Optional[str] = None
26
+
27
+
28
+ __all__ = ["ScatterPlotConfig"]
29
+
30
+ # EOF
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_models/_plot_types/_seaborn.py
4
+
5
+ """Seaborn plot configurations."""
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any, Optional, Union
9
+
10
+
11
+ @dataclass
12
+ class SeabornBoxplotConfig:
13
+ """Seaborn boxplot configuration."""
14
+
15
+ x: Optional[str] = None
16
+ y: Optional[str] = None
17
+ data: Optional[Any] = None
18
+ hue: Optional[str] = None
19
+ palette: Optional[str] = None
20
+ id: Optional[str] = None
21
+
22
+
23
+ @dataclass
24
+ class SeabornViolinplotConfig:
25
+ """Seaborn violinplot configuration."""
26
+
27
+ x: Optional[str] = None
28
+ y: Optional[str] = None
29
+ data: Optional[Any] = None
30
+ hue: Optional[str] = None
31
+ palette: Optional[str] = None
32
+ id: Optional[str] = None
33
+
34
+
35
+ @dataclass
36
+ class SeabornScatterplotConfig:
37
+ """Seaborn scatterplot configuration."""
38
+
39
+ x: Optional[str] = None
40
+ y: Optional[str] = None
41
+ data: Optional[Any] = None
42
+ hue: Optional[str] = None
43
+ size: Optional[str] = None
44
+ style: Optional[str] = None
45
+ palette: Optional[str] = None
46
+ id: Optional[str] = None
47
+
48
+
49
+ @dataclass
50
+ class SeabornLineplotConfig:
51
+ """Seaborn lineplot configuration."""
52
+
53
+ x: Optional[str] = None
54
+ y: Optional[str] = None
55
+ data: Optional[Any] = None
56
+ hue: Optional[str] = None
57
+ style: Optional[str] = None
58
+ palette: Optional[str] = None
59
+ id: Optional[str] = None
60
+
61
+
62
+ @dataclass
63
+ class SeabornHistplotConfig:
64
+ """Seaborn histplot configuration."""
65
+
66
+ x: Optional[str] = None
67
+ data: Optional[Any] = None
68
+ hue: Optional[str] = None
69
+ bins: Union[int, str] = "auto"
70
+ kde: bool = False
71
+ alpha: Optional[float] = None
72
+ id: Optional[str] = None
73
+
74
+
75
+ @dataclass
76
+ class SeabornBarplotConfig:
77
+ """Seaborn barplot configuration."""
78
+
79
+ x: Optional[str] = None
80
+ y: Optional[str] = None
81
+ data: Optional[Any] = None
82
+ hue: Optional[str] = None
83
+ palette: Optional[str] = None
84
+ estimator: str = "mean"
85
+ id: Optional[str] = None
86
+
87
+
88
+ @dataclass
89
+ class SeabornStripplotConfig:
90
+ """Seaborn stripplot configuration."""
91
+
92
+ x: Optional[str] = None
93
+ y: Optional[str] = None
94
+ data: Optional[Any] = None
95
+ hue: Optional[str] = None
96
+ alpha: Optional[float] = None
97
+ id: Optional[str] = None
98
+
99
+
100
+ @dataclass
101
+ class SeabornKDEplotConfig:
102
+ """Seaborn KDE plot configuration."""
103
+
104
+ x: Optional[str] = None
105
+ data: Optional[Any] = None
106
+ hue: Optional[str] = None
107
+ fill: bool = False
108
+
109
+
110
+ __all__ = [
111
+ "SeabornBoxplotConfig",
112
+ "SeabornViolinplotConfig",
113
+ "SeabornScatterplotConfig",
114
+ "SeabornLineplotConfig",
115
+ "SeabornHistplotConfig",
116
+ "SeabornBarplotConfig",
117
+ "SeabornStripplotConfig",
118
+ "SeabornKDEplotConfig",
119
+ ]
120
+
121
+ # EOF
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_models/_plot_types/_violin.py
4
+
5
+ """Violin plot configurations."""
6
+
7
+ from dataclasses import dataclass
8
+ from typing import List, Optional
9
+
10
+
11
+ @dataclass
12
+ class ViolinPlotConfig:
13
+ """Violin plot configuration."""
14
+
15
+ data: List[List[float]]
16
+ positions: Optional[List[float]] = None
17
+ widths: Optional[float] = 0.5
18
+ showmeans: bool = False
19
+ showmedians: bool = False
20
+ showextrema: bool = True
21
+ id: Optional[str] = None
22
+
23
+
24
+ @dataclass
25
+ class ViolinConfig:
26
+ """Violin plot configuration (scitex.plt.ax.stx_violin)."""
27
+
28
+ data: List[List[float]]
29
+ labels: Optional[List[str]] = None
30
+ colors: Optional[List[str]] = None
31
+ id: Optional[str] = None
32
+
33
+
34
+ __all__ = ["ViolinPlotConfig", "ViolinConfig"]
35
+
36
+ # EOF
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_utils/__init__.py
4
+
5
+ """Utilities for FTS figures - layout, defaults, validation."""
6
+
7
+ # Constants
8
+ from ._const_sizes import (
9
+ A4_HEIGHT_MM,
10
+ A4_WIDTH_MM,
11
+ CELL_DOUBLE_COLUMN_MM,
12
+ CELL_SINGLE_COLUMN_MM,
13
+ DEFAULT_MARGIN_MM,
14
+ DEFAULT_SPACING_MM,
15
+ NATURE_DOUBLE_COLUMN_MM,
16
+ NATURE_FULL_PAGE_MM,
17
+ NATURE_SINGLE_COLUMN_MM,
18
+ PNAS_DOUBLE_COLUMN_MM,
19
+ PNAS_SINGLE_COLUMN_MM,
20
+ SCIENCE_DOUBLE_COLUMN_MM,
21
+ SCIENCE_SINGLE_COLUMN_MM,
22
+ )
23
+
24
+ # Templates
25
+ from ._get_template import (
26
+ TEMPLATES,
27
+ get_a4_figure,
28
+ get_nature_double_column,
29
+ get_nature_single_column,
30
+ get_presentation_slide,
31
+ get_science_single_column,
32
+ get_square_figure,
33
+ get_template,
34
+ list_templates,
35
+ )
36
+
37
+ # Normalization utilities
38
+ from ._normalize import (
39
+ DEFAULT_POSITION,
40
+ DEFAULT_SIZE,
41
+ Position,
42
+ Size,
43
+ normalize_position,
44
+ normalize_size,
45
+ )
46
+
47
+ # Coordinate conversion
48
+ from ._convert_coords import to_absolute, to_relative
49
+
50
+ # Bounds calculation
51
+ from ._calc_bounds import (
52
+ Bounds,
53
+ content_bounds,
54
+ element_bounds,
55
+ validate_within_bounds,
56
+ )
57
+
58
+ # Auto layout
59
+ from ._auto_layout import auto_crop_layout, auto_layout_grid
60
+
61
+ # Layout visualization
62
+ from ._plot_layout import BLUEPRINT_STYLE, plot_auto_crop_comparison, plot_layout
63
+
64
+ # Validation
65
+ from ._validate import (
66
+ check_schema_version,
67
+ validate_axes_layout,
68
+ validate_color,
69
+ validate_json_structure,
70
+ validate_plot_data,
71
+ )
72
+
73
+ __all__ = [
74
+ # Constants
75
+ "NATURE_SINGLE_COLUMN_MM",
76
+ "NATURE_DOUBLE_COLUMN_MM",
77
+ "NATURE_FULL_PAGE_MM",
78
+ "SCIENCE_SINGLE_COLUMN_MM",
79
+ "SCIENCE_DOUBLE_COLUMN_MM",
80
+ "CELL_SINGLE_COLUMN_MM",
81
+ "CELL_DOUBLE_COLUMN_MM",
82
+ "PNAS_SINGLE_COLUMN_MM",
83
+ "PNAS_DOUBLE_COLUMN_MM",
84
+ "A4_WIDTH_MM",
85
+ "A4_HEIGHT_MM",
86
+ "DEFAULT_MARGIN_MM",
87
+ "DEFAULT_SPACING_MM",
88
+ # Templates
89
+ "get_nature_single_column",
90
+ "get_nature_double_column",
91
+ "get_science_single_column",
92
+ "get_a4_figure",
93
+ "get_square_figure",
94
+ "get_presentation_slide",
95
+ "get_template",
96
+ "list_templates",
97
+ "TEMPLATES",
98
+ # Type aliases
99
+ "Position",
100
+ "Size",
101
+ "Bounds",
102
+ "DEFAULT_POSITION",
103
+ "DEFAULT_SIZE",
104
+ # Normalization
105
+ "normalize_position",
106
+ "normalize_size",
107
+ # Coordinate conversion
108
+ "to_absolute",
109
+ "to_relative",
110
+ # Bounds calculation
111
+ "element_bounds",
112
+ "content_bounds",
113
+ "validate_within_bounds",
114
+ # Auto layout
115
+ "auto_layout_grid",
116
+ "auto_crop_layout",
117
+ # Layout visualization
118
+ "plot_layout",
119
+ "plot_auto_crop_comparison",
120
+ "BLUEPRINT_STYLE",
121
+ # Validation
122
+ "validate_json_structure",
123
+ "validate_plot_data",
124
+ "check_schema_version",
125
+ "validate_color",
126
+ "validate_axes_layout",
127
+ ]
128
+
129
+ # EOF
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_utils/_auto_layout.py
4
+
5
+ """Auto layout utilities for FTS elements."""
6
+
7
+ from typing import Any, Dict, List, Optional, Tuple
8
+
9
+ from ._calc_bounds import content_bounds
10
+ from ._normalize import Position, Size, normalize_position, normalize_size
11
+
12
+
13
+ def auto_layout_grid(
14
+ n_elements: int,
15
+ container_size: Size,
16
+ margin_mm: float = 5.0,
17
+ spacing_mm: float = 5.0,
18
+ max_cols: Optional[int] = None,
19
+ ) -> List[Tuple[Position, Size]]:
20
+ """Generate grid layout positions and sizes for n elements.
21
+
22
+ Args:
23
+ n_elements: Number of elements to layout
24
+ container_size: Container size
25
+ margin_mm: Margin from container edges
26
+ spacing_mm: Spacing between elements
27
+ max_cols: Maximum columns (None = auto)
28
+
29
+ Returns:
30
+ List of (position, size) tuples for each element
31
+
32
+ Example:
33
+ >>> layouts = auto_layout_grid(4, {"width_mm": 170, "height_mm": 120})
34
+ >>> len(layouts)
35
+ 4
36
+ """
37
+ if n_elements <= 0:
38
+ return []
39
+
40
+ container = normalize_size(container_size)
41
+
42
+ # Determine grid dimensions
43
+ if max_cols is None:
44
+ cols = min(n_elements, 2) # Default: max 2 columns
45
+ else:
46
+ cols = min(n_elements, max_cols)
47
+ rows = (n_elements + cols - 1) // cols
48
+
49
+ # Calculate element size
50
+ available_width = container["width_mm"] - 2 * margin_mm - (cols - 1) * spacing_mm
51
+ available_height = container["height_mm"] - 2 * margin_mm - (rows - 1) * spacing_mm
52
+ elem_width = available_width / cols
53
+ elem_height = available_height / rows
54
+
55
+ results = []
56
+ for i in range(n_elements):
57
+ row = i // cols
58
+ col = i % cols
59
+
60
+ pos: Position = {
61
+ "x_mm": margin_mm + col * (elem_width + spacing_mm),
62
+ "y_mm": margin_mm + row * (elem_height + spacing_mm),
63
+ }
64
+ size: Size = {
65
+ "width_mm": elem_width,
66
+ "height_mm": elem_height,
67
+ }
68
+ results.append((pos, size))
69
+
70
+ return results
71
+
72
+
73
+ def auto_crop_layout(
74
+ elements: List[Dict[str, Any]],
75
+ margin_mm: float = 5.0,
76
+ ) -> Tuple[List[Dict[str, Any]], Size]:
77
+ """Calculate auto-cropped layout by shifting elements and resizing canvas.
78
+
79
+ This function:
80
+ 1. Finds the bounding box of all elements
81
+ 2. Shifts all positions so content starts at (margin, margin)
82
+ 3. Calculates new canvas size to fit content + margin
83
+
84
+ Args:
85
+ elements: List of element specifications (not modified in place)
86
+ margin_mm: Margin to add around content (default: 5mm)
87
+
88
+ Returns:
89
+ Tuple of (shifted_elements, new_canvas_size)
90
+ - shifted_elements: New list with adjusted positions
91
+ - new_canvas_size: {"width_mm", "height_mm"} for the cropped canvas
92
+ """
93
+ if not elements:
94
+ return [], {"width_mm": margin_mm * 2, "height_mm": margin_mm * 2}
95
+
96
+ # Calculate content bounding box
97
+ bounds = content_bounds(elements)
98
+ if bounds is None:
99
+ return [], {"width_mm": margin_mm * 2, "height_mm": margin_mm * 2}
100
+
101
+ # Calculate offset to shift content to (margin, margin)
102
+ offset_x = bounds["x_mm"] - margin_mm
103
+ offset_y = bounds["y_mm"] - margin_mm
104
+
105
+ # Shift all element positions
106
+ shifted_elements = []
107
+ for elem in elements:
108
+ shifted = elem.copy()
109
+ pos = normalize_position(elem.get("position"))
110
+ shifted["position"] = {
111
+ "x_mm": pos["x_mm"] - offset_x,
112
+ "y_mm": pos["y_mm"] - offset_y,
113
+ }
114
+ shifted_elements.append(shifted)
115
+
116
+ # Calculate new canvas size
117
+ new_size: Size = {
118
+ "width_mm": bounds["width_mm"] + margin_mm * 2,
119
+ "height_mm": bounds["height_mm"] + margin_mm * 2,
120
+ }
121
+
122
+ return shifted_elements, new_size
123
+
124
+
125
+ __all__ = ["auto_layout_grid", "auto_crop_layout"]
126
+
127
+ # EOF
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_utils/_calc_bounds.py
4
+
5
+ """Bounding box calculation utilities for FTS elements."""
6
+
7
+ from typing import Any, Dict, List, Optional
8
+
9
+ from ._normalize import Size, normalize_position, normalize_size
10
+
11
+ # Type alias
12
+ Bounds = Dict[str, float] # {"x_mm", "y_mm", "width_mm", "height_mm"}
13
+
14
+
15
+ def element_bounds(element: Dict[str, Any]) -> Bounds:
16
+ """Get element bounding box.
17
+
18
+ Args:
19
+ element: Element specification with position and size
20
+
21
+ Returns:
22
+ Bounding box {x_mm, y_mm, width_mm, height_mm}
23
+ """
24
+ pos = normalize_position(element.get("position"))
25
+ size = normalize_size(element.get("size"))
26
+ return {
27
+ "x_mm": pos["x_mm"],
28
+ "y_mm": pos["y_mm"],
29
+ "width_mm": size["width_mm"],
30
+ "height_mm": size["height_mm"],
31
+ }
32
+
33
+
34
+ def content_bounds(elements: List[Dict[str, Any]]) -> Optional[Bounds]:
35
+ """Calculate the bounding box containing all elements.
36
+
37
+ Args:
38
+ elements: List of element specifications
39
+
40
+ Returns:
41
+ Bounding box {"x_mm", "y_mm", "width_mm", "height_mm"}
42
+ containing all elements, or None if no elements.
43
+ """
44
+ if not elements:
45
+ return None
46
+
47
+ min_x = float("inf")
48
+ min_y = float("inf")
49
+ max_x = float("-inf")
50
+ max_y = float("-inf")
51
+
52
+ for elem in elements:
53
+ bounds = element_bounds(elem)
54
+
55
+ # Update min/max coordinates
56
+ min_x = min(min_x, bounds["x_mm"])
57
+ min_y = min(min_y, bounds["y_mm"])
58
+ max_x = max(max_x, bounds["x_mm"] + bounds["width_mm"])
59
+ max_y = max(max_y, bounds["y_mm"] + bounds["height_mm"])
60
+
61
+ return {
62
+ "x_mm": min_x,
63
+ "y_mm": min_y,
64
+ "width_mm": max_x - min_x,
65
+ "height_mm": max_y - min_y,
66
+ }
67
+
68
+
69
+ def validate_within_bounds(
70
+ element: Dict[str, Any],
71
+ container_size: Size,
72
+ raise_error: bool = False,
73
+ ) -> bool:
74
+ """Check if element fits within container bounds.
75
+
76
+ Args:
77
+ element: Element with position and size
78
+ container_size: Container size {"width_mm", "height_mm"}
79
+ raise_error: If True, raise ValueError on violation
80
+
81
+ Returns:
82
+ True if element fits within container
83
+
84
+ Raises:
85
+ ValueError: If raise_error=True and element exceeds bounds
86
+ """
87
+ bounds = element_bounds(element)
88
+ container = normalize_size(container_size)
89
+
90
+ # Check bounds
91
+ fits = (
92
+ bounds["x_mm"] >= 0
93
+ and bounds["y_mm"] >= 0
94
+ and bounds["x_mm"] + bounds["width_mm"] <= container["width_mm"]
95
+ and bounds["y_mm"] + bounds["height_mm"] <= container["height_mm"]
96
+ )
97
+
98
+ if not fits and raise_error:
99
+ raise ValueError(
100
+ f"Element exceeds container bounds: "
101
+ f"element=({bounds['x_mm']}, {bounds['y_mm']}, "
102
+ f"{bounds['width_mm']}x{bounds['height_mm']}), "
103
+ f"container=({container['width_mm']}x{container['height_mm']})"
104
+ )
105
+
106
+ return fits
107
+
108
+
109
+ __all__ = ["element_bounds", "content_bounds", "validate_within_bounds", "Bounds"]
110
+
111
+ # EOF