scitex 2.10.2__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.
- scitex/__init__.py +1 -4
- scitex/__version__.py +1 -1
- scitex/_install_guide.py +14 -2
- scitex/bridge/_figrecipe.py +1 -1
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_plt.py +1 -1
- scitex/bridge/_stats_vis.py +2 -2
- scitex/{fig → canvas}/__init__.py +84 -96
- scitex/{fig → canvas}/backend/_parser.py +1 -1
- scitex/{fig → canvas}/canvas.py +13 -14
- scitex/{fts/_fig/_editor → canvas/editor}/_defaults.py +2 -2
- scitex/{fig → canvas}/editor/edit/__init__.py +11 -14
- scitex/{fig → canvas}/editor/edit/bundle_resolver.py +56 -48
- scitex/{fig → canvas}/editor/edit/editor_launcher.py +79 -26
- scitex/{fts/_fig/_editor/_cui/_panel_loader.py → canvas/editor/edit/panel_loader.py} +8 -8
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_bbox.py +2 -1
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_core.py +84 -84
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_renderer.py +7 -6
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/static/css/features/canvas.css +2 -2
- scitex/{fig → canvas}/editor/flask_editor/static/css/features/panel-grid.css +1 -1
- scitex/{fig → canvas}/editor/flask_editor/static/js/core/api.js +3 -4
- scitex/{fig → canvas}/editor/flask_editor/static/js/editor/preview.js +5 -5
- scitex/{fig → canvas}/editor/flask_editor/templates/_html.py +3 -3
- scitex/{fig → canvas}/editor/flask_editor/templates/_scripts.py +10 -10
- scitex/{fig → canvas}/editor/flask_editor/templates/_styles.py +3 -3
- scitex/{fig → canvas}/io/__init__.py +32 -38
- scitex/{fig → canvas}/io/_bundle.py +217 -154
- scitex/{fig → canvas}/io/_canvas.py +1 -1
- scitex/{fig → canvas}/io/_data.py +1 -1
- scitex/{fig → canvas}/io/_export.py +1 -1
- scitex/{fig → canvas}/io/_load.py +1 -1
- scitex/{fig → canvas}/io/_panel.py +1 -1
- scitex/{fig → canvas}/io/_save.py +1 -1
- scitex/{fig → canvas}/model/__init__.py +1 -1
- scitex/{fig → canvas}/model/_annotations.py +1 -1
- scitex/{fig → canvas}/model/_axes.py +1 -1
- scitex/{fig → canvas}/model/_figure.py +1 -1
- scitex/{fig → canvas}/model/_guides.py +1 -1
- scitex/{fig → canvas}/model/_plot.py +1 -1
- scitex/{fig → canvas}/model/_styles.py +1 -1
- scitex/{fig → canvas}/utils/__init__.py +1 -1
- scitex/cli/convert.py +10 -6
- scitex/diagram/README.md +7 -7
- scitex/io/__init__.py +7 -19
- scitex/io/_load.py +15 -19
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_con.py +5 -5
- scitex/io/_load_modules/_eeg.py +16 -12
- scitex/io/_save.py +11 -16
- scitex/io/_save_modules/__init__.py +6 -10
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_plot_bundle.py +112 -0
- scitex/io/_save_modules/{_pltz_stx.py → _plot_scitex.py} +7 -7
- scitex/io/_save_modules/_stx_bundle.py +16 -16
- scitex/io/bundle/README.md +89 -80
- scitex/{fts/_bundle/_FTS.py → io/bundle/_Bundle.py} +197 -95
- scitex/io/bundle/__init__.py +67 -35
- scitex/{fts/_bundle → io/bundle}/_children.py +32 -40
- scitex/io/bundle/_core.py +184 -97
- scitex/{fts/_bundle/_dataclasses/_Node.py → io/bundle/_dataclasses/_Spec.py} +29 -23
- scitex/{fts/_bundle/_dataclasses/_NodeRefs.py → io/bundle/_dataclasses/_SpecRefs.py} +6 -6
- scitex/{fts/_bundle → io/bundle}/_dataclasses/__init__.py +4 -4
- scitex/{fts/_bundle → io/bundle}/_loader.py +19 -19
- scitex/io/bundle/_manifest.py +99 -0
- scitex/{fts/_bundle → io/bundle}/_mpl_helpers.py +119 -28
- scitex/io/bundle/_nested.py +113 -100
- scitex/{fts/_bundle → io/bundle}/_saver.py +13 -14
- scitex/{fts/_bundle → io/bundle}/_storage.py +3 -3
- scitex/io/bundle/_types.py +41 -16
- scitex/{fts/_bundle → io/bundle}/_validation.py +20 -18
- scitex/io/bundle/_zip.py +21 -31
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_backend/_parser.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Annotations.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Axes.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Figure.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Guides.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Plot.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Styles.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_utils/_plot_layout.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/__init__.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/_app.py +1 -1
- scitex/{fts/_tables → io/bundle/kinds/_table}/_latex/_export.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_figure_exporter.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_table_exporter.py +1 -1
- scitex/io/bundle/schemas/__init__.py +30 -0
- scitex/parallel/_run.py +5 -4
- scitex/path/_find.py +60 -83
- scitex/path/_get_module_path.py +23 -21
- scitex/path/_get_spath.py +6 -27
- scitex/path/_getsize.py +23 -9
- scitex/path/_increment_version.py +31 -38
- scitex/path/_mk_spath.py +26 -29
- scitex/path/_path.py +5 -12
- scitex/path/_split.py +27 -15
- scitex/path/_this_path.py +23 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +2 -1
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +2 -2
- scitex/plt/gallery/_generate.py +76 -50
- scitex/plt/io/__init__.py +17 -19
- scitex/plt/io/_bundle.py +99 -52
- scitex/plt/io/_layered_bundle.py +303 -168
- scitex/plt/utils/_csv_column_naming.py +250 -118
- scitex/schema/__init__.py +69 -73
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +30 -33
- scitex/stats/_schema.py +1 -1
- scitex/stats/io/__init__.py +10 -11
- scitex/stats/io/_bundle.py +16 -16
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/METADATA +191 -72
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/RECORD +237 -360
- scitex/fig/editor/_defaults.py +0 -300
- scitex/fig/editor/edit/panel_loader.py +0 -232
- scitex/fig/editor/flask_editor/_bbox.py +0 -1299
- scitex/fig/editor/flask_editor/_core.py +0 -1429
- scitex/fig/editor/flask_editor/_renderer.py +0 -813
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +0 -176
- scitex/fts/README.md +0 -262
- scitex/fts/TODO.md +0 -66
- scitex/fts/__init__.py +0 -90
- scitex/fts/_bundle/README_IN_BUNDLE.md +0 -102
- scitex/fts/_bundle/__init__.py +0 -38
- scitex/fts/_bundle/_utils/__init__.py +0 -55
- scitex/fts/_bundle/_utils/_const.py +0 -26
- scitex/fts/_bundle/_utils/_errors.py +0 -73
- scitex/fts/_bundle/_utils/_generate.py +0 -21
- scitex/fts/_bundle/_utils/_types.py +0 -76
- scitex/fts/_bundle/_zipbundle.py +0 -165
- scitex/fts/_fig/__init__.py +0 -22
- scitex/fts/_fig/_backend/_parser.py +0 -188
- scitex/fts/_fig/_editor/__init__.py +0 -14
- scitex/fts/_fig/_editor/_cui/__init__.py +0 -33
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +0 -39
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +0 -366
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +0 -175
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +0 -52
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +0 -66
- scitex/fts/_fig/_editor/_gui/__init__.py +0 -11
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +0 -20
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +0 -664
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +0 -79
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +0 -41
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +0 -16
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +0 -85
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +0 -217
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +0 -93
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +0 -112
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +0 -212
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +0 -190
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +0 -45
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +0 -95
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +0 -101
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +0 -138
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +0 -31
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +0 -7
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +0 -56
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +0 -78
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +0 -314
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +0 -107
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +0 -54
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +0 -172
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +0 -258
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +0 -48
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +0 -71
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +0 -288
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +0 -143
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +0 -245
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +0 -992
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +0 -339
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +0 -286
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +0 -371
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +0 -293
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +0 -426
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +0 -152
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +0 -265
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +0 -184
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +0 -100
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +0 -34
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +0 -124
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +0 -851
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +0 -4932
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +0 -1657
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +0 -36
- scitex/fts/_fig/_models/_Annotations.py +0 -115
- scitex/fts/_fig/_models/_Axes.py +0 -152
- scitex/fts/_fig/_models/_Figure.py +0 -138
- scitex/fts/_fig/_models/_Plot.py +0 -123
- scitex/fts/_fig/_utils/_plot_layout.py +0 -397
- scitex/fts/_kinds/_figure/_composite.py +0 -345
- scitex/fts/_kinds/_plot/_backend/__init__.py +0 -53
- scitex/fts/_kinds/_plot/_backend/_export.py +0 -165
- scitex/fts/_kinds/_plot/_backend/_render.py +0 -538
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +0 -46
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +0 -82
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +0 -441
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +0 -52
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +0 -47
- scitex/fts/_kinds/_plot/_models/_Guides.py +0 -104
- scitex/fts/_kinds/_plot/_models/_Styles.py +0 -245
- scitex/fts/_kinds/_plot/_models/__init__.py +0 -80
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +0 -156
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +0 -43
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +0 -38
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +0 -36
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +0 -60
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +0 -61
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +0 -57
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +0 -121
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +0 -36
- scitex/fts/_kinds/_plot/_utils/__init__.py +0 -129
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +0 -127
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +0 -111
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +0 -48
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +0 -77
- scitex/fts/_kinds/_plot/_utils/_get_template.py +0 -178
- scitex/fts/_kinds/_plot/_utils/_normalize.py +0 -73
- scitex/fts/_kinds/_plot/_utils/_validate.py +0 -197
- scitex/fts/_kinds/_table/_latex/_export.py +0 -279
- scitex/fts/_stats/__init__.py +0 -48
- scitex/fts/_stats/_dataclasses/_Stats.py +0 -423
- scitex/fts/_stats/_dataclasses/__init__.py +0 -48
- scitex/fts/_tables/__init__.py +0 -65
- scitex/fts/_tables/_latex/__init__.py +0 -93
- scitex/fts/_tables/_latex/_editor/__init__.py +0 -11
- scitex/fts/_tables/_latex/_editor/_app.py +0 -725
- scitex/fts/_tables/_latex/_figure_exporter.py +0 -153
- scitex/fts/_tables/_latex/_stats_formatter.py +0 -274
- scitex/fts/_tables/_latex/_table_exporter.py +0 -362
- scitex/fts/_tables/_latex/_utils.py +0 -369
- scitex/fts/_tables/_latex/_validator.py +0 -445
- scitex/io/_save_modules/_pltz_bundle.py +0 -356
- /scitex/{fig → canvas}/README.md +0 -0
- /scitex/{fig → canvas}/backend/__init__.py +0 -0
- /scitex/{fig → canvas}/backend/_export.py +0 -0
- /scitex/{fig → canvas}/backend/_render.py +0 -0
- /scitex/{fig → canvas}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{fig → canvas}/editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/_dearpygui_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_flask_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_mpl_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_qt_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_tkinter_editor.py +0 -0
- /scitex/{fig → canvas}/editor/edit/backend_detector.py +0 -0
- /scitex/{fig → canvas}/editor/edit/manual_handler.py +0 -0
- /scitex/{fig → canvas}/editor/edit/path_resolver.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_plotter.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_utils.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/reset.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/typography.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/variables.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/buttons.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/context-menu.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/dropdown.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/forms.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/modal.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/sections.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/element-inspector.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/loading.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/overlay.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/selection.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/statistics.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/index.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/container.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/controls.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/preview.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/axis.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/basic.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/distribute.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/canvas.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/dragging.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/resize.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/selection.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/state.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/utils.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/dev/element-inspector.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/bbox.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/element-drag.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/overlay.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/main.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/context-menu.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/keyboard.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/controls.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/download.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/help.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/theme.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/templates/__init__.py +0 -0
- /scitex/{fig → canvas}/io/_directory.py +0 -0
- /scitex/{fig → canvas}/model/_plot_types.py +0 -0
- /scitex/{fig → canvas}/utils/_defaults.py +0 -0
- /scitex/{fig → canvas}/utils/_validate.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_bundle2dict.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_dict2bundle.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_Axes.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_BBox.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_ColumnDef.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataFormat.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataInfo.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataSource.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_SizeMM.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_bar.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_line.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_scatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_figure/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_figure}/_composite.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_plot/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_export.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_render.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_ChannelEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Encoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Theme.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_TraceEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_bar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_box.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_distribution.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_errorbar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_histogram.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_image.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_line.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_scatter.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_seaborn.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_violin.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_auto_layout.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_calc_bounds.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_const_sizes.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_convert_coords.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_get_template.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_normalize.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_validate.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_shape/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/_Stats.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_stats_formatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_utils.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_validator.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_text/__init__.py +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/data_info.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/encoding.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/node.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/render_manifest.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/stats.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/theme.schema.json +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/WHEEL +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.10.2.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
|
-
}
|