scitex 2.10.3__py3-none-any.whl → 2.11.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 (363) hide show
  1. scitex/__init__.py +1 -4
  2. scitex/__version__.py +1 -1
  3. scitex/_install_guide.py +14 -2
  4. scitex/bridge/_figrecipe.py +1 -1
  5. scitex/bridge/_helpers.py +1 -1
  6. scitex/bridge/_plt_vis.py +1 -1
  7. scitex/bridge/_stats_plt.py +1 -1
  8. scitex/bridge/_stats_vis.py +2 -2
  9. scitex/{fig → canvas}/__init__.py +84 -96
  10. scitex/{fig → canvas}/backend/_parser.py +1 -1
  11. scitex/{fig → canvas}/canvas.py +13 -14
  12. scitex/{fts/_fig/_editor → canvas/editor}/_defaults.py +2 -2
  13. scitex/{fig → canvas}/editor/edit/__init__.py +11 -14
  14. scitex/{fig → canvas}/editor/edit/bundle_resolver.py +56 -48
  15. scitex/{fig → canvas}/editor/edit/editor_launcher.py +79 -26
  16. scitex/{fts/_fig/_editor/_cui/_panel_loader.py → canvas/editor/edit/panel_loader.py} +8 -8
  17. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_bbox.py +2 -1
  18. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_core.py +84 -84
  19. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_renderer.py +7 -6
  20. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/static/css/features/canvas.css +2 -2
  21. scitex/{fig → canvas}/editor/flask_editor/static/css/features/panel-grid.css +1 -1
  22. scitex/{fig → canvas}/editor/flask_editor/static/js/core/api.js +3 -4
  23. scitex/{fig → canvas}/editor/flask_editor/static/js/editor/preview.js +5 -5
  24. scitex/{fig → canvas}/editor/flask_editor/templates/_html.py +3 -3
  25. scitex/{fig → canvas}/editor/flask_editor/templates/_scripts.py +10 -10
  26. scitex/{fig → canvas}/editor/flask_editor/templates/_styles.py +3 -3
  27. scitex/{fig → canvas}/io/__init__.py +32 -38
  28. scitex/{fig → canvas}/io/_bundle.py +217 -154
  29. scitex/{fig → canvas}/io/_canvas.py +1 -1
  30. scitex/{fig → canvas}/io/_data.py +1 -1
  31. scitex/{fig → canvas}/io/_export.py +1 -1
  32. scitex/{fig → canvas}/io/_load.py +1 -1
  33. scitex/{fig → canvas}/io/_panel.py +1 -1
  34. scitex/{fig → canvas}/io/_save.py +1 -1
  35. scitex/{fig → canvas}/model/__init__.py +1 -1
  36. scitex/{fig → canvas}/model/_annotations.py +1 -1
  37. scitex/{fig → canvas}/model/_axes.py +1 -1
  38. scitex/{fig → canvas}/model/_figure.py +1 -1
  39. scitex/{fig → canvas}/model/_guides.py +1 -1
  40. scitex/{fig → canvas}/model/_plot.py +1 -1
  41. scitex/{fig → canvas}/model/_styles.py +1 -1
  42. scitex/{fig → canvas}/utils/__init__.py +1 -1
  43. scitex/cli/convert.py +10 -6
  44. scitex/diagram/README.md +7 -7
  45. scitex/io/__init__.py +7 -19
  46. scitex/io/_load.py +15 -19
  47. scitex/io/_load_modules/_canvas.py +2 -2
  48. scitex/io/_load_modules/_con.py +5 -5
  49. scitex/io/_load_modules/_eeg.py +16 -12
  50. scitex/io/_save.py +11 -16
  51. scitex/io/_save_modules/__init__.py +6 -10
  52. scitex/io/_save_modules/_canvas.py +3 -3
  53. scitex/io/_save_modules/_plot_bundle.py +112 -0
  54. scitex/io/_save_modules/{_pltz_stx.py → _plot_scitex.py} +7 -7
  55. scitex/io/_save_modules/_stx_bundle.py +16 -16
  56. scitex/io/bundle/README.md +89 -80
  57. scitex/{fts/_bundle/_FTS.py → io/bundle/_Bundle.py} +197 -95
  58. scitex/io/bundle/__init__.py +67 -35
  59. scitex/{fts/_bundle → io/bundle}/_children.py +32 -40
  60. scitex/io/bundle/_core.py +184 -97
  61. scitex/{fts/_bundle/_dataclasses/_Node.py → io/bundle/_dataclasses/_Spec.py} +29 -23
  62. scitex/{fts/_bundle/_dataclasses/_NodeRefs.py → io/bundle/_dataclasses/_SpecRefs.py} +6 -6
  63. scitex/{fts/_bundle → io/bundle}/_dataclasses/__init__.py +4 -4
  64. scitex/{fts/_bundle → io/bundle}/_loader.py +19 -19
  65. scitex/io/bundle/_manifest.py +99 -0
  66. scitex/{fts/_bundle → io/bundle}/_mpl_helpers.py +119 -28
  67. scitex/io/bundle/_nested.py +113 -100
  68. scitex/{fts/_bundle → io/bundle}/_saver.py +13 -14
  69. scitex/{fts/_bundle → io/bundle}/_storage.py +3 -3
  70. scitex/io/bundle/_types.py +41 -16
  71. scitex/{fts/_bundle → io/bundle}/_validation.py +20 -18
  72. scitex/io/bundle/_zip.py +21 -31
  73. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_backend/_parser.py +1 -1
  74. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Annotations.py +1 -1
  75. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Axes.py +1 -1
  76. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Figure.py +1 -1
  77. scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Guides.py +1 -1
  78. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Plot.py +1 -1
  79. scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Styles.py +1 -1
  80. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_utils/_plot_layout.py +1 -1
  81. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/__init__.py +1 -1
  82. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/_app.py +1 -1
  83. scitex/{fts/_tables → io/bundle/kinds/_table}/_latex/_export.py +1 -1
  84. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_figure_exporter.py +1 -1
  85. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_table_exporter.py +1 -1
  86. scitex/io/bundle/schemas/__init__.py +30 -0
  87. scitex/parallel/_run.py +5 -4
  88. scitex/path/_find.py +60 -83
  89. scitex/path/_get_module_path.py +23 -21
  90. scitex/path/_get_spath.py +6 -27
  91. scitex/path/_getsize.py +23 -9
  92. scitex/path/_increment_version.py +31 -38
  93. scitex/path/_mk_spath.py +26 -29
  94. scitex/path/_path.py +5 -12
  95. scitex/path/_split.py +27 -15
  96. scitex/path/_this_path.py +23 -9
  97. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +2 -1
  98. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +2 -2
  99. scitex/plt/gallery/_generate.py +76 -50
  100. scitex/plt/io/__init__.py +17 -19
  101. scitex/plt/io/_bundle.py +99 -52
  102. scitex/plt/io/_layered_bundle.py +303 -168
  103. scitex/plt/utils/_csv_column_naming.py +250 -118
  104. scitex/schema/__init__.py +69 -73
  105. scitex/schema/_canvas.py +1 -1
  106. scitex/schema/_stats.py +2 -2
  107. scitex/stats/__init__.py +30 -33
  108. scitex/stats/_schema.py +1 -1
  109. scitex/stats/io/__init__.py +10 -11
  110. scitex/stats/io/_bundle.py +16 -16
  111. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/METADATA +190 -73
  112. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/RECORD +237 -360
  113. scitex/fig/editor/_defaults.py +0 -300
  114. scitex/fig/editor/edit/panel_loader.py +0 -232
  115. scitex/fig/editor/flask_editor/_bbox.py +0 -1299
  116. scitex/fig/editor/flask_editor/_core.py +0 -1429
  117. scitex/fig/editor/flask_editor/_renderer.py +0 -813
  118. scitex/fig/editor/flask_editor/static/css/features/canvas.css +0 -176
  119. scitex/fts/README.md +0 -262
  120. scitex/fts/TODO.md +0 -66
  121. scitex/fts/__init__.py +0 -90
  122. scitex/fts/_bundle/README_IN_BUNDLE.md +0 -102
  123. scitex/fts/_bundle/__init__.py +0 -38
  124. scitex/fts/_bundle/_utils/__init__.py +0 -55
  125. scitex/fts/_bundle/_utils/_const.py +0 -26
  126. scitex/fts/_bundle/_utils/_errors.py +0 -73
  127. scitex/fts/_bundle/_utils/_generate.py +0 -21
  128. scitex/fts/_bundle/_utils/_types.py +0 -76
  129. scitex/fts/_bundle/_zipbundle.py +0 -165
  130. scitex/fts/_fig/__init__.py +0 -22
  131. scitex/fts/_fig/_backend/_parser.py +0 -188
  132. scitex/fts/_fig/_editor/__init__.py +0 -14
  133. scitex/fts/_fig/_editor/_cui/__init__.py +0 -33
  134. scitex/fts/_fig/_editor/_cui/_backend_detector.py +0 -39
  135. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +0 -366
  136. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +0 -175
  137. scitex/fts/_fig/_editor/_cui/_manual_handler.py +0 -52
  138. scitex/fts/_fig/_editor/_cui/_path_resolver.py +0 -66
  139. scitex/fts/_fig/_editor/_gui/__init__.py +0 -11
  140. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +0 -20
  141. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +0 -664
  142. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +0 -79
  143. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +0 -41
  144. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +0 -16
  145. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +0 -85
  146. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +0 -217
  147. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +0 -93
  148. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +0 -57
  149. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +0 -112
  150. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +0 -59
  151. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +0 -212
  152. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +0 -190
  153. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +0 -59
  154. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +0 -45
  155. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +0 -95
  156. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +0 -101
  157. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +0 -138
  158. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +0 -31
  159. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +0 -7
  160. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +0 -56
  161. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +0 -78
  162. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +0 -314
  163. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +0 -107
  164. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +0 -54
  165. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +0 -172
  166. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +0 -258
  167. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +0 -48
  168. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +0 -71
  169. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +0 -288
  170. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +0 -143
  171. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +0 -245
  172. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +0 -992
  173. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +0 -339
  174. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +0 -286
  175. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +0 -371
  176. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +0 -293
  177. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +0 -426
  178. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +0 -152
  179. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +0 -265
  180. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +0 -184
  181. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +0 -57
  182. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +0 -100
  183. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +0 -34
  184. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +0 -124
  185. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +0 -851
  186. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +0 -4932
  187. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +0 -1657
  188. scitex/fts/_fig/_editor/_gui/_flask_editor.py +0 -36
  189. scitex/fts/_fig/_models/_Annotations.py +0 -115
  190. scitex/fts/_fig/_models/_Axes.py +0 -152
  191. scitex/fts/_fig/_models/_Figure.py +0 -138
  192. scitex/fts/_fig/_models/_Plot.py +0 -123
  193. scitex/fts/_fig/_utils/_plot_layout.py +0 -397
  194. scitex/fts/_kinds/_figure/_composite.py +0 -345
  195. scitex/fts/_kinds/_plot/_backend/__init__.py +0 -53
  196. scitex/fts/_kinds/_plot/_backend/_export.py +0 -165
  197. scitex/fts/_kinds/_plot/_backend/_render.py +0 -538
  198. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +0 -46
  199. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +0 -82
  200. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +0 -441
  201. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +0 -52
  202. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +0 -47
  203. scitex/fts/_kinds/_plot/_models/_Guides.py +0 -104
  204. scitex/fts/_kinds/_plot/_models/_Styles.py +0 -245
  205. scitex/fts/_kinds/_plot/_models/__init__.py +0 -80
  206. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +0 -156
  207. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +0 -43
  208. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +0 -38
  209. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +0 -36
  210. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +0 -60
  211. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +0 -30
  212. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +0 -61
  213. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +0 -57
  214. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +0 -30
  215. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +0 -121
  216. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +0 -36
  217. scitex/fts/_kinds/_plot/_utils/__init__.py +0 -129
  218. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +0 -127
  219. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +0 -111
  220. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +0 -48
  221. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +0 -77
  222. scitex/fts/_kinds/_plot/_utils/_get_template.py +0 -178
  223. scitex/fts/_kinds/_plot/_utils/_normalize.py +0 -73
  224. scitex/fts/_kinds/_plot/_utils/_validate.py +0 -197
  225. scitex/fts/_kinds/_table/_latex/_export.py +0 -279
  226. scitex/fts/_stats/__init__.py +0 -48
  227. scitex/fts/_stats/_dataclasses/_Stats.py +0 -423
  228. scitex/fts/_stats/_dataclasses/__init__.py +0 -48
  229. scitex/fts/_tables/__init__.py +0 -65
  230. scitex/fts/_tables/_latex/__init__.py +0 -93
  231. scitex/fts/_tables/_latex/_editor/__init__.py +0 -11
  232. scitex/fts/_tables/_latex/_editor/_app.py +0 -725
  233. scitex/fts/_tables/_latex/_figure_exporter.py +0 -153
  234. scitex/fts/_tables/_latex/_stats_formatter.py +0 -274
  235. scitex/fts/_tables/_latex/_table_exporter.py +0 -362
  236. scitex/fts/_tables/_latex/_utils.py +0 -369
  237. scitex/fts/_tables/_latex/_validator.py +0 -445
  238. scitex/io/_save_modules/_pltz_bundle.py +0 -356
  239. /scitex/{fig → canvas}/README.md +0 -0
  240. /scitex/{fig → canvas}/backend/__init__.py +0 -0
  241. /scitex/{fig → canvas}/backend/_export.py +0 -0
  242. /scitex/{fig → canvas}/backend/_render.py +0 -0
  243. /scitex/{fig → canvas}/docs/CANVAS_ARCHITECTURE.md +0 -0
  244. /scitex/{fig → canvas}/editor/__init__.py +0 -0
  245. /scitex/{fig → canvas}/editor/_dearpygui_editor.py +0 -0
  246. /scitex/{fig → canvas}/editor/_flask_editor.py +0 -0
  247. /scitex/{fig → canvas}/editor/_mpl_editor.py +0 -0
  248. /scitex/{fig → canvas}/editor/_qt_editor.py +0 -0
  249. /scitex/{fig → canvas}/editor/_tkinter_editor.py +0 -0
  250. /scitex/{fig → canvas}/editor/edit/backend_detector.py +0 -0
  251. /scitex/{fig → canvas}/editor/edit/manual_handler.py +0 -0
  252. /scitex/{fig → canvas}/editor/edit/path_resolver.py +0 -0
  253. /scitex/{fig → canvas}/editor/flask_editor/__init__.py +0 -0
  254. /scitex/{fig → canvas}/editor/flask_editor/_plotter.py +0 -0
  255. /scitex/{fig → canvas}/editor/flask_editor/_utils.py +0 -0
  256. /scitex/{fig → canvas}/editor/flask_editor/static/css/base/reset.css +0 -0
  257. /scitex/{fig → canvas}/editor/flask_editor/static/css/base/typography.css +0 -0
  258. /scitex/{fig → canvas}/editor/flask_editor/static/css/base/variables.css +0 -0
  259. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/buttons.css +0 -0
  260. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/context-menu.css +0 -0
  261. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/dropdown.css +0 -0
  262. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/forms.css +0 -0
  263. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/modal.css +0 -0
  264. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/sections.css +0 -0
  265. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/element-inspector.css +0 -0
  266. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/loading.css +0 -0
  267. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/overlay.css +0 -0
  268. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/selection.css +0 -0
  269. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/statistics.css +0 -0
  270. /scitex/{fig → canvas}/editor/flask_editor/static/css/index.css +0 -0
  271. /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/container.css +0 -0
  272. /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/controls.css +0 -0
  273. /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/preview.css +0 -0
  274. /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/axis.js +0 -0
  275. /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/basic.js +0 -0
  276. /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/distribute.js +0 -0
  277. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/canvas.js +0 -0
  278. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/dragging.js +0 -0
  279. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/resize.js +0 -0
  280. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/selection.js +0 -0
  281. /scitex/{fig → canvas}/editor/flask_editor/static/js/core/state.js +0 -0
  282. /scitex/{fig → canvas}/editor/flask_editor/static/js/core/utils.js +0 -0
  283. /scitex/{fig → canvas}/editor/flask_editor/static/js/dev/element-inspector.js +0 -0
  284. /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/bbox.js +0 -0
  285. /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/element-drag.js +0 -0
  286. /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/overlay.js +0 -0
  287. /scitex/{fig → canvas}/editor/flask_editor/static/js/main.js +0 -0
  288. /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/context-menu.js +0 -0
  289. /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/keyboard.js +0 -0
  290. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/controls.js +0 -0
  291. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/download.js +0 -0
  292. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/help.js +0 -0
  293. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/theme.js +0 -0
  294. /scitex/{fig → canvas}/editor/flask_editor/templates/__init__.py +0 -0
  295. /scitex/{fig → canvas}/io/_directory.py +0 -0
  296. /scitex/{fig → canvas}/model/_plot_types.py +0 -0
  297. /scitex/{fig → canvas}/utils/_defaults.py +0 -0
  298. /scitex/{fig → canvas}/utils/_validate.py +0 -0
  299. /scitex/{fts/_bundle → io/bundle}/_conversion/__init__.py +0 -0
  300. /scitex/{fts/_bundle → io/bundle}/_conversion/_bundle2dict.py +0 -0
  301. /scitex/{fts/_bundle → io/bundle}/_conversion/_dict2bundle.py +0 -0
  302. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_Axes.py +0 -0
  303. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_BBox.py +0 -0
  304. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_ColumnDef.py +0 -0
  305. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataFormat.py +0 -0
  306. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataInfo.py +0 -0
  307. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataSource.py +0 -0
  308. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_SizeMM.py +0 -0
  309. /scitex/{fts/_bundle → io/bundle}/_extractors/__init__.py +0 -0
  310. /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_bar.py +0 -0
  311. /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_line.py +0 -0
  312. /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_scatter.py +0 -0
  313. /scitex/{fts/_kinds → io/bundle/kinds}/__init__.py +0 -0
  314. /scitex/{fts/_kinds → io/bundle/kinds}/_figure/__init__.py +0 -0
  315. /scitex/{fts/_fig → io/bundle/kinds/_figure}/_composite.py +0 -0
  316. /scitex/{fts/_kinds → io/bundle/kinds}/_plot/__init__.py +0 -0
  317. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/__init__.py +0 -0
  318. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_export.py +0 -0
  319. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_render.py +0 -0
  320. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_ChannelEncoding.py +0 -0
  321. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Encoding.py +0 -0
  322. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Theme.py +0 -0
  323. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_TraceEncoding.py +0 -0
  324. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/__init__.py +0 -0
  325. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/__init__.py +0 -0
  326. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/__init__.py +0 -0
  327. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_bar.py +0 -0
  328. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_box.py +0 -0
  329. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_distribution.py +0 -0
  330. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_errorbar.py +0 -0
  331. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_histogram.py +0 -0
  332. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_image.py +0 -0
  333. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_line.py +0 -0
  334. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_scatter.py +0 -0
  335. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_seaborn.py +0 -0
  336. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_violin.py +0 -0
  337. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/__init__.py +0 -0
  338. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_auto_layout.py +0 -0
  339. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_calc_bounds.py +0 -0
  340. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_const_sizes.py +0 -0
  341. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_convert_coords.py +0 -0
  342. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_get_template.py +0 -0
  343. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_normalize.py +0 -0
  344. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_validate.py +0 -0
  345. /scitex/{fts/_kinds → io/bundle/kinds}/_shape/__init__.py +0 -0
  346. /scitex/{fts/_kinds → io/bundle/kinds}/_stats/__init__.py +0 -0
  347. /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/_Stats.py +0 -0
  348. /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/__init__.py +0 -0
  349. /scitex/{fts/_kinds → io/bundle/kinds}/_table/__init__.py +0 -0
  350. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/__init__.py +0 -0
  351. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_stats_formatter.py +0 -0
  352. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_utils.py +0 -0
  353. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_validator.py +0 -0
  354. /scitex/{fts/_kinds → io/bundle/kinds}/_text/__init__.py +0 -0
  355. /scitex/{fts/_schemas → io/bundle/schemas}/data_info.schema.json +0 -0
  356. /scitex/{fts/_schemas → io/bundle/schemas}/encoding.schema.json +0 -0
  357. /scitex/{fts/_schemas → io/bundle/schemas}/node.schema.json +0 -0
  358. /scitex/{fts/_schemas → io/bundle/schemas}/render_manifest.schema.json +0 -0
  359. /scitex/{fts/_schemas → io/bundle/schemas}/stats.schema.json +0 -0
  360. /scitex/{fts/_schemas → io/bundle/schemas}/theme.schema.json +0 -0
  361. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/WHEEL +0 -0
  362. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/entry_points.txt +0 -0
  363. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,339 +0,0 @@
1
- /**
2
- * Bounding Box Detection
3
- * Element hit detection and proximity calculations
4
- */
5
-
6
- // ============================================================================
7
- // Hit Detection - Main Entry Point
8
- // ============================================================================
9
- function findElementAt(x, y) {
10
- // Multi-panel aware hit detection with specificity hierarchy:
11
- // 1. Data elements with legacy points - proximity detection (correct saved-image coords)
12
- // 2. Small elements (labels, ticks, legends, bars, fills)
13
- // 3. Panel bboxes - lowest priority (fallback)
14
-
15
- const PROXIMITY_THRESHOLD = 15;
16
- const SCATTER_THRESHOLD = 20; // Larger threshold for scatter points
17
-
18
- // First: Check for data elements using legacy points (in saved-image coordinates)
19
- let closestDataElement = null;
20
- let minDistance = Infinity;
21
-
22
- for (const [name, bbox] of Object.entries(elementBboxes)) {
23
- if (name === '_meta') continue; // Skip metadata entry
24
-
25
- // Prioritize legacy points array (already in correct saved-image coordinates)
26
- if (bbox.points && bbox.points.length > 0) {
27
- // Check if cursor is within general bbox area first
28
- if (x >= bbox.x0 - SCATTER_THRESHOLD && x <= bbox.x1 + SCATTER_THRESHOLD &&
29
- y >= bbox.y0 - SCATTER_THRESHOLD && y <= bbox.y1 + SCATTER_THRESHOLD) {
30
-
31
- const elementType = bbox.element_type || 'line';
32
- let dist;
33
-
34
- if (elementType === 'scatter') {
35
- // For scatter, find distance to nearest point
36
- dist = distanceToNearestPoint(x, y, bbox.points);
37
- } else {
38
- // For lines, find distance to line segments
39
- dist = distanceToLine(x, y, bbox.points);
40
- }
41
-
42
- if (dist < minDistance) {
43
- minDistance = dist;
44
- closestDataElement = name;
45
- }
46
- }
47
- }
48
- }
49
-
50
- // Use appropriate threshold based on element type
51
- if (closestDataElement) {
52
- const bbox = elementBboxes[closestDataElement];
53
- const threshold = (bbox.element_type === 'scatter') ? SCATTER_THRESHOLD : PROXIMITY_THRESHOLD;
54
- if (minDistance <= threshold) {
55
- return closestDataElement;
56
- }
57
- }
58
-
59
- // Second: Collect all bbox matches, excluding panels and data elements with points
60
- const elementMatches = [];
61
- const panelMatches = [];
62
-
63
- for (const [name, bbox] of Object.entries(elementBboxes)) {
64
- if (x >= bbox.x0 && x <= bbox.x1 && y >= bbox.y0 && y <= bbox.y1) {
65
- const area = (bbox.x1 - bbox.x0) * (bbox.y1 - bbox.y0);
66
- const isPanel = bbox.is_panel || name.endsWith('_panel');
67
- const hasPoints = bbox.points && bbox.points.length > 0;
68
-
69
- if (hasPoints) {
70
- // Already handled above with proximity
71
- continue;
72
- } else if (isPanel) {
73
- panelMatches.push({name, area, bbox});
74
- } else {
75
- elementMatches.push({name, area, bbox});
76
- }
77
- }
78
- }
79
-
80
- // Return smallest non-panel element if any
81
- if (elementMatches.length > 0) {
82
- elementMatches.sort((a, b) => a.area - b.area);
83
- return elementMatches[0].name;
84
- }
85
-
86
- // Fallback to panel selection (useful for multi-panel figures)
87
- if (panelMatches.length > 0) {
88
- panelMatches.sort((a, b) => a.area - b.area);
89
- return panelMatches[0].name;
90
- }
91
-
92
- return null;
93
- }
94
-
95
- // ============================================================================
96
- // Find All Overlapping Elements (for cycle selection)
97
- // ============================================================================
98
- function findAllElementsAt(x, y) {
99
- // Find all elements at cursor position (for cycle selection)
100
- // Returns array sorted by specificity (most specific first)
101
- const PROXIMITY_THRESHOLD = 15;
102
- const SCATTER_THRESHOLD = 20;
103
-
104
- const results = [];
105
-
106
- for (const [name, bbox] of Object.entries(elementBboxes)) {
107
- let match = false;
108
- let distance = Infinity;
109
- let priority = 0; // Lower = more specific
110
-
111
- const hasPoints = bbox.points && bbox.points.length > 0;
112
- const elementType = bbox.element_type || '';
113
- const isPanel = bbox.is_panel || name.endsWith('_panel');
114
-
115
- // Check data elements with points (lines, scatter)
116
- if (hasPoints) {
117
- if (x >= bbox.x0 - SCATTER_THRESHOLD && x <= bbox.x1 + SCATTER_THRESHOLD &&
118
- y >= bbox.y0 - SCATTER_THRESHOLD && y <= bbox.y1 + SCATTER_THRESHOLD) {
119
-
120
- if (elementType === 'scatter') {
121
- distance = distanceToNearestPoint(x, y, bbox.points);
122
- if (distance <= SCATTER_THRESHOLD) {
123
- match = true;
124
- priority = 1; // Scatter points = high priority
125
- }
126
- } else {
127
- distance = distanceToLine(x, y, bbox.points);
128
- if (distance <= PROXIMITY_THRESHOLD) {
129
- match = true;
130
- priority = 2; // Lines = high priority
131
- }
132
- }
133
- }
134
- }
135
-
136
- // Check bbox containment
137
- if (x >= bbox.x0 && x <= bbox.x1 && y >= bbox.y0 && y <= bbox.y1) {
138
- const area = (bbox.x1 - bbox.x0) * (bbox.y1 - bbox.y0);
139
-
140
- if (!match) {
141
- match = true;
142
- distance = 0;
143
- }
144
-
145
- if (isPanel) {
146
- priority = 100; // Panels = lowest priority
147
- } else if (!hasPoints) {
148
- // Small elements like labels, ticks - use area for priority
149
- priority = 10 + Math.min(area / 10000, 50);
150
- }
151
- }
152
-
153
- if (match) {
154
- results.push({ name, distance, priority, bbox });
155
- }
156
- }
157
-
158
- // Sort by priority (lower first), then by distance
159
- results.sort((a, b) => {
160
- if (a.priority !== b.priority) return a.priority - b.priority;
161
- return a.distance - b.distance;
162
- });
163
-
164
- return results.map(r => r.name);
165
- }
166
-
167
- // ============================================================================
168
- // Panel Element Detection (for multi-panel canvas)
169
- // ============================================================================
170
- function findElementInPanelAt(x, y, bboxes) {
171
- const PROXIMITY_THRESHOLD = 15;
172
- const SCATTER_THRESHOLD = 20;
173
-
174
- let closestDataElement = null;
175
- let minDistance = Infinity;
176
-
177
- // Check data elements with points
178
- for (const [name, bbox] of Object.entries(bboxes)) {
179
- if (name === '_meta') continue;
180
-
181
- if (bbox.points && bbox.points.length > 0) {
182
- if (x >= bbox.x0 - SCATTER_THRESHOLD && x <= bbox.x1 + SCATTER_THRESHOLD &&
183
- y >= bbox.y0 - SCATTER_THRESHOLD && y <= bbox.y1 + SCATTER_THRESHOLD) {
184
-
185
- const elementType = bbox.element_type || 'line';
186
- let dist;
187
-
188
- if (elementType === 'scatter') {
189
- dist = distanceToNearestPoint(x, y, bbox.points);
190
- } else {
191
- dist = distanceToLine(x, y, bbox.points);
192
- }
193
-
194
- if (dist < minDistance) {
195
- minDistance = dist;
196
- closestDataElement = name;
197
- }
198
- }
199
- }
200
- }
201
-
202
- if (closestDataElement) {
203
- const bbox = bboxes[closestDataElement];
204
- const threshold = (bbox.element_type === 'scatter') ? SCATTER_THRESHOLD : PROXIMITY_THRESHOLD;
205
- if (minDistance <= threshold) {
206
- return closestDataElement;
207
- }
208
- }
209
-
210
- // Check bbox containment for other elements
211
- const elementMatches = [];
212
- for (const [name, bbox] of Object.entries(bboxes)) {
213
- if (name === '_meta') continue;
214
-
215
- if (x >= bbox.x0 && x <= bbox.x1 && y >= bbox.y0 && y <= bbox.y1) {
216
- const area = (bbox.x1 - bbox.x0) * (bbox.y1 - bbox.y0);
217
- const isPanel = bbox.is_panel || name.endsWith('_panel');
218
-
219
- if (!isPanel) {
220
- elementMatches.push({name, area, bbox});
221
- }
222
- }
223
- }
224
-
225
- if (elementMatches.length > 0) {
226
- elementMatches.sort((a, b) => a.area - b.area);
227
- return elementMatches[0].name;
228
- }
229
-
230
- return null;
231
- }
232
-
233
- // ============================================================================
234
- // Distance Calculations
235
- // ============================================================================
236
- function distanceToNearestPoint(px, py, points) {
237
- // Find distance to nearest point in scatter
238
- if (!Array.isArray(points) || points.length === 0) return Infinity;
239
- let minDist = Infinity;
240
- for (const pt of points) {
241
- if (!Array.isArray(pt) || pt.length < 2) continue;
242
- const [x, y] = pt;
243
- const dist = Math.sqrt((px - x) ** 2 + (py - y) ** 2);
244
- if (dist < minDist) minDist = dist;
245
- }
246
- return minDist;
247
- }
248
-
249
- function distanceToLine(px, py, points) {
250
- if (!Array.isArray(points) || points.length < 2) return Infinity;
251
- let minDist = Infinity;
252
- for (let i = 0; i < points.length - 1; i++) {
253
- const pt1 = points[i];
254
- const pt2 = points[i + 1];
255
- if (!Array.isArray(pt1) || pt1.length < 2) continue;
256
- if (!Array.isArray(pt2) || pt2.length < 2) continue;
257
- const [x1, y1] = pt1;
258
- const [x2, y2] = pt2;
259
- const dist = distanceToSegment(px, py, x1, y1, x2, y2);
260
- if (dist < minDist) minDist = dist;
261
- }
262
- return minDist;
263
- }
264
-
265
- function distanceToSegment(px, py, x1, y1, x2, y2) {
266
- const dx = x2 - x1;
267
- const dy = y2 - y1;
268
- const lenSq = dx * dx + dy * dy;
269
-
270
- if (lenSq === 0) {
271
- return Math.sqrt((px - x1) ** 2 + (py - y1) ** 2);
272
- }
273
-
274
- let t = ((px - x1) * dx + (py - y1) * dy) / lenSq;
275
- t = Math.max(0, Math.min(1, t));
276
-
277
- const projX = x1 + t * dx;
278
- const projY = y1 + t * dy;
279
-
280
- return Math.sqrt((px - projX) ** 2 + (py - projY) ** 2);
281
- }
282
-
283
- // ============================================================================
284
- // Polygon Test
285
- // ============================================================================
286
- function pointInPolygon(px, py, polygon) {
287
- if (!Array.isArray(polygon) || polygon.length < 3) return false;
288
-
289
- let inside = false;
290
- for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
291
- const ptI = polygon[i];
292
- const ptJ = polygon[j];
293
- if (!Array.isArray(ptI) || ptI.length < 2) continue;
294
- if (!Array.isArray(ptJ) || ptJ.length < 2) continue;
295
- const [xi, yi] = ptI;
296
- const [xj, yj] = ptJ;
297
-
298
- if (((yi > py) !== (yj > py)) &&
299
- (px < (xj - xi) * (py - yi) / (yj - yi) + xi)) {
300
- inside = !inside;
301
- }
302
- }
303
- return inside;
304
- }
305
-
306
- // ============================================================================
307
- // Geometry Extraction
308
- // ============================================================================
309
- function getGeometryPoints(bbox) {
310
- // Extract points for overlay drawing
311
- // Returns array of [x, y] points or null
312
-
313
- // For scatter: use points array directly
314
- if (bbox.element_type === 'scatter' && bbox.points) {
315
- return bbox.points;
316
- }
317
-
318
- // For lines: use path_simplified
319
- if (bbox.element_type === 'line' && bbox.path_simplified) {
320
- return bbox.path_simplified;
321
- }
322
-
323
- // For fills/polygons: use polygon
324
- if (bbox.polygon) {
325
- return bbox.polygon;
326
- }
327
-
328
- return null;
329
- }
330
-
331
- // ============================================================================
332
- // Axes Coordinate Transformation (for future use with geometry_px)
333
- // ============================================================================
334
- function axesLocalToImage(axLocalX, axLocalY, axesBbox) {
335
- // axesBbox has: x, y, width, height in figure pixel coordinates
336
- // The local editor uses tight layout which shifts coordinates
337
- // For now we use the existing image coordinates from bboxes
338
- return {x: axLocalX, y: axLocalY};
339
- }
@@ -1,286 +0,0 @@
1
- /**
2
- * Element Dragging
3
- * Handles dragging of legends and panel letters (maintains scientific rigor)
4
- */
5
-
6
- // ============================================================================
7
- // Draggable Element Check
8
- // ============================================================================
9
- function isDraggableElement(elementName, bboxes) {
10
- if (!elementName || !bboxes) return false;
11
-
12
- // Whitelist: ONLY these element types are draggable
13
- const DRAGGABLE_TYPES = ['legend', 'panel_letter'];
14
-
15
- // Check by element_type in bbox info
16
- const info = bboxes[elementName];
17
- if (info && DRAGGABLE_TYPES.includes(info.element_type)) {
18
- return true;
19
- }
20
-
21
- // Check by naming convention (strict match)
22
- if (elementName.match(/_legend$/)) return true;
23
- if (elementName.match(/_panel_letter_[A-Z]$/)) return true;
24
-
25
- // Everything else is NOT draggable (data integrity)
26
- return false;
27
- }
28
-
29
- // ============================================================================
30
- // Element Drag Start
31
- // ============================================================================
32
- function startElementDrag(e, elementName, panelName, img, bboxes) {
33
- const info = bboxes[elementName] || {};
34
- const elementType = info.element_type || (elementName.includes('legend') ? 'legend' : 'panel_letter');
35
-
36
- // Extract ax_id from element name (e.g., "ax_00_legend" -> "ax_00")
37
- const axId = elementName.split('_').slice(0, 2).join('_');
38
-
39
- // Get axes bbox for constraining drag
40
- const axesBbox = bboxes[`${axId}_panel`] || null;
41
-
42
- elementDragState = {
43
- element: elementName,
44
- panelName: panelName,
45
- elementType: elementType,
46
- axId: axId,
47
- axesBbox: axesBbox,
48
- bboxes: bboxes,
49
- img: img,
50
- startMouseX: e.clientX,
51
- startMouseY: e.clientY,
52
- startBbox: {...info},
53
- };
54
-
55
- // Show snap guide overlay
56
- showSnapGuides(img, axesBbox, bboxes);
57
-
58
- document.addEventListener('mousemove', onElementDrag);
59
- document.addEventListener('mouseup', stopElementDrag);
60
-
61
- e.preventDefault();
62
- e.stopPropagation();
63
- }
64
-
65
- // ============================================================================
66
- // Element Drag Move
67
- // ============================================================================
68
- function onElementDrag(e) {
69
- if (!elementDragState) return;
70
-
71
- const {img, bboxes, element, axId, axesBbox, startBbox, startMouseX, startMouseY} = elementDragState;
72
- if (!img) return;
73
-
74
- const rect = img.getBoundingClientRect();
75
- const dims = getObjectFitContainDimensions(img);
76
-
77
- // Calculate delta in image coordinates
78
- const deltaX = e.clientX - startMouseX;
79
- const deltaY = e.clientY - startMouseY;
80
-
81
- // Convert to image pixel coordinates
82
- const scaleX = dims.displayWidth / rect.width;
83
- const scaleY = dims.displayHeight / rect.height;
84
- const imgSize = bboxes._meta?.imgSize || {width: dims.displayWidth, height: dims.displayHeight};
85
- const imgDeltaX = deltaX * scaleX * imgSize.width / dims.displayWidth;
86
- const imgDeltaY = deltaY * scaleY * imgSize.height / dims.displayHeight;
87
-
88
- // Update bbox position (for visual feedback)
89
- if (bboxes[element]) {
90
- const newX0 = startBbox.x0 + imgDeltaX;
91
- const newY0 = startBbox.y0 + imgDeltaY;
92
- bboxes[element].x0 = newX0;
93
- bboxes[element].y0 = newY0;
94
- bboxes[element].x1 = newX0 + (startBbox.x1 - startBbox.x0);
95
- bboxes[element].y1 = newY0 + (startBbox.y1 - startBbox.y0);
96
- }
97
-
98
- // Calculate normalized axes position (0-1)
99
- if (axesBbox) {
100
- const axesWidth = axesBbox.x1 - axesBbox.x0;
101
- const axesHeight = axesBbox.y1 - axesBbox.y0;
102
- const elemCenterX = (bboxes[element].x0 + bboxes[element].x1) / 2;
103
- const elemCenterY = (bboxes[element].y0 + bboxes[element].y1) / 2;
104
- const normX = (elemCenterX - axesBbox.x0) / axesWidth;
105
- const normY = 1 - (elemCenterY - axesBbox.y0) / axesHeight; // Flip Y
106
-
107
- // Update snap guide highlighting
108
- updateSnapHighlight(normX, normY);
109
-
110
- // Show position indicator
111
- showElementPositionIndicator(element, normX, normY);
112
- }
113
-
114
- // Redraw overlay
115
- const overlay = img.parentElement?.querySelector('svg.panel-card-overlay');
116
- if (overlay) {
117
- const panelCache = panelBboxesCache[elementDragState.panelName];
118
- if (panelCache) {
119
- updatePanelOverlay(overlay, bboxes, panelCache.imgSize, rect.width, rect.height, element, element, img);
120
- }
121
- }
122
- }
123
-
124
- // ============================================================================
125
- // Element Drag Stop
126
- // ============================================================================
127
- function stopElementDrag() {
128
- if (!elementDragState) return;
129
-
130
- const {element, panelName, elementType, axId, bboxes, axesBbox} = elementDragState;
131
-
132
- // Calculate final normalized position
133
- let finalPosition = null;
134
- let snapName = null;
135
-
136
- if (axesBbox && bboxes[element]) {
137
- const axesWidth = axesBbox.x1 - axesBbox.x0;
138
- const axesHeight = axesBbox.y1 - axesBbox.y0;
139
- const elemCenterX = (bboxes[element].x0 + bboxes[element].x1) / 2;
140
- const elemCenterY = (bboxes[element].y0 + bboxes[element].y1) / 2;
141
- const normX = (elemCenterX - axesBbox.x0) / axesWidth;
142
- const normY = 1 - (elemCenterY - axesBbox.y0) / axesHeight;
143
-
144
- // Check for snap to named position
145
- snapName = findNearestSnapPosition(normX, normY);
146
- finalPosition = snapName ? SNAP_POSITIONS[snapName] : {x: normX, y: normY};
147
- }
148
-
149
- // Hide snap guides
150
- hideSnapGuides();
151
- hideElementPositionIndicator();
152
-
153
- // Save position to server
154
- if (finalPosition) {
155
- updateElementPosition(panelName, element, finalPosition);
156
- }
157
-
158
- document.removeEventListener('mousemove', onElementDrag);
159
- document.removeEventListener('mouseup', stopElementDrag);
160
- elementDragState = null;
161
- }
162
-
163
- // ============================================================================
164
- // Snap Position Finding
165
- // ============================================================================
166
- function findNearestSnapPosition(normX, normY, threshold = 0.08) {
167
- let nearest = null;
168
- let minDist = Infinity;
169
-
170
- for (const [name, pos] of Object.entries(SNAP_POSITIONS)) {
171
- const dist = Math.sqrt(Math.pow(normX - pos.x, 2) + Math.pow(normY - pos.y, 2));
172
- if (dist < threshold && dist < minDist) {
173
- minDist = dist;
174
- nearest = name;
175
- }
176
- }
177
- return nearest;
178
- }
179
-
180
- // ============================================================================
181
- // Snap Guides UI
182
- // ============================================================================
183
- function showSnapGuides(img, axesBbox, bboxes) {
184
- if (!img || !axesBbox) return;
185
-
186
- const container = img.parentElement;
187
- if (!container) return;
188
-
189
- // Remove existing guides
190
- container.querySelectorAll('.snap-guide').forEach(el => el.remove());
191
-
192
- const rect = img.getBoundingClientRect();
193
- const dims = getObjectFitContainDimensions(img);
194
- const imgSize = bboxes._meta?.imgSize || {width: dims.displayWidth, height: dims.displayHeight};
195
-
196
- // Scale factors
197
- const scaleX = dims.displayWidth / imgSize.width;
198
- const scaleY = dims.displayHeight / imgSize.height;
199
-
200
- // Create snap points
201
- for (const [name, pos] of Object.entries(SNAP_POSITIONS)) {
202
- const axesWidth = axesBbox.x1 - axesBbox.x0;
203
- const axesHeight = axesBbox.y1 - axesBbox.y0;
204
-
205
- // Calculate pixel position
206
- const imgX = axesBbox.x0 + pos.x * axesWidth;
207
- const imgY = axesBbox.y0 + (1 - pos.y) * axesHeight;
208
-
209
- const displayX = dims.offsetX + imgX * scaleX;
210
- const displayY = dims.offsetY + imgY * scaleY;
211
-
212
- const guide = document.createElement('div');
213
- guide.className = 'snap-guide';
214
- guide.dataset.snapName = name;
215
- guide.style.cssText = `
216
- position: absolute;
217
- left: ${displayX - 6}px;
218
- top: ${displayY - 6}px;
219
- width: 12px;
220
- height: 12px;
221
- border: 2px dashed rgba(100, 149, 237, 0.6);
222
- border-radius: 50%;
223
- pointer-events: none;
224
- z-index: 50;
225
- transition: all 0.15s ease;
226
- `;
227
- container.appendChild(guide);
228
- }
229
- }
230
-
231
- function updateSnapHighlight(normX, normY) {
232
- const threshold = 0.08;
233
- document.querySelectorAll('.snap-guide').forEach(guide => {
234
- const name = guide.dataset.snapName;
235
- const pos = SNAP_POSITIONS[name];
236
- const dist = Math.sqrt(Math.pow(normX - pos.x, 2) + Math.pow(normY - pos.y, 2));
237
- if (dist < threshold) {
238
- guide.style.borderColor = 'rgba(76, 175, 80, 0.9)';
239
- guide.style.backgroundColor = 'rgba(76, 175, 80, 0.3)';
240
- guide.style.transform = 'scale(1.5)';
241
- } else {
242
- guide.style.borderColor = 'rgba(100, 149, 237, 0.6)';
243
- guide.style.backgroundColor = 'transparent';
244
- guide.style.transform = 'scale(1)';
245
- }
246
- });
247
- }
248
-
249
- function hideSnapGuides() {
250
- document.querySelectorAll('.snap-guide').forEach(el => el.remove());
251
- }
252
-
253
- // ============================================================================
254
- // Position Indicator
255
- // ============================================================================
256
- function showElementPositionIndicator(element, normX, normY) {
257
- let indicator = document.getElementById('element-position-indicator');
258
- if (!indicator) {
259
- indicator = document.createElement('div');
260
- indicator.id = 'element-position-indicator';
261
- indicator.style.cssText = `
262
- position: fixed;
263
- bottom: 60px;
264
- right: 20px;
265
- background: rgba(0, 0, 0, 0.85);
266
- color: white;
267
- padding: 6px 10px;
268
- border-radius: 4px;
269
- font-family: monospace;
270
- font-size: 11px;
271
- z-index: 10001;
272
- pointer-events: none;
273
- `;
274
- document.body.appendChild(indicator);
275
- }
276
-
277
- indicator.textContent = `${element}: (${normX.toFixed(2)}, ${normY.toFixed(2)})`;
278
- indicator.style.display = 'block';
279
- }
280
-
281
- function hideElementPositionIndicator() {
282
- const indicator = document.getElementById('element-position-indicator');
283
- if (indicator) {
284
- indicator.style.display = 'none';
285
- }
286
- }