scitex 2.7.0__py3-none-any.whl → 2.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scitex/__init__.py +6 -2
- scitex/__version__.py +1 -1
- scitex/audio/README.md +52 -0
- scitex/audio/__init__.py +384 -0
- scitex/audio/__main__.py +129 -0
- scitex/audio/_tts.py +334 -0
- scitex/audio/engines/__init__.py +44 -0
- scitex/audio/engines/base.py +275 -0
- scitex/audio/engines/elevenlabs_engine.py +143 -0
- scitex/audio/engines/gtts_engine.py +162 -0
- scitex/audio/engines/pyttsx3_engine.py +131 -0
- scitex/audio/mcp_server.py +757 -0
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_vis.py +1 -1
- scitex/dev/plt/__init__.py +272 -0
- scitex/dev/plt/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/plot_mpl_bar.py +29 -0
- scitex/dev/plt/plot_mpl_barh.py +29 -0
- scitex/dev/plt/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/plot_mpl_contour.py +31 -0
- scitex/dev/plt/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/plot_mpl_fill.py +30 -0
- scitex/dev/plt/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/plot_mpl_hist.py +28 -0
- scitex/dev/plt/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/plot_mpl_pie.py +29 -0
- scitex/dev/plt/plot_mpl_plot.py +29 -0
- scitex/dev/plt/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/plot_mpl_stem.py +29 -0
- scitex/dev/plt/plot_mpl_step.py +29 -0
- scitex/dev/plt/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/plot_sns_barplot.py +29 -0
- scitex/dev/plt/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/plot_sns_histplot.py +29 -0
- scitex/dev/plt/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/plot_stx_bar.py +29 -0
- scitex/dev/plt/plot_stx_barh.py +29 -0
- scitex/dev/plt/plot_stx_box.py +28 -0
- scitex/dev/plt/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/plot_stx_contour.py +31 -0
- scitex/dev/plt/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/plot_stx_fillv.py +28 -0
- scitex/dev/plt/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/plot_stx_image.py +28 -0
- scitex/dev/plt/plot_stx_imshow.py +28 -0
- scitex/dev/plt/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/plot_stx_kde.py +28 -0
- scitex/dev/plt/plot_stx_line.py +28 -0
- scitex/dev/plt/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/plot_stx_raster.py +28 -0
- scitex/dev/plt/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/plot_stx_scatter.py +29 -0
- scitex/dev/plt/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/plot_stx_violin.py +28 -0
- scitex/dev/plt/plot_stx_violinplot.py +28 -0
- scitex/diagram/README.md +197 -0
- scitex/diagram/__init__.py +48 -0
- scitex/diagram/_compile.py +312 -0
- scitex/diagram/_diagram.py +355 -0
- scitex/diagram/_presets.py +173 -0
- scitex/diagram/_schema.py +182 -0
- scitex/diagram/_split.py +278 -0
- scitex/fig/__init__.py +352 -0
- scitex/{vis → fig}/backend/_parser.py +1 -1
- scitex/{vis → fig}/canvas.py +1 -1
- scitex/{vis → fig}/editor/__init__.py +5 -2
- scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
- scitex/{vis → fig}/editor/_defaults.py +70 -5
- scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
- scitex/{vis → fig}/editor/_qt_editor.py +182 -2
- scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
- scitex/fig/editor/edit/__init__.py +50 -0
- scitex/fig/editor/edit/backend_detector.py +109 -0
- scitex/fig/editor/edit/bundle_resolver.py +240 -0
- scitex/fig/editor/edit/editor_launcher.py +239 -0
- scitex/fig/editor/edit/manual_handler.py +53 -0
- scitex/fig/editor/edit/panel_loader.py +232 -0
- scitex/fig/editor/edit/path_resolver.py +67 -0
- scitex/fig/editor/flask_editor/_bbox.py +1299 -0
- scitex/fig/editor/flask_editor/_core.py +1429 -0
- scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
- scitex/fig/editor/flask_editor/_renderer.py +813 -0
- scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
- scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
- scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
- scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
- scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
- scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
- scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
- scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
- scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
- scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
- scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
- scitex/fig/editor/flask_editor/static/css/index.css +31 -0
- scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
- scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
- scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
- scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
- scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
- scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
- scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
- scitex/fig/editor/flask_editor/static/js/main.js +426 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
- scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
- scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
- scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
- scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
- scitex/fig/editor/flask_editor/templates/_html.py +852 -0
- scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
- scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
- scitex/{vis → fig}/io/__init__.py +13 -1
- scitex/fig/io/_bundle.py +1058 -0
- scitex/{vis → fig}/io/_canvas.py +1 -1
- scitex/{vis → fig}/io/_data.py +1 -1
- scitex/{vis → fig}/io/_export.py +1 -1
- scitex/{vis → fig}/io/_load.py +1 -1
- scitex/{vis → fig}/io/_panel.py +1 -1
- scitex/{vis → fig}/io/_save.py +1 -1
- scitex/{vis → fig}/model/__init__.py +1 -1
- scitex/{vis → fig}/model/_annotations.py +1 -1
- scitex/{vis → fig}/model/_axes.py +1 -1
- scitex/{vis → fig}/model/_figure.py +1 -1
- scitex/{vis → fig}/model/_guides.py +1 -1
- scitex/{vis → fig}/model/_plot.py +1 -1
- scitex/{vis → fig}/model/_styles.py +1 -1
- scitex/{vis → fig}/utils/__init__.py +1 -1
- scitex/io/__init__.py +22 -26
- scitex/io/_bundle.py +493 -0
- scitex/io/_flush.py +5 -2
- scitex/io/_load.py +98 -0
- scitex/io/_load_modules/_H5Explorer.py +5 -2
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_image.py +3 -4
- scitex/io/_load_modules/_txt.py +4 -2
- scitex/io/_metadata.py +34 -324
- scitex/io/_metadata_modules/__init__.py +46 -0
- scitex/io/_metadata_modules/_embed.py +70 -0
- scitex/io/_metadata_modules/_read.py +64 -0
- scitex/io/_metadata_modules/_utils.py +79 -0
- scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
- scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
- scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
- scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
- scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
- scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
- scitex/io/_metadata_modules/read_metadata_png.py +39 -0
- scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
- scitex/io/_qr_utils.py +5 -3
- scitex/io/_save.py +548 -30
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_image.py +5 -9
- scitex/io/_save_modules/_tex.py +7 -4
- scitex/io/_zip_bundle.py +439 -0
- scitex/io/utils/h5_to_zarr.py +11 -9
- scitex/msword/__init__.py +255 -0
- scitex/msword/profiles.py +357 -0
- scitex/msword/reader.py +753 -0
- scitex/msword/utils.py +289 -0
- scitex/msword/writer.py +362 -0
- scitex/plt/__init__.py +5 -2
- scitex/plt/_subplots/_AxesWrapper.py +6 -6
- scitex/plt/_subplots/_AxisWrapper.py +15 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
- scitex/plt/_subplots/_FigWrapper.py +6 -6
- scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
- scitex/plt/_subplots/_export_as_csv.py +35 -5
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
- scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
- scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
- scitex/plt/gallery/_generate.py +421 -14
- scitex/plt/io/__init__.py +53 -0
- scitex/plt/io/_bundle.py +490 -0
- scitex/plt/io/_layered_bundle.py +1343 -0
- scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
- scitex/plt/styles/__init__.py +14 -0
- scitex/plt/styles/presets.py +78 -0
- scitex/plt/utils/__init__.py +13 -1
- scitex/plt/utils/_collect_figure_metadata.py +10 -14
- scitex/plt/utils/_configure_mpl.py +6 -18
- scitex/plt/utils/_crop.py +32 -14
- scitex/plt/utils/_csv_column_naming.py +54 -0
- scitex/plt/utils/_figure_mm.py +116 -1
- scitex/plt/utils/_hitmap.py +1643 -0
- scitex/plt/utils/metadata/__init__.py +25 -0
- scitex/plt/utils/metadata/_core.py +9 -10
- scitex/plt/utils/metadata/_dimensions.py +6 -3
- scitex/plt/utils/metadata/_editable_export.py +405 -0
- scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
- scitex/schema/__init__.py +109 -16
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_plot.py +1015 -0
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +117 -0
- scitex/stats/io/__init__.py +29 -0
- scitex/stats/io/_bundle.py +156 -0
- scitex/tex/__init__.py +4 -0
- scitex/tex/_export.py +890 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
- scitex/io/memo.md +0 -2827
- scitex/plt/REQUESTS.md +0 -191
- scitex/plt/_subplots/TODO.md +0 -53
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
- scitex/vis/__init__.py +0 -177
- scitex/vis/editor/_edit.py +0 -390
- scitex/vis/editor/flask_editor/_bbox.py +0 -529
- scitex/vis/editor/flask_editor/_core.py +0 -168
- scitex/vis/editor/flask_editor/_renderer.py +0 -393
- scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
- scitex/vis/editor/flask_editor/templates/_html.py +0 -513
- scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
- scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
- /scitex/{vis → fig}/README.md +0 -0
- /scitex/{vis → fig}/backend/__init__.py +0 -0
- /scitex/{vis → fig}/backend/_export.py +0 -0
- /scitex/{vis → fig}/backend/_render.py +0 -0
- /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
- /scitex/{vis → fig}/io/_directory.py +0 -0
- /scitex/{vis → fig}/model/_plot_types.py +0 -0
- /scitex/{vis → fig}/utils/_defaults.py +0 -0
- /scitex/{vis → fig}/utils/_validate.py +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axis-Based Alignment
|
|
3
|
+
* Scientific plot alignment using axes bounding boxes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Get Axes Bbox for Panel
|
|
8
|
+
// ============================================================================
|
|
9
|
+
function getAxesBboxForPanel(panelName) {
|
|
10
|
+
const cache = panelBboxesCache[panelName];
|
|
11
|
+
if (!cache || !cache.bboxes) return null;
|
|
12
|
+
|
|
13
|
+
const bboxes = cache.bboxes;
|
|
14
|
+
|
|
15
|
+
// Method 1: Look for ax_00_panel, ax_01_panel, etc.
|
|
16
|
+
for (const key of Object.keys(bboxes)) {
|
|
17
|
+
if (key.endsWith('_panel') && key.startsWith('ax_')) {
|
|
18
|
+
const bbox = bboxes[key];
|
|
19
|
+
if (bbox && bbox.x0 !== undefined) {
|
|
20
|
+
return {
|
|
21
|
+
x0: bbox.x0,
|
|
22
|
+
y0: bbox.y0,
|
|
23
|
+
x1: bbox.x1,
|
|
24
|
+
y1: bbox.y1,
|
|
25
|
+
key: key
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Method 2: Calculate axes bbox from spine bboxes (xaxis_spine + yaxis_spine)
|
|
32
|
+
// This is the common case for matplotlib figures
|
|
33
|
+
let xSpine = null, ySpine = null;
|
|
34
|
+
for (const key of Object.keys(bboxes)) {
|
|
35
|
+
if (key.endsWith('_xaxis_spine') && key.startsWith('ax_')) {
|
|
36
|
+
xSpine = bboxes[key];
|
|
37
|
+
}
|
|
38
|
+
if (key.endsWith('_yaxis_spine') && key.startsWith('ax_')) {
|
|
39
|
+
ySpine = bboxes[key];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (xSpine && ySpine) {
|
|
44
|
+
// Combine spine bboxes to get axes area
|
|
45
|
+
// Y-spine defines left edge, X-spine defines bottom edge
|
|
46
|
+
const x0 = ySpine.x0 !== undefined ? ySpine.x0 : ySpine.x;
|
|
47
|
+
const y0 = ySpine.y0 !== undefined ? ySpine.y0 : ySpine.y;
|
|
48
|
+
const x1 = xSpine.x1 !== undefined ? xSpine.x1 : (xSpine.x + xSpine.width);
|
|
49
|
+
const y1 = xSpine.y1 !== undefined ? xSpine.y1 : (xSpine.y + xSpine.height);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
x0: Math.min(x0, xSpine.x0 || xSpine.x || x0),
|
|
53
|
+
y0: Math.min(y0, xSpine.y0 || xSpine.y || y0),
|
|
54
|
+
x1: Math.max(x1, ySpine.x1 || (ySpine.x + ySpine.width) || x1),
|
|
55
|
+
y1: Math.max(y1, ySpine.y1 || (ySpine.y + ySpine.height) || y1),
|
|
56
|
+
key: 'derived_from_spines'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Method 3: Fallback to _meta.axes_bbox_px for single-axes plots
|
|
61
|
+
if (bboxes._meta && bboxes._meta.axes_bbox_px) {
|
|
62
|
+
const axBbox = bboxes._meta.axes_bbox_px;
|
|
63
|
+
return {
|
|
64
|
+
x0: axBbox.x0 || axBbox.x,
|
|
65
|
+
y0: axBbox.y0 || axBbox.y,
|
|
66
|
+
x1: axBbox.x1 || (axBbox.x + axBbox.width),
|
|
67
|
+
y1: axBbox.y1 || (axBbox.y + axBbox.height),
|
|
68
|
+
key: '_meta.axes_bbox_px'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Calculate Axis Edge Offset
|
|
77
|
+
// ============================================================================
|
|
78
|
+
function getAxisEdgeOffset(panel, axesBbox, edge, imgSize) {
|
|
79
|
+
if (!axesBbox || !imgSize) return 0;
|
|
80
|
+
|
|
81
|
+
// Scale factor from image pixels to displayed panel pixels
|
|
82
|
+
const panelEl = panel;
|
|
83
|
+
const displayWidth = panelEl.offsetWidth;
|
|
84
|
+
const displayHeight = panelEl.offsetHeight;
|
|
85
|
+
const scaleX = displayWidth / imgSize.width;
|
|
86
|
+
const scaleY = displayHeight / imgSize.height;
|
|
87
|
+
|
|
88
|
+
switch(edge) {
|
|
89
|
+
case 'left':
|
|
90
|
+
// Y-axis left edge
|
|
91
|
+
return axesBbox.x0 * scaleX;
|
|
92
|
+
case 'right':
|
|
93
|
+
// Right edge of axes
|
|
94
|
+
return axesBbox.x1 * scaleX;
|
|
95
|
+
case 'top':
|
|
96
|
+
// Top edge of axes
|
|
97
|
+
return axesBbox.y0 * scaleY;
|
|
98
|
+
case 'bottom':
|
|
99
|
+
// X-axis bottom edge
|
|
100
|
+
return axesBbox.y1 * scaleY;
|
|
101
|
+
case 'center-h':
|
|
102
|
+
// Horizontal center of axes
|
|
103
|
+
return ((axesBbox.x0 + axesBbox.x1) / 2) * scaleX;
|
|
104
|
+
case 'center-v':
|
|
105
|
+
// Vertical center of axes
|
|
106
|
+
return ((axesBbox.y0 + axesBbox.y1) / 2) * scaleY;
|
|
107
|
+
default:
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Align Panels by Axis
|
|
114
|
+
// ============================================================================
|
|
115
|
+
function alignPanelsByAxis(edge) {
|
|
116
|
+
// Use selected panels only
|
|
117
|
+
const panels = Array.from(document.querySelectorAll('.panel-canvas-item.selected'));
|
|
118
|
+
if (panels.length < 2) {
|
|
119
|
+
setStatus('Select at least 2 panels for axis alignment', true);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Collect panel info with axes bboxes
|
|
124
|
+
const panelInfos = [];
|
|
125
|
+
for (const panel of panels) {
|
|
126
|
+
const panelName = panel.dataset.panelName;
|
|
127
|
+
const cache = panelBboxesCache[panelName];
|
|
128
|
+
const axesBbox = getAxesBboxForPanel(panelName);
|
|
129
|
+
|
|
130
|
+
if (!axesBbox || !cache) {
|
|
131
|
+
console.warn(`Panel ${panelName}: no axes bbox found`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const currentPos = panelPositions[panelName];
|
|
136
|
+
const axisOffset = getAxisEdgeOffset(panel, axesBbox, edge, cache.imgSize);
|
|
137
|
+
|
|
138
|
+
panelInfos.push({
|
|
139
|
+
panel,
|
|
140
|
+
panelName,
|
|
141
|
+
axesBbox,
|
|
142
|
+
imgSize: cache.imgSize,
|
|
143
|
+
currentPos,
|
|
144
|
+
axisOffset
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (panelInfos.length < 2) {
|
|
149
|
+
setStatus('Not enough panels with axes info', true);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Calculate target position - use the first panel's axis position as reference
|
|
154
|
+
const referenceInfo = panelInfos[0];
|
|
155
|
+
const referenceAxisPos = referenceInfo.currentPos.x + (edge.includes('h') || edge === 'left' || edge === 'right' ? referenceInfo.axisOffset : 0);
|
|
156
|
+
const referenceAxisPosY = referenceInfo.currentPos.y + (edge.includes('v') || edge === 'top' || edge === 'bottom' ? referenceInfo.axisOffset : 0);
|
|
157
|
+
|
|
158
|
+
if (edge === 'left' || edge === 'right' || edge === 'center-h') {
|
|
159
|
+
// Align horizontally (match X positions of axis edges)
|
|
160
|
+
// Target = first panel's axis X position in canvas coords
|
|
161
|
+
for (const info of panelInfos) {
|
|
162
|
+
const newX = referenceAxisPos - info.axisOffset;
|
|
163
|
+
info.currentPos.x = newX;
|
|
164
|
+
info.panel.style.left = newX + 'px';
|
|
165
|
+
panelLayoutMm[info.panelName].x_mm = newX / canvasScale;
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
// Align vertically (match Y positions of axis edges)
|
|
169
|
+
// Target = first panel's axis Y position in canvas coords
|
|
170
|
+
for (const info of panelInfos) {
|
|
171
|
+
const newY = referenceAxisPosY - info.axisOffset;
|
|
172
|
+
info.currentPos.y = newY;
|
|
173
|
+
info.panel.style.top = newY + 'px';
|
|
174
|
+
panelLayoutMm[info.panelName].y_mm = newY / canvasScale;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Update layout data
|
|
179
|
+
updatePanelLayoutFromDOM();
|
|
180
|
+
layoutModified = true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Stack Panels Vertically
|
|
185
|
+
// ============================================================================
|
|
186
|
+
function stackPanelsVertically() {
|
|
187
|
+
// Use selected panels only
|
|
188
|
+
const panels = Array.from(document.querySelectorAll('.panel-canvas-item.selected'));
|
|
189
|
+
if (panels.length < 2) {
|
|
190
|
+
setStatus('Select at least 2 panels for stacking', true);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Collect panel info with axes bboxes
|
|
195
|
+
const panelInfos = [];
|
|
196
|
+
for (const panel of panels) {
|
|
197
|
+
const panelName = panel.dataset.panelName;
|
|
198
|
+
const cache = panelBboxesCache[panelName];
|
|
199
|
+
const axesBbox = getAxesBboxForPanel(panelName);
|
|
200
|
+
|
|
201
|
+
if (!axesBbox || !cache) continue;
|
|
202
|
+
|
|
203
|
+
const currentPos = panelPositions[panelName];
|
|
204
|
+
const axisOffsetLeft = getAxisEdgeOffset(panel, axesBbox, 'left', cache.imgSize);
|
|
205
|
+
|
|
206
|
+
panelInfos.push({
|
|
207
|
+
panel,
|
|
208
|
+
panelName,
|
|
209
|
+
axesBbox,
|
|
210
|
+
imgSize: cache.imgSize,
|
|
211
|
+
currentPos,
|
|
212
|
+
axisOffsetLeft,
|
|
213
|
+
height: panel.offsetHeight
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (panelInfos.length < 2) return;
|
|
218
|
+
|
|
219
|
+
// Sort by current vertical position
|
|
220
|
+
panelInfos.sort((a, b) => a.currentPos.y - b.currentPos.y);
|
|
221
|
+
|
|
222
|
+
// Use first panel as reference for Y-axis alignment
|
|
223
|
+
const referenceAxisX = panelInfos[0].currentPos.x + panelInfos[0].axisOffsetLeft;
|
|
224
|
+
|
|
225
|
+
// Stack panels vertically with small gap, aligned by Y-axis
|
|
226
|
+
const gap = 10; // pixels
|
|
227
|
+
let currentY = panelInfos[0].currentPos.y;
|
|
228
|
+
|
|
229
|
+
for (const info of panelInfos) {
|
|
230
|
+
// Align Y-axis (left edge of axes)
|
|
231
|
+
const newX = referenceAxisX - info.axisOffsetLeft;
|
|
232
|
+
info.currentPos.x = newX;
|
|
233
|
+
info.panel.style.left = newX + 'px';
|
|
234
|
+
|
|
235
|
+
// Stack vertically
|
|
236
|
+
info.currentPos.y = currentY;
|
|
237
|
+
info.panel.style.top = currentY + 'px';
|
|
238
|
+
currentY += info.height + gap;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Update layout data
|
|
242
|
+
updatePanelLayoutFromDOM();
|
|
243
|
+
layoutModified = true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Axis Alignment Shortcut Handler
|
|
248
|
+
// ============================================================================
|
|
249
|
+
function handleAlignByAxisShortcut(key) {
|
|
250
|
+
const panels = document.querySelectorAll('.panel-canvas-item');
|
|
251
|
+
if (panels.length < 2) {
|
|
252
|
+
setStatus('Need multiple panels for axis alignment', true);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const dirNames = {
|
|
257
|
+
'l': 'Y-axis (left edge)',
|
|
258
|
+
'r': 'Right edge',
|
|
259
|
+
't': 'Top edge',
|
|
260
|
+
'b': 'X-axis (bottom edge)',
|
|
261
|
+
'c': 'Center horizontal',
|
|
262
|
+
'm': 'Center vertical',
|
|
263
|
+
's': 'Stacked vertically'
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
switch(key) {
|
|
267
|
+
case 'l': alignPanelsByAxis('left'); break; // Y-axis left
|
|
268
|
+
case 'r': alignPanelsByAxis('right'); break; // Right edge
|
|
269
|
+
case 't': alignPanelsByAxis('top'); break; // Top edge
|
|
270
|
+
case 'b': alignPanelsByAxis('bottom'); break; // X-axis bottom
|
|
271
|
+
case 'c': alignPanelsByAxis('center-h'); break; // Horizontal center
|
|
272
|
+
case 'm': alignPanelsByAxis('center-v'); break; // Vertical center
|
|
273
|
+
case 's': stackPanelsVertically(); break; // Stack with Y-axis alignment
|
|
274
|
+
default:
|
|
275
|
+
setStatus('Unknown axis key: ' + key + '. Use L/R/T/B/C/M/S', true);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (dirNames[key]) {
|
|
279
|
+
setStatus(`Aligned by axis: ${dirNames[key]}`, false);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// Panel Movement (Arrow Keys)
|
|
285
|
+
// ============================================================================
|
|
286
|
+
function moveSelectedPanel(direction, amountMm) {
|
|
287
|
+
const selected = document.querySelector('.panel-canvas-item.selected');
|
|
288
|
+
if (!selected) return;
|
|
289
|
+
|
|
290
|
+
const panelName = selected.dataset.panelName;
|
|
291
|
+
const pos = panelPositions[panelName];
|
|
292
|
+
if (!pos) return;
|
|
293
|
+
|
|
294
|
+
switch(direction) {
|
|
295
|
+
case 'left': pos.x -= amountMm * canvasScale; break;
|
|
296
|
+
case 'right': pos.x += amountMm * canvasScale; break;
|
|
297
|
+
case 'up': pos.y -= amountMm * canvasScale; break;
|
|
298
|
+
case 'down': pos.y += amountMm * canvasScale; break;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Update position in pixels (canvasScale = px/mm)
|
|
302
|
+
selected.style.left = pos.x + 'px';
|
|
303
|
+
selected.style.top = pos.y + 'px';
|
|
304
|
+
|
|
305
|
+
// Update layout data
|
|
306
|
+
panelLayoutMm[panelName] = {
|
|
307
|
+
...panelLayoutMm[panelName],
|
|
308
|
+
x_mm: pos.x / canvasScale,
|
|
309
|
+
y_mm: pos.y / canvasScale
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
layoutModified = true;
|
|
313
|
+
setStatus(`Moved ${panelName} ${direction} by ${amountMm}mm`);
|
|
314
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic Panel Alignment
|
|
3
|
+
* Bounding box-based alignment (non-scientific)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Basic Alignment (by bounding box)
|
|
8
|
+
// ============================================================================
|
|
9
|
+
function alignPanels(mode) {
|
|
10
|
+
const selectedPanels = getSelectedPanels();
|
|
11
|
+
if (selectedPanels.length < 2) return;
|
|
12
|
+
|
|
13
|
+
// Get bounds
|
|
14
|
+
let targetValue;
|
|
15
|
+
switch(mode) {
|
|
16
|
+
case 'left':
|
|
17
|
+
targetValue = Math.min(...selectedPanels.map(p => p.pos.x));
|
|
18
|
+
selectedPanels.forEach(p => {
|
|
19
|
+
p.pos.x = targetValue;
|
|
20
|
+
p.item.style.left = targetValue + 'px';
|
|
21
|
+
});
|
|
22
|
+
break;
|
|
23
|
+
case 'right':
|
|
24
|
+
targetValue = Math.max(...selectedPanels.map(p => p.pos.x + p.pos.width));
|
|
25
|
+
selectedPanels.forEach(p => {
|
|
26
|
+
p.pos.x = targetValue - p.pos.width;
|
|
27
|
+
p.item.style.left = p.pos.x + 'px';
|
|
28
|
+
});
|
|
29
|
+
break;
|
|
30
|
+
case 'top':
|
|
31
|
+
targetValue = Math.min(...selectedPanels.map(p => p.pos.y));
|
|
32
|
+
selectedPanels.forEach(p => {
|
|
33
|
+
p.pos.y = targetValue;
|
|
34
|
+
p.item.style.top = targetValue + 'px';
|
|
35
|
+
});
|
|
36
|
+
break;
|
|
37
|
+
case 'bottom':
|
|
38
|
+
targetValue = Math.max(...selectedPanels.map(p => p.pos.y + p.pos.height));
|
|
39
|
+
selectedPanels.forEach(p => {
|
|
40
|
+
p.pos.y = targetValue - p.pos.height;
|
|
41
|
+
p.item.style.top = p.pos.y + 'px';
|
|
42
|
+
});
|
|
43
|
+
break;
|
|
44
|
+
case 'center-h':
|
|
45
|
+
const avgX = selectedPanels.reduce((sum, p) => sum + p.pos.x + p.pos.width/2, 0) / selectedPanels.length;
|
|
46
|
+
selectedPanels.forEach(p => {
|
|
47
|
+
p.pos.x = avgX - p.pos.width/2;
|
|
48
|
+
p.item.style.left = p.pos.x + 'px';
|
|
49
|
+
});
|
|
50
|
+
break;
|
|
51
|
+
case 'center-v':
|
|
52
|
+
const avgY = selectedPanels.reduce((sum, p) => sum + p.pos.y + p.pos.height/2, 0) / selectedPanels.length;
|
|
53
|
+
selectedPanels.forEach(p => {
|
|
54
|
+
p.pos.y = avgY - p.pos.height/2;
|
|
55
|
+
p.item.style.top = p.pos.y + 'px';
|
|
56
|
+
});
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Update layout data
|
|
61
|
+
updatePanelLayoutFromDOM();
|
|
62
|
+
layoutModified = true;
|
|
63
|
+
setStatus(`Aligned panels: ${mode}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Alignment Shortcut Handler
|
|
68
|
+
// ============================================================================
|
|
69
|
+
function handleAlignShortcut(key, isShift) {
|
|
70
|
+
const panels = document.querySelectorAll('.panel-canvas-item');
|
|
71
|
+
if (panels.length < 2) {
|
|
72
|
+
setStatus('Need multiple panels for alignment', true);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
switch(key) {
|
|
77
|
+
case 'l': alignPanels('left'); break;
|
|
78
|
+
case 'r': alignPanels('right'); break;
|
|
79
|
+
case 't': alignPanels('top'); break;
|
|
80
|
+
case 'b': alignPanels('bottom'); break;
|
|
81
|
+
case 'c': alignPanels('center-h'); break;
|
|
82
|
+
case 'm': alignPanels('center-v'); break;
|
|
83
|
+
case 'h': distributePanels('horizontal'); break;
|
|
84
|
+
case 'v': distributePanels('vertical'); break;
|
|
85
|
+
default:
|
|
86
|
+
setStatus('Unknown alignment key: ' + key, true);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Z-Order Management
|
|
92
|
+
// ============================================================================
|
|
93
|
+
function bringPanelToFront() {
|
|
94
|
+
const selected = document.querySelector('.panel-canvas-item.selected');
|
|
95
|
+
if (selected) {
|
|
96
|
+
selected.style.zIndex = (parseInt(selected.style.zIndex || 0) + 1).toString();
|
|
97
|
+
setStatus('Brought panel to front');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function sendPanelToBack() {
|
|
102
|
+
const selected = document.querySelector('.panel-canvas-item.selected');
|
|
103
|
+
if (selected) {
|
|
104
|
+
selected.style.zIndex = (parseInt(selected.style.zIndex || 0) - 1).toString();
|
|
105
|
+
setStatus('Sent panel to back');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel Distribution
|
|
3
|
+
* Evenly distribute panels horizontally or vertically
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Distribute Panels
|
|
8
|
+
// ============================================================================
|
|
9
|
+
function distributePanels(direction) {
|
|
10
|
+
const selectedPanels = getSelectedPanels();
|
|
11
|
+
if (selectedPanels.length < 3) {
|
|
12
|
+
setStatus('Need at least 3 panels for distribution', true);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (direction === 'horizontal') {
|
|
17
|
+
// Sort by X position
|
|
18
|
+
selectedPanels.sort((a, b) => a.pos.x - b.pos.x);
|
|
19
|
+
|
|
20
|
+
const first = selectedPanels[0];
|
|
21
|
+
const last = selectedPanels[selectedPanels.length - 1];
|
|
22
|
+
const totalSpace = (last.pos.x + last.pos.width) - first.pos.x;
|
|
23
|
+
const totalPanelWidth = selectedPanels.reduce((sum, p) => sum + p.pos.width, 0);
|
|
24
|
+
const gap = (totalSpace - totalPanelWidth) / (selectedPanels.length - 1);
|
|
25
|
+
|
|
26
|
+
let currentX = first.pos.x;
|
|
27
|
+
selectedPanels.forEach(p => {
|
|
28
|
+
p.pos.x = currentX;
|
|
29
|
+
p.item.style.left = currentX + 'px';
|
|
30
|
+
currentX += p.pos.width + gap;
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
// Sort by Y position
|
|
34
|
+
selectedPanels.sort((a, b) => a.pos.y - b.pos.y);
|
|
35
|
+
|
|
36
|
+
const first = selectedPanels[0];
|
|
37
|
+
const last = selectedPanels[selectedPanels.length - 1];
|
|
38
|
+
const totalSpace = (last.pos.y + last.pos.height) - first.pos.y;
|
|
39
|
+
const totalPanelHeight = selectedPanels.reduce((sum, p) => sum + p.pos.height, 0);
|
|
40
|
+
const gap = (totalSpace - totalPanelHeight) / (selectedPanels.length - 1);
|
|
41
|
+
|
|
42
|
+
let currentY = first.pos.y;
|
|
43
|
+
selectedPanels.forEach(p => {
|
|
44
|
+
p.pos.y = currentY;
|
|
45
|
+
p.item.style.top = currentY + 'px';
|
|
46
|
+
currentY += p.pos.height + gap;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Update layout data
|
|
51
|
+
updatePanelLayoutFromDOM();
|
|
52
|
+
layoutModified = true;
|
|
53
|
+
setStatus(`Distributed panels ${direction}ly`);
|
|
54
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas View Management
|
|
3
|
+
* Handles the unified canvas view for multi-panel figures
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Canvas Mode Control
|
|
8
|
+
// ============================================================================
|
|
9
|
+
function setCanvasMode(mode) {
|
|
10
|
+
canvasMode = mode;
|
|
11
|
+
document.getElementById('canvas-grid').classList.toggle('canvas-mode', mode === 'canvas');
|
|
12
|
+
document.getElementById('canvas-grid').classList.toggle('grid-mode', mode === 'grid');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Canvas Rendering
|
|
17
|
+
// ============================================================================
|
|
18
|
+
function renderCanvasView() {
|
|
19
|
+
const container = document.getElementById('canvas-grid');
|
|
20
|
+
container.innerHTML = '';
|
|
21
|
+
|
|
22
|
+
// Fetch panels if not cached
|
|
23
|
+
if (!panelData || !panelData.panels) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (canvasMode === 'canvas') {
|
|
28
|
+
// Calculate canvas size based on number of panels
|
|
29
|
+
const panels = panelData.panels;
|
|
30
|
+
|
|
31
|
+
panels.forEach((panel, idx) => {
|
|
32
|
+
const item = document.createElement('div');
|
|
33
|
+
item.className = 'panel-canvas-item';
|
|
34
|
+
item.dataset.panelName = panel.name;
|
|
35
|
+
|
|
36
|
+
// Initialize position if not set
|
|
37
|
+
if (!panelPositions[panel.name]) {
|
|
38
|
+
panelPositions[panel.name] = {
|
|
39
|
+
x: idx * 150,
|
|
40
|
+
y: idx * 150,
|
|
41
|
+
width: panel.width_px || 400,
|
|
42
|
+
height: panel.height_px || 300
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const pos = panelPositions[panel.name];
|
|
47
|
+
item.style.left = pos.x + 'px';
|
|
48
|
+
item.style.top = pos.y + 'px';
|
|
49
|
+
item.style.width = pos.width + 'px';
|
|
50
|
+
item.style.height = pos.height + 'px';
|
|
51
|
+
|
|
52
|
+
item.innerHTML = `
|
|
53
|
+
<div class="panel-drag-handle">☰</div>
|
|
54
|
+
<div class="panel-label">${panel.name}</div>
|
|
55
|
+
<img src="data:image/png;base64,${panel.image_base64}" style="width: 100%; height: 100%; object-fit: contain;">
|
|
56
|
+
<canvas class="panel-overlay"></canvas>
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
container.appendChild(item);
|
|
60
|
+
|
|
61
|
+
// Double-click to edit
|
|
62
|
+
item.addEventListener('dblclick', () => {
|
|
63
|
+
loadPanelForEditing(panel.name);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Drag start
|
|
67
|
+
initPanelDrag(item, panel.name);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Update canvas height to fit all panels
|
|
71
|
+
updateCanvasSize();
|
|
72
|
+
} else {
|
|
73
|
+
// Grid mode - use CSS grid layout (simpler)
|
|
74
|
+
loadPanelGrid();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Interactive Element Detection Helper
|
|
80
|
+
// ============================================================================
|
|
81
|
+
function isInteractiveElement(target) {
|
|
82
|
+
// SVG paths with hover-path class are interactive elements
|
|
83
|
+
if (target.classList && target.classList.contains('hover-path')) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
// Check parent elements for hover-path (click might be on child)
|
|
87
|
+
let parent = target.parentElement;
|
|
88
|
+
while (parent) {
|
|
89
|
+
if (parent.tagName === 'path' || parent.classList.contains('hover-path')) {
|
|
90
|
+
// Path elements in SVG overlay are interactive
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
parent = parent.parentElement;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Canvas Size Management
|
|
100
|
+
// ============================================================================
|
|
101
|
+
function updateCanvasSize() {
|
|
102
|
+
// Find the maximum extent of all panels
|
|
103
|
+
let maxX = 0;
|
|
104
|
+
let maxY = 0;
|
|
105
|
+
|
|
106
|
+
Object.values(panelPositions).forEach(pos => {
|
|
107
|
+
maxX = Math.max(maxX, pos.x + pos.width);
|
|
108
|
+
maxY = Math.max(maxY, pos.y + pos.height);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Add some padding
|
|
112
|
+
const container = document.getElementById('canvas-grid');
|
|
113
|
+
if (container && canvasMode === 'canvas') {
|
|
114
|
+
container.style.minHeight = (maxY + 100) + 'px';
|
|
115
|
+
container.style.minWidth = (maxX + 100) + 'px';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// Canvas Zoom Functions
|
|
121
|
+
// ============================================================================
|
|
122
|
+
function zoomCanvas(factor) {
|
|
123
|
+
canvasZoom = Math.max(0.1, Math.min(5.0, canvasZoom * factor));
|
|
124
|
+
const container = document.getElementById('canvas-grid');
|
|
125
|
+
if (container) {
|
|
126
|
+
container.style.transform = `scale(${canvasZoom})`;
|
|
127
|
+
container.style.transformOrigin = 'top left';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function fitCanvasToWindow() {
|
|
132
|
+
const container = document.getElementById('canvas-grid');
|
|
133
|
+
if (!container) return;
|
|
134
|
+
|
|
135
|
+
const containerWidth = container.scrollWidth;
|
|
136
|
+
const windowWidth = window.innerWidth - 400; // Account for side panels
|
|
137
|
+
canvasZoom = Math.min(1.0, windowWidth / containerWidth);
|
|
138
|
+
container.style.transform = `scale(${canvasZoom})`;
|
|
139
|
+
container.style.transformOrigin = 'top left';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function resizeCanvas(factor) {
|
|
143
|
+
const container = document.getElementById('canvas-grid');
|
|
144
|
+
if (!container) return;
|
|
145
|
+
|
|
146
|
+
// Scale all panel positions and sizes
|
|
147
|
+
Object.keys(panelPositions).forEach(name => {
|
|
148
|
+
const pos = panelPositions[name];
|
|
149
|
+
pos.x *= factor;
|
|
150
|
+
pos.y *= factor;
|
|
151
|
+
pos.width *= factor;
|
|
152
|
+
pos.height *= factor;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
renderCanvasView();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================================================
|
|
159
|
+
// Panel Layout Update from DOM
|
|
160
|
+
// ============================================================================
|
|
161
|
+
function updatePanelLayoutFromDOM() {
|
|
162
|
+
document.querySelectorAll('.panel-canvas-item').forEach(item => {
|
|
163
|
+
const name = item.dataset.panelName;
|
|
164
|
+
const rect = item.getBoundingClientRect();
|
|
165
|
+
panelPositions[name] = {
|
|
166
|
+
x: parseFloat(item.style.left),
|
|
167
|
+
y: parseFloat(item.style.top),
|
|
168
|
+
width: rect.width,
|
|
169
|
+
height: rect.height
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
}
|