scitex 2.10.3__py3-none-any.whl → 2.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.3.dist-info → scitex-2.11.0.dist-info}/METADATA +190 -73
- {scitex-2.10.3.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.3.dist-info → scitex-2.11.0.dist-info}/WHEEL +0 -0
- {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
# Timestamp: "2025-12-14 (ywatanabe)"
|
|
4
3
|
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/io/_bundle.py
|
|
5
4
|
|
|
6
5
|
"""
|
|
7
|
-
SciTeX
|
|
6
|
+
SciTeX Figure Bundle I/O - Figure-specific bundle operations.
|
|
8
7
|
|
|
9
8
|
Handles:
|
|
10
9
|
- Figure specification validation
|
|
11
10
|
- Panel composition and layout
|
|
12
|
-
- Nested .
|
|
11
|
+
- Nested .plot bundle management
|
|
13
12
|
- Export file handling
|
|
14
13
|
"""
|
|
15
14
|
|
|
@@ -19,23 +18,23 @@ from pathlib import Path
|
|
|
19
18
|
from typing import Any, Dict, List
|
|
20
19
|
|
|
21
20
|
__all__ = [
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
21
|
+
"validate_figure_spec",
|
|
22
|
+
"load_figure_bundle",
|
|
23
|
+
"save_figure_bundle",
|
|
24
|
+
"FIGURE_SCHEMA_SPEC",
|
|
26
25
|
]
|
|
27
26
|
|
|
28
|
-
# Schema specification for .
|
|
29
|
-
|
|
30
|
-
"name": "scitex.
|
|
27
|
+
# Schema specification for .figure bundles
|
|
28
|
+
FIGURE_SCHEMA_SPEC = {
|
|
29
|
+
"name": "scitex.canvas.figure",
|
|
31
30
|
"version": "1.0.0",
|
|
32
31
|
"required_fields": ["schema"],
|
|
33
32
|
"optional_fields": ["figure", "panels", "notations"],
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
|
|
37
|
-
def
|
|
38
|
-
"""Validate .
|
|
36
|
+
def validate_figure_spec(spec: Dict[str, Any]) -> List[str]:
|
|
37
|
+
"""Validate .figure-specific fields.
|
|
39
38
|
|
|
40
39
|
Args:
|
|
41
40
|
spec: The specification dictionary to validate.
|
|
@@ -65,8 +64,8 @@ def validate_figz_spec(spec: Dict[str, Any]) -> List[str]:
|
|
|
65
64
|
return errors
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
def
|
|
69
|
-
"""Load .
|
|
67
|
+
def load_figure_bundle(bundle_dir: Path) -> Dict[str, Any]:
|
|
68
|
+
"""Load .figure bundle contents from directory.
|
|
70
69
|
|
|
71
70
|
Supports both:
|
|
72
71
|
- New format: spec.json + style.json (separate semantic/appearance)
|
|
@@ -79,32 +78,32 @@ def load_figz_bundle(bundle_dir: Path) -> Dict[str, Any]:
|
|
|
79
78
|
Dictionary with loaded bundle contents:
|
|
80
79
|
- spec: Figure specification (semantic)
|
|
81
80
|
- style: Figure style (appearance)
|
|
82
|
-
- plots: Dict of nested
|
|
81
|
+
- plots: Dict of nested plot bundles
|
|
83
82
|
- basename: Base filename
|
|
84
83
|
"""
|
|
85
84
|
result = {}
|
|
86
85
|
bundle_dir = Path(bundle_dir)
|
|
87
86
|
|
|
88
87
|
# Determine basename from directory name
|
|
89
|
-
basename = bundle_dir.stem.replace(".
|
|
88
|
+
basename = bundle_dir.stem.replace(".figure", "")
|
|
90
89
|
result["basename"] = basename
|
|
91
90
|
|
|
92
91
|
# Try to load spec.json (new format) first
|
|
93
92
|
spec_file = bundle_dir / "spec.json"
|
|
94
93
|
if spec_file.exists():
|
|
95
|
-
with open(spec_file
|
|
94
|
+
with open(spec_file) as f:
|
|
96
95
|
result["spec"] = json.load(f)
|
|
97
96
|
else:
|
|
98
97
|
# Fallback to {basename}.json (legacy format)
|
|
99
98
|
legacy_file = bundle_dir / f"{basename}.json"
|
|
100
99
|
if legacy_file.exists():
|
|
101
|
-
with open(legacy_file
|
|
100
|
+
with open(legacy_file) as f:
|
|
102
101
|
result["spec"] = json.load(f)
|
|
103
102
|
else:
|
|
104
103
|
# Try any .json file
|
|
105
104
|
for f in bundle_dir.glob("*.json"):
|
|
106
|
-
if not f.name.startswith(
|
|
107
|
-
with open(f
|
|
105
|
+
if not f.name.startswith(".") and f.name != "style.json":
|
|
106
|
+
with open(f) as fp:
|
|
108
107
|
result["spec"] = json.load(fp)
|
|
109
108
|
break
|
|
110
109
|
else:
|
|
@@ -113,7 +112,7 @@ def load_figz_bundle(bundle_dir: Path) -> Dict[str, Any]:
|
|
|
113
112
|
# Load style.json if exists
|
|
114
113
|
style_file = bundle_dir / "style.json"
|
|
115
114
|
if style_file.exists():
|
|
116
|
-
with open(style_file
|
|
115
|
+
with open(style_file) as f:
|
|
117
116
|
result["style"] = json.load(f)
|
|
118
117
|
else:
|
|
119
118
|
# Extract from embedded styles in spec (legacy)
|
|
@@ -126,30 +125,32 @@ def load_figz_bundle(bundle_dir: Path) -> Dict[str, Any]:
|
|
|
126
125
|
else:
|
|
127
126
|
result["style"] = {}
|
|
128
127
|
|
|
129
|
-
# Load nested .
|
|
128
|
+
# Load nested .plot bundles
|
|
130
129
|
result["plots"] = {}
|
|
131
130
|
|
|
132
|
-
# Load from .
|
|
133
|
-
for
|
|
134
|
-
plot_name =
|
|
131
|
+
# Load from .plot directories
|
|
132
|
+
for plot_dir in bundle_dir.glob("*.plot"):
|
|
133
|
+
plot_name = plot_dir.stem.replace(".plot", "")
|
|
135
134
|
from scitex.io.bundle import load
|
|
136
|
-
result["plots"][plot_name] = load(pltz_dir)
|
|
137
135
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
result["plots"][plot_name] = load(plot_dir)
|
|
137
|
+
|
|
138
|
+
# Load from .plot.zip files
|
|
139
|
+
for plot_zip in bundle_dir.glob("*.plot.zip"):
|
|
140
|
+
if plot_zip.is_file():
|
|
141
|
+
plot_name = plot_zip.stem.replace(".plot", "")
|
|
142
142
|
from scitex.io.bundle import load
|
|
143
|
-
|
|
143
|
+
|
|
144
|
+
result["plots"][plot_name] = load(plot_zip)
|
|
144
145
|
|
|
145
146
|
return result
|
|
146
147
|
|
|
147
148
|
|
|
148
|
-
def
|
|
149
|
-
"""Save .
|
|
149
|
+
def save_figure_bundle(data: Dict[str, Any], dir_path: Path) -> None:
|
|
150
|
+
"""Save .figure bundle contents to directory.
|
|
150
151
|
|
|
151
152
|
Structure:
|
|
152
|
-
figure.
|
|
153
|
+
figure.figure/
|
|
153
154
|
spec.json # Figure-level specification
|
|
154
155
|
style.json # Figure-level style (optional)
|
|
155
156
|
exports/ # Figure-level exports
|
|
@@ -160,9 +161,9 @@ def save_figz_bundle(data: Dict[str, Any], dir_path: Path) -> None:
|
|
|
160
161
|
cache/ # Figure-level cache
|
|
161
162
|
geometry_px.json # Combined geometry for all panels
|
|
162
163
|
render_manifest.json
|
|
163
|
-
panels/ # Nested panel bundles (or *.
|
|
164
|
-
A.
|
|
165
|
-
B.
|
|
164
|
+
panels/ # Nested panel bundles (or *.plot at root)
|
|
165
|
+
A.plot/
|
|
166
|
+
B.plot/
|
|
166
167
|
README.md
|
|
167
168
|
|
|
168
169
|
Args:
|
|
@@ -170,10 +171,11 @@ def save_figz_bundle(data: Dict[str, Any], dir_path: Path) -> None:
|
|
|
170
171
|
dir_path: Path to the bundle directory.
|
|
171
172
|
"""
|
|
172
173
|
import logging
|
|
174
|
+
|
|
173
175
|
logger = logging.getLogger("scitex")
|
|
174
176
|
|
|
175
|
-
# Get basename from directory name (e.g., "Figure1" from "Figure1.
|
|
176
|
-
basename = dir_path.stem.replace(".
|
|
177
|
+
# Get basename from directory name (e.g., "Figure1" from "Figure1.figure")
|
|
178
|
+
basename = dir_path.stem.replace(".figure", "")
|
|
177
179
|
|
|
178
180
|
# Create directories
|
|
179
181
|
exports_dir = dir_path / "exports"
|
|
@@ -192,7 +194,9 @@ def save_figz_bundle(data: Dict[str, Any], dir_path: Path) -> None:
|
|
|
192
194
|
|
|
193
195
|
# Build clean spec (semantic data only)
|
|
194
196
|
clean_spec = {
|
|
195
|
-
"schema": spec.get(
|
|
197
|
+
"schema": spec.get(
|
|
198
|
+
"schema", {"name": "scitex.canvas.figure", "version": "1.0.0"}
|
|
199
|
+
),
|
|
196
200
|
"figure": {
|
|
197
201
|
"id": figure_data.get("id", "figure"),
|
|
198
202
|
"title": figure_data.get("title", ""),
|
|
@@ -204,17 +208,20 @@ def save_figz_bundle(data: Dict[str, Any], dir_path: Path) -> None:
|
|
|
204
208
|
clean_spec["notations"] = spec["notations"]
|
|
205
209
|
|
|
206
210
|
# Build style (appearance data)
|
|
207
|
-
|
|
208
|
-
"schema": {"name": "scitex.
|
|
211
|
+
figure_style = {
|
|
212
|
+
"schema": {"name": "scitex.canvas.style", "version": "1.0.0"},
|
|
209
213
|
"size": style.get("size", {"width_mm": 180, "height_mm": 120}),
|
|
210
214
|
"background": style.get("background", "#ffffff"),
|
|
211
215
|
"theme": style.get("theme", {"mode": "light"}),
|
|
212
|
-
"panel_labels": style.get(
|
|
213
|
-
"
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
"panel_labels": style.get(
|
|
217
|
+
"panel_labels",
|
|
218
|
+
{
|
|
219
|
+
"visible": True,
|
|
220
|
+
"fontsize": 12,
|
|
221
|
+
"fontweight": "bold",
|
|
222
|
+
"position": "top-left",
|
|
223
|
+
},
|
|
224
|
+
),
|
|
218
225
|
}
|
|
219
226
|
|
|
220
227
|
# Save spec.json (semantic)
|
|
@@ -225,24 +232,24 @@ def save_figz_bundle(data: Dict[str, Any], dir_path: Path) -> None:
|
|
|
225
232
|
# Save style.json (appearance)
|
|
226
233
|
style_file = dir_path / "style.json"
|
|
227
234
|
with open(style_file, "w") as f:
|
|
228
|
-
json.dump(
|
|
235
|
+
json.dump(figure_style, f, indent=2)
|
|
229
236
|
|
|
230
237
|
# Also save as {basename}.json for backward compatibility (full spec with embedded style)
|
|
231
238
|
compat_spec = dict(clean_spec)
|
|
232
239
|
compat_spec["figure"]["styles"] = {
|
|
233
|
-
"size":
|
|
234
|
-
"background":
|
|
240
|
+
"size": figure_style["size"],
|
|
241
|
+
"background": figure_style["background"],
|
|
235
242
|
}
|
|
236
243
|
compat_spec_file = dir_path / f"{basename}.json"
|
|
237
244
|
with open(compat_spec_file, "w") as f:
|
|
238
245
|
json.dump(compat_spec, f, indent=2)
|
|
239
246
|
|
|
240
247
|
# Save exports to exports/ directory
|
|
241
|
-
|
|
248
|
+
_save_figure_exports(data, exports_dir, spec, basename)
|
|
242
249
|
|
|
243
|
-
# Copy nested .
|
|
250
|
+
# Copy nested .plot bundles directly (preserving all files)
|
|
244
251
|
if "plots" in data:
|
|
245
|
-
|
|
252
|
+
_copy_nested_plot_bundles(data["plots"], dir_path)
|
|
246
253
|
|
|
247
254
|
# Generate composed figure in exports/ (Figure1.png, Figure1.svg)
|
|
248
255
|
try:
|
|
@@ -250,26 +257,28 @@ def save_figz_bundle(data: Dict[str, Any], dir_path: Path) -> None:
|
|
|
250
257
|
except Exception as e:
|
|
251
258
|
logger.debug(f"Could not generate composed figure: {e}")
|
|
252
259
|
|
|
253
|
-
# Generate
|
|
260
|
+
# Generate figure overview in exports/
|
|
254
261
|
try:
|
|
255
|
-
|
|
262
|
+
_generate_figure_overview(dir_path, spec, data, basename)
|
|
256
263
|
except Exception as e:
|
|
257
|
-
logger.debug(f"Could not generate
|
|
264
|
+
logger.debug(f"Could not generate figure overview: {e}")
|
|
258
265
|
|
|
259
266
|
# Generate figure-level geometry cache
|
|
260
267
|
try:
|
|
261
|
-
|
|
268
|
+
_generate_figure_geometry_cache(dir_path, spec, basename)
|
|
262
269
|
except Exception as e:
|
|
263
|
-
logger.debug(f"Could not generate
|
|
270
|
+
logger.debug(f"Could not generate figure geometry cache: {e}")
|
|
264
271
|
|
|
265
272
|
# Generate README.md
|
|
266
273
|
try:
|
|
267
|
-
|
|
274
|
+
_generate_figure_readme(dir_path, spec, data, basename)
|
|
268
275
|
except Exception as e:
|
|
269
|
-
logger.debug(f"Could not generate
|
|
276
|
+
logger.debug(f"Could not generate figure README: {e}")
|
|
270
277
|
|
|
271
278
|
|
|
272
|
-
def
|
|
279
|
+
def _save_figure_exports(
|
|
280
|
+
data: Dict[str, Any], exports_dir: Path, spec: Dict, basename: str
|
|
281
|
+
) -> None:
|
|
273
282
|
"""Save figure-level export files to exports/ directory.
|
|
274
283
|
|
|
275
284
|
Args:
|
|
@@ -297,12 +306,15 @@ def _save_figz_exports(data: Dict[str, Any], exports_dir: Path, spec: Dict, base
|
|
|
297
306
|
_embed_metadata_in_export(out_file, spec, fmt)
|
|
298
307
|
except Exception as e:
|
|
299
308
|
import logging
|
|
309
|
+
|
|
300
310
|
logging.getLogger("scitex").debug(
|
|
301
311
|
f"Could not embed metadata in {out_file}: {e}"
|
|
302
312
|
)
|
|
303
313
|
|
|
304
314
|
|
|
305
|
-
def _save_exports(
|
|
315
|
+
def _save_exports(
|
|
316
|
+
data: Dict[str, Any], dir_path: Path, spec: Dict, basename: str = "figure"
|
|
317
|
+
) -> None:
|
|
306
318
|
"""Save export files (PNG, SVG, PDF) with embedded metadata. (Legacy - root level)"""
|
|
307
319
|
for fmt in ["png", "svg", "pdf"]:
|
|
308
320
|
if fmt not in data:
|
|
@@ -323,42 +335,43 @@ def _save_exports(data: Dict[str, Any], dir_path: Path, spec: Dict, basename: st
|
|
|
323
335
|
_embed_metadata_in_export(out_file, spec, fmt)
|
|
324
336
|
except Exception as e:
|
|
325
337
|
import logging
|
|
338
|
+
|
|
326
339
|
logging.getLogger("scitex").debug(
|
|
327
340
|
f"Could not embed metadata in {out_file}: {e}"
|
|
328
341
|
)
|
|
329
342
|
|
|
330
343
|
|
|
331
|
-
def
|
|
332
|
-
"""Copy nested .
|
|
344
|
+
def _copy_nested_plot_bundles(plots: Dict[str, Any], dir_path: Path) -> None:
|
|
345
|
+
"""Copy nested .plot bundles directly, preserving all files.
|
|
333
346
|
|
|
334
347
|
Args:
|
|
335
348
|
plots: Dict mapping panel IDs to either:
|
|
336
|
-
- source_path: Path to existing .
|
|
349
|
+
- source_path: Path to existing .plot directory or .plot.zip
|
|
337
350
|
- bundle_data: Dict with spec/data (will use save_bundle)
|
|
338
|
-
dir_path: Target
|
|
351
|
+
dir_path: Target figure directory.
|
|
339
352
|
"""
|
|
340
353
|
for panel_id, plot_source in plots.items():
|
|
341
354
|
if isinstance(plot_source, (str, Path)):
|
|
342
355
|
# Direct copy from source path
|
|
343
356
|
source_path = Path(plot_source)
|
|
344
357
|
|
|
345
|
-
if source_path.is_dir() and str(source_path).endswith(
|
|
346
|
-
# Source is .
|
|
347
|
-
target_path = dir_path / f"{panel_id}.
|
|
358
|
+
if source_path.is_dir() and str(source_path).endswith(".plot"):
|
|
359
|
+
# Source is .plot directory - copy as directory
|
|
360
|
+
target_path = dir_path / f"{panel_id}.plot"
|
|
348
361
|
if target_path.exists():
|
|
349
362
|
shutil.rmtree(target_path)
|
|
350
363
|
shutil.copytree(source_path, target_path)
|
|
351
364
|
|
|
352
|
-
elif source_path.is_file() and str(source_path).endswith(
|
|
353
|
-
# Source is .
|
|
354
|
-
target_path = dir_path / f"{panel_id}.
|
|
365
|
+
elif source_path.is_file() and str(source_path).endswith(".plot.zip"):
|
|
366
|
+
# Source is .plot.zip file - copy as zip file (preserving zip format)
|
|
367
|
+
target_path = dir_path / f"{panel_id}.plot.zip"
|
|
355
368
|
if target_path.exists():
|
|
356
369
|
target_path.unlink()
|
|
357
370
|
shutil.copy2(source_path, target_path)
|
|
358
371
|
|
|
359
372
|
elif source_path.exists():
|
|
360
373
|
# Unknown format - try to copy as directory
|
|
361
|
-
target_path = dir_path / f"{panel_id}.
|
|
374
|
+
target_path = dir_path / f"{panel_id}.plot"
|
|
362
375
|
if source_path.is_dir():
|
|
363
376
|
if target_path.exists():
|
|
364
377
|
shutil.rmtree(target_path)
|
|
@@ -368,27 +381,30 @@ def _copy_nested_pltz_bundles(plots: Dict[str, Any], dir_path: Path) -> None:
|
|
|
368
381
|
# Check if it has source_path for direct copy
|
|
369
382
|
if "source_path" in plot_source:
|
|
370
383
|
source_path = Path(plot_source["source_path"])
|
|
371
|
-
if source_path.is_file() and str(source_path).endswith(
|
|
372
|
-
# .
|
|
373
|
-
target_path = dir_path / f"{panel_id}.
|
|
384
|
+
if source_path.is_file() and str(source_path).endswith(".plot.zip"):
|
|
385
|
+
# .plot.zip file
|
|
386
|
+
target_path = dir_path / f"{panel_id}.plot.zip"
|
|
374
387
|
if target_path.exists():
|
|
375
388
|
target_path.unlink()
|
|
376
389
|
shutil.copy2(source_path, target_path)
|
|
377
390
|
elif source_path.exists() and source_path.is_dir():
|
|
378
|
-
# .
|
|
379
|
-
target_path = dir_path / f"{panel_id}.
|
|
391
|
+
# .plot directory
|
|
392
|
+
target_path = dir_path / f"{panel_id}.plot"
|
|
380
393
|
if target_path.exists():
|
|
381
394
|
shutil.rmtree(target_path)
|
|
382
395
|
shutil.copytree(source_path, target_path)
|
|
383
396
|
else:
|
|
384
397
|
# Fallback to save (will lose images)
|
|
385
|
-
from scitex.io.bundle import
|
|
386
|
-
target_path = dir_path / f"{panel_id}.pltz.d"
|
|
387
|
-
save(plot_source, target_path, bundle_type=BundleType.PLTZ)
|
|
398
|
+
from scitex.io.bundle import BundleType, save
|
|
388
399
|
|
|
400
|
+
target_path = dir_path / f"{panel_id}.plot"
|
|
401
|
+
save(plot_source, target_path, bundle_type=BundleType.PLOT)
|
|
389
402
|
|
|
390
|
-
|
|
391
|
-
|
|
403
|
+
|
|
404
|
+
def _generate_figure_overview(
|
|
405
|
+
dir_path: Path, spec: Dict, data: Dict, basename: str
|
|
406
|
+
) -> None:
|
|
407
|
+
"""Generate overview image for figure bundle showing panels with hitmaps, overlays, and bboxes.
|
|
392
408
|
|
|
393
409
|
Args:
|
|
394
410
|
dir_path: Bundle directory path.
|
|
@@ -396,36 +412,36 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
396
412
|
data: Bundle data dictionary.
|
|
397
413
|
basename: Base filename for bundle files.
|
|
398
414
|
"""
|
|
399
|
-
import matplotlib.pyplot as plt
|
|
400
|
-
import matplotlib.gridspec as gridspec
|
|
401
|
-
import matplotlib.patches as patches
|
|
402
|
-
from PIL import Image
|
|
403
|
-
import numpy as np
|
|
404
|
-
import warnings
|
|
405
415
|
import tempfile
|
|
416
|
+
import warnings
|
|
406
417
|
import zipfile
|
|
407
418
|
|
|
408
|
-
|
|
419
|
+
import matplotlib.gridspec as gridspec
|
|
420
|
+
import matplotlib.pyplot as plt
|
|
421
|
+
import numpy as np
|
|
422
|
+
from PIL import Image
|
|
423
|
+
|
|
424
|
+
# Find all panel bundles (both .plot directories and .plot.zip files)
|
|
409
425
|
panel_dirs = []
|
|
410
426
|
temp_dirs_to_cleanup = []
|
|
411
427
|
|
|
412
428
|
for item in dir_path.iterdir():
|
|
413
|
-
if item.is_dir() and str(item).endswith(
|
|
429
|
+
if item.is_dir() and str(item).endswith(".plot"):
|
|
414
430
|
panel_dirs.append(item)
|
|
415
|
-
elif item.is_file() and str(item).endswith(
|
|
416
|
-
# Extract .
|
|
417
|
-
temp_dir = tempfile.mkdtemp(prefix=f
|
|
431
|
+
elif item.is_file() and str(item).endswith(".plot.zip"):
|
|
432
|
+
# Extract .plot.zip to temp directory for overview generation
|
|
433
|
+
temp_dir = tempfile.mkdtemp(prefix=f"scitex_overview_{item.stem}_")
|
|
418
434
|
temp_dirs_to_cleanup.append(temp_dir)
|
|
419
|
-
with zipfile.ZipFile(item,
|
|
435
|
+
with zipfile.ZipFile(item, "r") as zf:
|
|
420
436
|
zf.extractall(temp_dir)
|
|
421
|
-
# Find the extracted .
|
|
437
|
+
# Find the extracted .plot directory
|
|
422
438
|
extracted = Path(temp_dir)
|
|
423
439
|
for subitem in extracted.iterdir():
|
|
424
|
-
if subitem.is_dir() and str(subitem).endswith(
|
|
440
|
+
if subitem.is_dir() and str(subitem).endswith(".plot"):
|
|
425
441
|
panel_dirs.append(subitem)
|
|
426
442
|
break
|
|
427
443
|
else:
|
|
428
|
-
# Use temp dir directly if no .
|
|
444
|
+
# Use temp dir directly if no .plot subfolder
|
|
429
445
|
panel_dirs.append(extracted)
|
|
430
446
|
|
|
431
447
|
panel_dirs = sorted(panel_dirs, key=lambda x: x.name)
|
|
@@ -446,19 +462,29 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
446
462
|
fig.suptitle(f"Figure Overview: {title}", fontsize=14, fontweight="bold", y=0.99)
|
|
447
463
|
|
|
448
464
|
# Create gridspec - 2 rows per panel, 3 columns
|
|
449
|
-
gs = gridspec.GridSpec(
|
|
450
|
-
|
|
465
|
+
gs = gridspec.GridSpec(
|
|
466
|
+
n_panels * 2,
|
|
467
|
+
3,
|
|
468
|
+
figure=fig,
|
|
469
|
+
hspace=0.3,
|
|
470
|
+
wspace=0.15,
|
|
471
|
+
height_ratios=[1, 1] * n_panels,
|
|
472
|
+
)
|
|
451
473
|
|
|
452
474
|
# Add each panel
|
|
453
475
|
for idx, panel_dir in enumerate(panel_dirs):
|
|
454
|
-
panel_id = panel_dir.stem.replace(".
|
|
476
|
+
panel_id = panel_dir.stem.replace(".plot", "")
|
|
455
477
|
row_base = idx * 2 # Two rows per panel
|
|
456
478
|
|
|
457
479
|
# Find PNG in panel directory (check exports/ first for layered format, then root)
|
|
458
480
|
png_files = list(panel_dir.glob("exports/*.png"))
|
|
459
481
|
if not png_files:
|
|
460
482
|
png_files = list(panel_dir.glob("*.png"))
|
|
461
|
-
main_pngs = [
|
|
483
|
+
main_pngs = [
|
|
484
|
+
f
|
|
485
|
+
for f in png_files
|
|
486
|
+
if "_hitmap" not in f.name and "_overview" not in f.name
|
|
487
|
+
]
|
|
462
488
|
|
|
463
489
|
# Find hitmap PNG
|
|
464
490
|
hitmap_files = list(panel_dir.glob("exports/*_hitmap.png"))
|
|
@@ -469,7 +495,7 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
469
495
|
geometry_data = {}
|
|
470
496
|
geometry_path = panel_dir / "cache" / "geometry_px.json"
|
|
471
497
|
if geometry_path.exists():
|
|
472
|
-
with open(geometry_path
|
|
498
|
+
with open(geometry_path) as f:
|
|
473
499
|
geometry_data = json.load(f)
|
|
474
500
|
|
|
475
501
|
# === Row 1: Plot | Hitmap | Overlay ===
|
|
@@ -482,7 +508,14 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
482
508
|
main_img = Image.open(main_pngs[0])
|
|
483
509
|
ax_main.imshow(main_img)
|
|
484
510
|
else:
|
|
485
|
-
ax_main.text(
|
|
511
|
+
ax_main.text(
|
|
512
|
+
0.5,
|
|
513
|
+
0.5,
|
|
514
|
+
"No image",
|
|
515
|
+
ha="center",
|
|
516
|
+
va="center",
|
|
517
|
+
transform=ax_main.transAxes,
|
|
518
|
+
)
|
|
486
519
|
ax_main.axis("off")
|
|
487
520
|
|
|
488
521
|
# Middle subplot: hitmap
|
|
@@ -494,7 +527,14 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
494
527
|
hitmap_img = Image.open(hitmap_files[0])
|
|
495
528
|
ax_hitmap.imshow(hitmap_img)
|
|
496
529
|
else:
|
|
497
|
-
ax_hitmap.text(
|
|
530
|
+
ax_hitmap.text(
|
|
531
|
+
0.5,
|
|
532
|
+
0.5,
|
|
533
|
+
"No hitmap",
|
|
534
|
+
ha="center",
|
|
535
|
+
va="center",
|
|
536
|
+
transform=ax_hitmap.transAxes,
|
|
537
|
+
)
|
|
498
538
|
ax_hitmap.axis("off")
|
|
499
539
|
|
|
500
540
|
# Right subplot: overlay
|
|
@@ -510,7 +550,14 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
510
550
|
hitmap_array[:, :, 3] = (hitmap_array[:, :, 3] * 0.5).astype(np.uint8)
|
|
511
551
|
ax_overlay.imshow(hitmap_array, alpha=0.5)
|
|
512
552
|
else:
|
|
513
|
-
ax_overlay.text(
|
|
553
|
+
ax_overlay.text(
|
|
554
|
+
0.5,
|
|
555
|
+
0.5,
|
|
556
|
+
"No overlay",
|
|
557
|
+
ha="center",
|
|
558
|
+
va="center",
|
|
559
|
+
transform=ax_overlay.transAxes,
|
|
560
|
+
)
|
|
514
561
|
ax_overlay.axis("off")
|
|
515
562
|
|
|
516
563
|
# === Row 2: Bboxes ===
|
|
@@ -522,7 +569,14 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
522
569
|
# Draw bboxes from geometry
|
|
523
570
|
_draw_bboxes_from_geometry(ax_bboxes, geometry_data)
|
|
524
571
|
else:
|
|
525
|
-
ax_bboxes.text(
|
|
572
|
+
ax_bboxes.text(
|
|
573
|
+
0.5,
|
|
574
|
+
0.5,
|
|
575
|
+
"No image",
|
|
576
|
+
ha="center",
|
|
577
|
+
va="center",
|
|
578
|
+
transform=ax_bboxes.transAxes,
|
|
579
|
+
)
|
|
526
580
|
ax_bboxes.axis("off")
|
|
527
581
|
|
|
528
582
|
# Info panel
|
|
@@ -536,13 +590,13 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
536
590
|
info_text = ""
|
|
537
591
|
|
|
538
592
|
if spec_path.exists():
|
|
539
|
-
with open(spec_path
|
|
593
|
+
with open(spec_path) as f:
|
|
540
594
|
spec_data = json.load(f)
|
|
541
595
|
info_text += f"Axes: {len(spec_data.get('axes', []))}\n"
|
|
542
596
|
info_text += f"Traces: {len(spec_data.get('traces', []))}\n"
|
|
543
597
|
|
|
544
598
|
if style_path.exists():
|
|
545
|
-
with open(style_path
|
|
599
|
+
with open(style_path) as f:
|
|
546
600
|
style_data = json.load(f)
|
|
547
601
|
size = style_data.get("size", {})
|
|
548
602
|
info_text += f"Size: {size.get('width_mm', 0):.1f} × {size.get('height_mm', 0):.1f} mm\n"
|
|
@@ -550,15 +604,22 @@ def _generate_figz_overview(dir_path: Path, spec: Dict, data: Dict, basename: st
|
|
|
550
604
|
|
|
551
605
|
manifest_path = panel_dir / "cache" / "render_manifest.json"
|
|
552
606
|
if manifest_path.exists():
|
|
553
|
-
with open(manifest_path
|
|
607
|
+
with open(manifest_path) as f:
|
|
554
608
|
manifest_data = json.load(f)
|
|
555
609
|
info_text += f"DPI: {manifest_data.get('dpi', 300)}\n"
|
|
556
610
|
render_px = manifest_data.get("render_px", [0, 0])
|
|
557
611
|
info_text += f"Pixels: {render_px[0]} × {render_px[1]}\n"
|
|
558
612
|
|
|
559
|
-
ax_info.text(
|
|
560
|
-
|
|
561
|
-
|
|
613
|
+
ax_info.text(
|
|
614
|
+
0.02,
|
|
615
|
+
0.98,
|
|
616
|
+
info_text,
|
|
617
|
+
transform=ax_info.transAxes,
|
|
618
|
+
fontsize=10,
|
|
619
|
+
fontfamily="monospace",
|
|
620
|
+
verticalalignment="top",
|
|
621
|
+
bbox=dict(boxstyle="round", facecolor="wheat", alpha=0.5),
|
|
622
|
+
)
|
|
562
623
|
|
|
563
624
|
# Save overview to exports/ directory
|
|
564
625
|
exports_dir = dir_path / "exports"
|
|
@@ -584,7 +645,6 @@ def _draw_bboxes_from_geometry(ax, geometry_data: Dict) -> None:
|
|
|
584
645
|
ax: Matplotlib axes.
|
|
585
646
|
geometry_data: Geometry data dictionary.
|
|
586
647
|
"""
|
|
587
|
-
import matplotlib.patches as patches
|
|
588
648
|
|
|
589
649
|
colors = ["red", "blue", "green", "orange", "purple", "cyan"]
|
|
590
650
|
selectable = geometry_data.get("selectable_regions", {})
|
|
@@ -645,8 +705,9 @@ def _draw_single_bbox(ax, bbox: List, color: str, label: str, lw: int = 2) -> No
|
|
|
645
705
|
x0, y0, x1, y1 = bbox
|
|
646
706
|
width = x1 - x0
|
|
647
707
|
height = y1 - y0
|
|
648
|
-
rect = patches.Rectangle(
|
|
649
|
-
|
|
708
|
+
rect = patches.Rectangle(
|
|
709
|
+
(x0, y0), width, height, linewidth=lw, edgecolor=color, facecolor="none"
|
|
710
|
+
)
|
|
650
711
|
ax.add_patch(rect)
|
|
651
712
|
# Add label
|
|
652
713
|
ax.text(x0 + 2, y0 + height / 2, label, fontsize=6, color=color, fontweight="bold")
|
|
@@ -663,8 +724,8 @@ def _generate_composed_figure(dir_path: Path, spec: Dict, basename: str) -> None
|
|
|
663
724
|
spec: Bundle specification with panel layout.
|
|
664
725
|
basename: Base filename for exports.
|
|
665
726
|
"""
|
|
727
|
+
|
|
666
728
|
from PIL import Image
|
|
667
|
-
import warnings
|
|
668
729
|
|
|
669
730
|
exports_dir = dir_path / "exports"
|
|
670
731
|
exports_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -672,7 +733,7 @@ def _generate_composed_figure(dir_path: Path, spec: Dict, basename: str) -> None
|
|
|
672
733
|
# Load style from style.json if exists, else from spec
|
|
673
734
|
style_file = dir_path / "style.json"
|
|
674
735
|
if style_file.exists():
|
|
675
|
-
with open(style_file
|
|
736
|
+
with open(style_file) as f:
|
|
676
737
|
style = json.load(f)
|
|
677
738
|
size = style.get("size", {})
|
|
678
739
|
background = style.get("background", "#ffffff")
|
|
@@ -702,11 +763,11 @@ def _generate_composed_figure(dir_path: Path, spec: Dict, basename: str) -> None
|
|
|
702
763
|
panel_id = panel.get("id", "")
|
|
703
764
|
plot_ref = panel.get("plot", "")
|
|
704
765
|
|
|
705
|
-
# Find the panel's
|
|
706
|
-
if plot_ref.endswith(".
|
|
766
|
+
# Find the panel's plot bundle
|
|
767
|
+
if plot_ref.endswith(".plot"):
|
|
707
768
|
panel_dir = dir_path / plot_ref
|
|
708
769
|
else:
|
|
709
|
-
panel_dir = dir_path / f"{panel_id}.
|
|
770
|
+
panel_dir = dir_path / f"{panel_id}.plot"
|
|
710
771
|
|
|
711
772
|
if not panel_dir.exists():
|
|
712
773
|
continue
|
|
@@ -749,7 +810,9 @@ def _generate_composed_figure(dir_path: Path, spec: Dict, basename: str) -> None
|
|
|
749
810
|
target_height = int(height_mm * mm_to_inch * dpi)
|
|
750
811
|
|
|
751
812
|
# Resize panel to fit
|
|
752
|
-
panel_img = panel_img.resize(
|
|
813
|
+
panel_img = panel_img.resize(
|
|
814
|
+
(target_width, target_height), Image.Resampling.LANCZOS
|
|
815
|
+
)
|
|
753
816
|
|
|
754
817
|
# Convert to RGB if necessary (for transparent PNGs)
|
|
755
818
|
if panel_img.mode == "RGBA":
|
|
@@ -774,22 +837,23 @@ def _generate_composed_figure(dir_path: Path, spec: Dict, basename: str) -> None
|
|
|
774
837
|
|
|
775
838
|
# Create simple SVG wrapper with embedded image
|
|
776
839
|
import base64
|
|
840
|
+
|
|
777
841
|
with open(png_path, "rb") as f:
|
|
778
842
|
png_b64 = base64.b64encode(f.read()).decode("utf-8")
|
|
779
843
|
|
|
780
|
-
svg_content = f
|
|
844
|
+
svg_content = f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
781
845
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
782
846
|
width="{fig_width_px}" height="{fig_height_px}"
|
|
783
847
|
viewBox="0 0 {fig_width_px} {fig_height_px}">
|
|
784
848
|
<image width="{fig_width_px}" height="{fig_height_px}"
|
|
785
849
|
xlink:href="data:image/png;base64,{png_b64}"/>
|
|
786
|
-
</svg>
|
|
850
|
+
</svg>"""
|
|
787
851
|
|
|
788
852
|
with open(svg_path, "w") as f:
|
|
789
853
|
f.write(svg_content)
|
|
790
854
|
|
|
791
855
|
|
|
792
|
-
def
|
|
856
|
+
def _generate_figure_geometry_cache(dir_path: Path, spec: Dict, basename: str) -> None:
|
|
793
857
|
"""Generate figure-level geometry cache combining all panel geometries.
|
|
794
858
|
|
|
795
859
|
Creates:
|
|
@@ -801,9 +865,9 @@ def _generate_figz_geometry_cache(dir_path: Path, spec: Dict, basename: str) ->
|
|
|
801
865
|
spec: Bundle specification.
|
|
802
866
|
basename: Base filename for bundle files.
|
|
803
867
|
"""
|
|
804
|
-
from datetime import datetime
|
|
805
868
|
import tempfile
|
|
806
869
|
import zipfile
|
|
870
|
+
from datetime import datetime
|
|
807
871
|
|
|
808
872
|
cache_dir = dir_path / "cache"
|
|
809
873
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -815,22 +879,22 @@ def _generate_figz_geometry_cache(dir_path: Path, spec: Dict, basename: str) ->
|
|
|
815
879
|
"generated_at": datetime.now().isoformat(),
|
|
816
880
|
}
|
|
817
881
|
|
|
818
|
-
# Find all panel bundles (both .
|
|
882
|
+
# Find all panel bundles (both .plot directories and .plot.zip files)
|
|
819
883
|
panel_sources = []
|
|
820
884
|
temp_dirs_to_cleanup = []
|
|
821
885
|
|
|
822
886
|
for item in dir_path.iterdir():
|
|
823
|
-
if item.is_dir() and str(item).endswith(
|
|
824
|
-
panel_sources.append((item.stem.replace(
|
|
825
|
-
elif item.is_file() and str(item).endswith(
|
|
826
|
-
# Extract .
|
|
827
|
-
temp_dir = tempfile.mkdtemp(prefix=f
|
|
887
|
+
if item.is_dir() and str(item).endswith(".plot"):
|
|
888
|
+
panel_sources.append((item.stem.replace(".plot", ""), item))
|
|
889
|
+
elif item.is_file() and str(item).endswith(".plot.zip"):
|
|
890
|
+
# Extract .plot.zip to temp directory
|
|
891
|
+
temp_dir = tempfile.mkdtemp(prefix=f"scitex_geom_{item.stem}_")
|
|
828
892
|
temp_dirs_to_cleanup.append(temp_dir)
|
|
829
|
-
with zipfile.ZipFile(item,
|
|
893
|
+
with zipfile.ZipFile(item, "r") as zf:
|
|
830
894
|
zf.extractall(temp_dir)
|
|
831
895
|
extracted = Path(temp_dir)
|
|
832
896
|
for subitem in extracted.iterdir():
|
|
833
|
-
if subitem.is_dir() and str(subitem).endswith(
|
|
897
|
+
if subitem.is_dir() and str(subitem).endswith(".plot"):
|
|
834
898
|
panel_sources.append((item.stem, subitem))
|
|
835
899
|
break
|
|
836
900
|
else:
|
|
@@ -839,11 +903,10 @@ def _generate_figz_geometry_cache(dir_path: Path, spec: Dict, basename: str) ->
|
|
|
839
903
|
panel_sources = sorted(panel_sources, key=lambda x: x[0])
|
|
840
904
|
|
|
841
905
|
for panel_id, panel_dir in panel_sources:
|
|
842
|
-
|
|
843
906
|
# Load panel geometry
|
|
844
907
|
panel_geometry_path = panel_dir / "cache" / "geometry_px.json"
|
|
845
908
|
if panel_geometry_path.exists():
|
|
846
|
-
with open(panel_geometry_path
|
|
909
|
+
with open(panel_geometry_path) as f:
|
|
847
910
|
panel_geometry = json.load(f)
|
|
848
911
|
combined_geometry["panels"][panel_id] = panel_geometry
|
|
849
912
|
|
|
@@ -852,7 +915,9 @@ def _generate_figz_geometry_cache(dir_path: Path, spec: Dict, basename: str) ->
|
|
|
852
915
|
for panel in panels_spec:
|
|
853
916
|
panel_id = panel.get("id")
|
|
854
917
|
if panel_id and panel_id in combined_geometry["panels"]:
|
|
855
|
-
combined_geometry["panels"][panel_id]["position_mm"] = panel.get(
|
|
918
|
+
combined_geometry["panels"][panel_id]["position_mm"] = panel.get(
|
|
919
|
+
"position", {}
|
|
920
|
+
)
|
|
856
921
|
combined_geometry["panels"][panel_id]["size_mm"] = panel.get("size", {})
|
|
857
922
|
|
|
858
923
|
# Save combined geometry
|
|
@@ -884,9 +949,7 @@ def _generate_figz_geometry_cache(dir_path: Path, spec: Dict, basename: str) ->
|
|
|
884
949
|
pass
|
|
885
950
|
|
|
886
951
|
|
|
887
|
-
def _embed_metadata_in_export(
|
|
888
|
-
file_path: Path, spec: Dict[str, Any], fmt: str
|
|
889
|
-
) -> None:
|
|
952
|
+
def _embed_metadata_in_export(file_path: Path, spec: Dict[str, Any], fmt: str) -> None:
|
|
890
953
|
"""Embed bundle spec metadata into exported image files."""
|
|
891
954
|
from scitex.io._metadata import embed_metadata
|
|
892
955
|
|
|
@@ -903,10 +966,10 @@ def _embed_metadata_in_export(
|
|
|
903
966
|
embed_metadata(str(file_path), embed_data)
|
|
904
967
|
|
|
905
968
|
|
|
906
|
-
def
|
|
969
|
+
def _generate_figure_readme(
|
|
907
970
|
dir_path: Path, spec: Dict, data: Dict, basename: str
|
|
908
971
|
) -> None:
|
|
909
|
-
"""Generate a dynamic README.md for
|
|
972
|
+
"""Generate a dynamic README.md for figure bundle.
|
|
910
973
|
|
|
911
974
|
Args:
|
|
912
975
|
dir_path: Bundle directory path.
|
|
@@ -924,7 +987,7 @@ def _generate_figz_readme(
|
|
|
924
987
|
# Load style from style.json if exists, else from spec.figure.styles
|
|
925
988
|
style_file = dir_path / "style.json"
|
|
926
989
|
if style_file.exists():
|
|
927
|
-
with open(style_file
|
|
990
|
+
with open(style_file) as f:
|
|
928
991
|
style = json.load(f)
|
|
929
992
|
size = style.get("size", {})
|
|
930
993
|
background = style.get("background", "#ffffff")
|
|
@@ -941,7 +1004,7 @@ def _generate_figz_readme(
|
|
|
941
1004
|
n_panels = len(panels)
|
|
942
1005
|
|
|
943
1006
|
# Find panel directories
|
|
944
|
-
panel_dirs = sorted(dir_path.glob("*.
|
|
1007
|
+
panel_dirs = sorted(dir_path.glob("*.plot"))
|
|
945
1008
|
|
|
946
1009
|
# Build panel table
|
|
947
1010
|
panel_rows = ""
|
|
@@ -958,7 +1021,7 @@ def _generate_figz_readme(
|
|
|
958
1021
|
for pd in panel_dirs:
|
|
959
1022
|
panel_dir_list += f"│ ├── {pd.name}/\n"
|
|
960
1023
|
|
|
961
|
-
readme_content = f"""# {basename}.
|
|
1024
|
+
readme_content = f"""# {basename}.figure
|
|
962
1025
|
|
|
963
1026
|
> SciTeX Figure Bundle - Auto-generated README
|
|
964
1027
|
|
|
@@ -969,10 +1032,10 @@ def _generate_figz_readme(
|
|
|
969
1032
|
## Bundle Structure
|
|
970
1033
|
|
|
971
1034
|
```
|
|
972
|
-
{basename}.
|
|
1035
|
+
{basename}.figure/
|
|
973
1036
|
├── spec.json # Figure specification (semantic: what to draw)
|
|
974
1037
|
├── style.json # Figure style (appearance: how it looks)
|
|
975
|
-
├── {basename}.json # Combined spec+style
|
|
1038
|
+
├── {basename}.json # Combined spec+style
|
|
976
1039
|
├── exports/ # Figure-level exports
|
|
977
1040
|
│ ├── {basename}.png # Rendered figure (raster)
|
|
978
1041
|
│ ├── {basename}.svg # Rendered figure (vector)
|
|
@@ -987,7 +1050,7 @@ def _generate_figz_readme(
|
|
|
987
1050
|
|
|
988
1051
|
| Property | Value |
|
|
989
1052
|
|----------|-------|
|
|
990
|
-
| Title | {title or
|
|
1053
|
+
| Title | {title or "(none)"} |
|
|
991
1054
|
| Panels | {n_panels} |
|
|
992
1055
|
| Size | {width_mm:.1f} × {height_mm:.1f} mm |
|
|
993
1056
|
| Background | `{background}` |
|
|
@@ -1002,7 +1065,7 @@ def _generate_figz_readme(
|
|
|
1002
1065
|
|
|
1003
1066
|
## Nested Bundles
|
|
1004
1067
|
|
|
1005
|
-
Each panel is stored as a separate `.
|
|
1068
|
+
Each panel is stored as a separate `.plot` bundle containing:
|
|
1006
1069
|
- `spec.json` - What to plot (data, axes, traces)
|
|
1007
1070
|
- `style.json` - How it looks (colors, fonts, theme)
|
|
1008
1071
|
- `exports/` - Rendered images (PNG, SVG, hitmap)
|
|
@@ -1023,7 +1086,7 @@ spec = bundle["spec"] # Figure layout
|
|
|
1023
1086
|
plots = bundle["plots"] # Dict of panel bundles
|
|
1024
1087
|
|
|
1025
1088
|
# Access specific panel
|
|
1026
|
-
panel_a = plots["A"] # Get panel A's
|
|
1089
|
+
panel_a = plots["A"] # Get panel A's plot bundle
|
|
1027
1090
|
```
|
|
1028
1091
|
|
|
1029
1092
|
### Editing
|
|
@@ -1039,15 +1102,15 @@ Edit `style.json` to change appearance:
|
|
|
1039
1102
|
- Panel label styling
|
|
1040
1103
|
- Theme (light/dark)
|
|
1041
1104
|
|
|
1042
|
-
Edit individual `*.
|
|
1105
|
+
Edit individual `*.plot/spec.json` and `*.plot/style.json` to change:
|
|
1043
1106
|
- Plot data and axes (spec.json)
|
|
1044
1107
|
- Trace specifications (spec.json)
|
|
1045
1108
|
- Colors, fonts, theme (style.json)
|
|
1046
1109
|
|
|
1047
1110
|
---
|
|
1048
1111
|
|
|
1049
|
-
*Generated: {datetime.now().strftime(
|
|
1050
|
-
*Schema: {spec.get("schema", {}).get("name", "scitex.
|
|
1112
|
+
*Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}*
|
|
1113
|
+
*Schema: {spec.get("schema", {}).get("name", "scitex.canvas.figure")} v{spec.get("schema", {}).get("version", "1.0.0")}*
|
|
1051
1114
|
"""
|
|
1052
1115
|
|
|
1053
1116
|
readme_path = dir_path / "README.md"
|