scitex 2.10.2__py3-none-any.whl → 2.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scitex/__init__.py +1 -4
- scitex/__version__.py +1 -1
- scitex/_install_guide.py +14 -2
- scitex/bridge/_figrecipe.py +1 -1
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_plt.py +1 -1
- scitex/bridge/_stats_vis.py +2 -2
- scitex/{fig → canvas}/__init__.py +84 -96
- scitex/{fig → canvas}/backend/_parser.py +1 -1
- scitex/{fig → canvas}/canvas.py +13 -14
- scitex/{fts/_fig/_editor → canvas/editor}/_defaults.py +2 -2
- scitex/{fig → canvas}/editor/edit/__init__.py +11 -14
- scitex/{fig → canvas}/editor/edit/bundle_resolver.py +56 -48
- scitex/{fig → canvas}/editor/edit/editor_launcher.py +79 -26
- scitex/{fts/_fig/_editor/_cui/_panel_loader.py → canvas/editor/edit/panel_loader.py} +8 -8
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_bbox.py +2 -1
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_core.py +84 -84
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_renderer.py +7 -6
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/static/css/features/canvas.css +2 -2
- scitex/{fig → canvas}/editor/flask_editor/static/css/features/panel-grid.css +1 -1
- scitex/{fig → canvas}/editor/flask_editor/static/js/core/api.js +3 -4
- scitex/{fig → canvas}/editor/flask_editor/static/js/editor/preview.js +5 -5
- scitex/{fig → canvas}/editor/flask_editor/templates/_html.py +3 -3
- scitex/{fig → canvas}/editor/flask_editor/templates/_scripts.py +10 -10
- scitex/{fig → canvas}/editor/flask_editor/templates/_styles.py +3 -3
- scitex/{fig → canvas}/io/__init__.py +32 -38
- scitex/{fig → canvas}/io/_bundle.py +217 -154
- scitex/{fig → canvas}/io/_canvas.py +1 -1
- scitex/{fig → canvas}/io/_data.py +1 -1
- scitex/{fig → canvas}/io/_export.py +1 -1
- scitex/{fig → canvas}/io/_load.py +1 -1
- scitex/{fig → canvas}/io/_panel.py +1 -1
- scitex/{fig → canvas}/io/_save.py +1 -1
- scitex/{fig → canvas}/model/__init__.py +1 -1
- scitex/{fig → canvas}/model/_annotations.py +1 -1
- scitex/{fig → canvas}/model/_axes.py +1 -1
- scitex/{fig → canvas}/model/_figure.py +1 -1
- scitex/{fig → canvas}/model/_guides.py +1 -1
- scitex/{fig → canvas}/model/_plot.py +1 -1
- scitex/{fig → canvas}/model/_styles.py +1 -1
- scitex/{fig → canvas}/utils/__init__.py +1 -1
- scitex/cli/convert.py +10 -6
- scitex/diagram/README.md +7 -7
- scitex/io/__init__.py +7 -19
- scitex/io/_load.py +15 -19
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_con.py +5 -5
- scitex/io/_load_modules/_eeg.py +16 -12
- scitex/io/_save.py +11 -16
- scitex/io/_save_modules/__init__.py +6 -10
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_plot_bundle.py +112 -0
- scitex/io/_save_modules/{_pltz_stx.py → _plot_scitex.py} +7 -7
- scitex/io/_save_modules/_stx_bundle.py +16 -16
- scitex/io/bundle/README.md +89 -80
- scitex/{fts/_bundle/_FTS.py → io/bundle/_Bundle.py} +197 -95
- scitex/io/bundle/__init__.py +67 -35
- scitex/{fts/_bundle → io/bundle}/_children.py +32 -40
- scitex/io/bundle/_core.py +184 -97
- scitex/{fts/_bundle/_dataclasses/_Node.py → io/bundle/_dataclasses/_Spec.py} +29 -23
- scitex/{fts/_bundle/_dataclasses/_NodeRefs.py → io/bundle/_dataclasses/_SpecRefs.py} +6 -6
- scitex/{fts/_bundle → io/bundle}/_dataclasses/__init__.py +4 -4
- scitex/{fts/_bundle → io/bundle}/_loader.py +19 -19
- scitex/io/bundle/_manifest.py +99 -0
- scitex/{fts/_bundle → io/bundle}/_mpl_helpers.py +119 -28
- scitex/io/bundle/_nested.py +113 -100
- scitex/{fts/_bundle → io/bundle}/_saver.py +13 -14
- scitex/{fts/_bundle → io/bundle}/_storage.py +3 -3
- scitex/io/bundle/_types.py +41 -16
- scitex/{fts/_bundle → io/bundle}/_validation.py +20 -18
- scitex/io/bundle/_zip.py +21 -31
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_backend/_parser.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Annotations.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Axes.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Figure.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Guides.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Plot.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Styles.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_utils/_plot_layout.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/__init__.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/_app.py +1 -1
- scitex/{fts/_tables → io/bundle/kinds/_table}/_latex/_export.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_figure_exporter.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_table_exporter.py +1 -1
- scitex/io/bundle/schemas/__init__.py +30 -0
- scitex/parallel/_run.py +5 -4
- scitex/path/_find.py +60 -83
- scitex/path/_get_module_path.py +23 -21
- scitex/path/_get_spath.py +6 -27
- scitex/path/_getsize.py +23 -9
- scitex/path/_increment_version.py +31 -38
- scitex/path/_mk_spath.py +26 -29
- scitex/path/_path.py +5 -12
- scitex/path/_split.py +27 -15
- scitex/path/_this_path.py +23 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +2 -1
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +2 -2
- scitex/plt/gallery/_generate.py +76 -50
- scitex/plt/io/__init__.py +17 -19
- scitex/plt/io/_bundle.py +99 -52
- scitex/plt/io/_layered_bundle.py +303 -168
- scitex/plt/utils/_csv_column_naming.py +250 -118
- scitex/schema/__init__.py +69 -73
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +30 -33
- scitex/stats/_schema.py +1 -1
- scitex/stats/io/__init__.py +10 -11
- scitex/stats/io/_bundle.py +16 -16
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/METADATA +191 -72
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/RECORD +237 -360
- scitex/fig/editor/_defaults.py +0 -300
- scitex/fig/editor/edit/panel_loader.py +0 -232
- scitex/fig/editor/flask_editor/_bbox.py +0 -1299
- scitex/fig/editor/flask_editor/_core.py +0 -1429
- scitex/fig/editor/flask_editor/_renderer.py +0 -813
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +0 -176
- scitex/fts/README.md +0 -262
- scitex/fts/TODO.md +0 -66
- scitex/fts/__init__.py +0 -90
- scitex/fts/_bundle/README_IN_BUNDLE.md +0 -102
- scitex/fts/_bundle/__init__.py +0 -38
- scitex/fts/_bundle/_utils/__init__.py +0 -55
- scitex/fts/_bundle/_utils/_const.py +0 -26
- scitex/fts/_bundle/_utils/_errors.py +0 -73
- scitex/fts/_bundle/_utils/_generate.py +0 -21
- scitex/fts/_bundle/_utils/_types.py +0 -76
- scitex/fts/_bundle/_zipbundle.py +0 -165
- scitex/fts/_fig/__init__.py +0 -22
- scitex/fts/_fig/_backend/_parser.py +0 -188
- scitex/fts/_fig/_editor/__init__.py +0 -14
- scitex/fts/_fig/_editor/_cui/__init__.py +0 -33
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +0 -39
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +0 -366
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +0 -175
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +0 -52
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +0 -66
- scitex/fts/_fig/_editor/_gui/__init__.py +0 -11
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +0 -20
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +0 -664
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +0 -79
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +0 -41
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +0 -16
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +0 -85
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +0 -217
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +0 -93
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +0 -112
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +0 -212
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +0 -190
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +0 -45
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +0 -95
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +0 -101
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +0 -138
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +0 -31
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +0 -7
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +0 -56
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +0 -78
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +0 -314
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +0 -107
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +0 -54
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +0 -172
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +0 -258
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +0 -48
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +0 -71
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +0 -288
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +0 -143
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +0 -245
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +0 -992
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +0 -339
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +0 -286
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +0 -371
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +0 -293
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +0 -426
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +0 -152
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +0 -265
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +0 -184
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +0 -100
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +0 -34
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +0 -124
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +0 -851
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +0 -4932
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +0 -1657
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +0 -36
- scitex/fts/_fig/_models/_Annotations.py +0 -115
- scitex/fts/_fig/_models/_Axes.py +0 -152
- scitex/fts/_fig/_models/_Figure.py +0 -138
- scitex/fts/_fig/_models/_Plot.py +0 -123
- scitex/fts/_fig/_utils/_plot_layout.py +0 -397
- scitex/fts/_kinds/_figure/_composite.py +0 -345
- scitex/fts/_kinds/_plot/_backend/__init__.py +0 -53
- scitex/fts/_kinds/_plot/_backend/_export.py +0 -165
- scitex/fts/_kinds/_plot/_backend/_render.py +0 -538
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +0 -46
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +0 -82
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +0 -441
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +0 -52
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +0 -47
- scitex/fts/_kinds/_plot/_models/_Guides.py +0 -104
- scitex/fts/_kinds/_plot/_models/_Styles.py +0 -245
- scitex/fts/_kinds/_plot/_models/__init__.py +0 -80
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +0 -156
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +0 -43
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +0 -38
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +0 -36
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +0 -60
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +0 -61
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +0 -57
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +0 -121
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +0 -36
- scitex/fts/_kinds/_plot/_utils/__init__.py +0 -129
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +0 -127
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +0 -111
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +0 -48
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +0 -77
- scitex/fts/_kinds/_plot/_utils/_get_template.py +0 -178
- scitex/fts/_kinds/_plot/_utils/_normalize.py +0 -73
- scitex/fts/_kinds/_plot/_utils/_validate.py +0 -197
- scitex/fts/_kinds/_table/_latex/_export.py +0 -279
- scitex/fts/_stats/__init__.py +0 -48
- scitex/fts/_stats/_dataclasses/_Stats.py +0 -423
- scitex/fts/_stats/_dataclasses/__init__.py +0 -48
- scitex/fts/_tables/__init__.py +0 -65
- scitex/fts/_tables/_latex/__init__.py +0 -93
- scitex/fts/_tables/_latex/_editor/__init__.py +0 -11
- scitex/fts/_tables/_latex/_editor/_app.py +0 -725
- scitex/fts/_tables/_latex/_figure_exporter.py +0 -153
- scitex/fts/_tables/_latex/_stats_formatter.py +0 -274
- scitex/fts/_tables/_latex/_table_exporter.py +0 -362
- scitex/fts/_tables/_latex/_utils.py +0 -369
- scitex/fts/_tables/_latex/_validator.py +0 -445
- scitex/io/_save_modules/_pltz_bundle.py +0 -356
- /scitex/{fig → canvas}/README.md +0 -0
- /scitex/{fig → canvas}/backend/__init__.py +0 -0
- /scitex/{fig → canvas}/backend/_export.py +0 -0
- /scitex/{fig → canvas}/backend/_render.py +0 -0
- /scitex/{fig → canvas}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{fig → canvas}/editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/_dearpygui_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_flask_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_mpl_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_qt_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_tkinter_editor.py +0 -0
- /scitex/{fig → canvas}/editor/edit/backend_detector.py +0 -0
- /scitex/{fig → canvas}/editor/edit/manual_handler.py +0 -0
- /scitex/{fig → canvas}/editor/edit/path_resolver.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_plotter.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_utils.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/reset.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/typography.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/variables.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/buttons.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/context-menu.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/dropdown.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/forms.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/modal.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/sections.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/element-inspector.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/loading.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/overlay.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/selection.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/statistics.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/index.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/container.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/controls.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/preview.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/axis.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/basic.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/distribute.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/canvas.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/dragging.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/resize.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/selection.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/state.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/utils.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/dev/element-inspector.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/bbox.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/element-drag.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/overlay.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/main.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/context-menu.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/keyboard.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/controls.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/download.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/help.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/theme.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/templates/__init__.py +0 -0
- /scitex/{fig → canvas}/io/_directory.py +0 -0
- /scitex/{fig → canvas}/model/_plot_types.py +0 -0
- /scitex/{fig → canvas}/utils/_defaults.py +0 -0
- /scitex/{fig → canvas}/utils/_validate.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_bundle2dict.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_dict2bundle.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_Axes.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_BBox.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_ColumnDef.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataFormat.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataInfo.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataSource.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_SizeMM.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_bar.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_line.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_scatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_figure/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_figure}/_composite.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_plot/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_export.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_render.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_ChannelEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Encoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Theme.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_TraceEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_bar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_box.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_distribution.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_errorbar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_histogram.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_image.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_line.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_scatter.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_seaborn.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_violin.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_auto_layout.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_calc_bounds.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_const_sizes.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_convert_coords.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_get_template.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_normalize.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_validate.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_shape/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/_Stats.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_stats_formatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_utils.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_validator.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_text/__init__.py +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/data_info.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/encoding.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/node.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/render_manifest.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/stats.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/theme.schema.json +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/WHEEL +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/licenses/LICENSE +0 -0
scitex/plt/io/_layered_bundle.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
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/plt/io/_layered_bundle.py
|
|
5
4
|
|
|
6
5
|
"""
|
|
7
|
-
Layered .
|
|
6
|
+
Layered .plot Bundle I/O - New schema with spec/style/geometry separation.
|
|
8
7
|
|
|
9
8
|
Bundle structure:
|
|
10
|
-
plot.
|
|
9
|
+
plot.plot/
|
|
11
10
|
spec.json # Semantic: WHAT to plot (canonical)
|
|
12
11
|
style.json # Appearance: HOW it looks (canonical)
|
|
13
12
|
data.csv # Raw data (immutable)
|
|
@@ -25,31 +24,43 @@ Design Principles:
|
|
|
25
24
|
- Canonical units: ratio (0-1) for axes bbox, mm for panel size
|
|
26
25
|
"""
|
|
27
26
|
|
|
28
|
-
import json
|
|
29
27
|
import hashlib
|
|
30
|
-
|
|
31
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
28
|
+
import json
|
|
32
29
|
from dataclasses import asdict
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Any, Dict, List, Optional
|
|
33
32
|
|
|
34
33
|
from scitex import logging
|
|
35
34
|
from scitex.plt.styles import get_default_dpi, get_preview_dpi
|
|
36
35
|
from scitex.schema import (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
PLOT_GEOMETRY_VERSION,
|
|
37
|
+
PLOT_SPEC_VERSION,
|
|
38
|
+
PLOT_STYLE_VERSION,
|
|
39
|
+
AxesLabels,
|
|
40
|
+
AxesLimits,
|
|
41
|
+
AxesSpecItem,
|
|
42
|
+
BboxPx,
|
|
43
|
+
BboxRatio,
|
|
44
|
+
DataSourceSpec,
|
|
45
|
+
FontSpec,
|
|
46
|
+
LegendSpec,
|
|
47
|
+
PlotGeometry,
|
|
48
|
+
PlotSpec,
|
|
49
|
+
PlotStyle,
|
|
50
|
+
RenderedArtist,
|
|
51
|
+
RenderedAxes,
|
|
52
|
+
RenderManifest,
|
|
53
|
+
SizeSpec,
|
|
54
|
+
ThemeSpec,
|
|
55
|
+
TraceSpec,
|
|
56
|
+
TraceStyleSpec,
|
|
46
57
|
)
|
|
47
58
|
|
|
48
59
|
logger = logging.getLogger()
|
|
49
60
|
|
|
50
61
|
__all__ = [
|
|
51
|
-
"
|
|
52
|
-
"
|
|
62
|
+
"save_layered_plot_bundle",
|
|
63
|
+
"load_layered_plot_bundle",
|
|
53
64
|
"merge_layered_bundle",
|
|
54
65
|
"is_layered_bundle",
|
|
55
66
|
]
|
|
@@ -60,7 +71,7 @@ def is_layered_bundle(bundle_dir: Path) -> bool:
|
|
|
60
71
|
return (bundle_dir / "spec.json").exists()
|
|
61
72
|
|
|
62
73
|
|
|
63
|
-
def
|
|
74
|
+
def save_layered_plot_bundle(
|
|
64
75
|
fig,
|
|
65
76
|
bundle_dir: Path,
|
|
66
77
|
basename: str = "plot",
|
|
@@ -68,14 +79,14 @@ def save_layered_pltz_bundle(
|
|
|
68
79
|
csv_df=None,
|
|
69
80
|
) -> None:
|
|
70
81
|
"""
|
|
71
|
-
Save matplotlib figure as layered .
|
|
82
|
+
Save matplotlib figure as layered .plot bundle.
|
|
72
83
|
|
|
73
84
|
Parameters
|
|
74
85
|
----------
|
|
75
86
|
fig : matplotlib.figure.Figure
|
|
76
87
|
The figure to save.
|
|
77
88
|
bundle_dir : Path
|
|
78
|
-
Output directory (e.g., plot.
|
|
89
|
+
Output directory (e.g., plot.plot).
|
|
79
90
|
basename : str
|
|
80
91
|
Base filename for exports.
|
|
81
92
|
dpi : int, optional
|
|
@@ -86,9 +97,10 @@ def save_layered_pltz_bundle(
|
|
|
86
97
|
# Resolve DPI from config if not specified
|
|
87
98
|
if dpi is None:
|
|
88
99
|
dpi = get_default_dpi()
|
|
89
|
-
import numpy as np
|
|
90
100
|
import tempfile
|
|
91
101
|
import warnings
|
|
102
|
+
|
|
103
|
+
import numpy as np
|
|
92
104
|
from PIL import Image as PILImage
|
|
93
105
|
|
|
94
106
|
bundle_dir = Path(bundle_dir)
|
|
@@ -103,7 +115,7 @@ def save_layered_pltz_bundle(
|
|
|
103
115
|
# Extract figure dimensions
|
|
104
116
|
fig_width_inch, fig_height_inch = fig.get_size_inches()
|
|
105
117
|
|
|
106
|
-
# === Build
|
|
118
|
+
# === Build PlotSpec (semantic) ===
|
|
107
119
|
axes_items = []
|
|
108
120
|
traces = []
|
|
109
121
|
extracted_data = {}
|
|
@@ -113,7 +125,7 @@ def save_layered_pltz_bundle(
|
|
|
113
125
|
ax_id = f"ax{ax_idx}"
|
|
114
126
|
|
|
115
127
|
# Create axes item
|
|
116
|
-
ax_item =
|
|
128
|
+
ax_item = AxesSpecItem(
|
|
117
129
|
id=ax_id,
|
|
118
130
|
bbox=BboxRatio(
|
|
119
131
|
x0=round(bbox.x0, 4),
|
|
@@ -122,11 +134,11 @@ def save_layered_pltz_bundle(
|
|
|
122
134
|
height=round(bbox.height, 4),
|
|
123
135
|
space="panel",
|
|
124
136
|
),
|
|
125
|
-
limits=
|
|
137
|
+
limits=AxesLimits(
|
|
126
138
|
x=list(ax.get_xlim()),
|
|
127
139
|
y=list(ax.get_ylim()),
|
|
128
140
|
),
|
|
129
|
-
labels=
|
|
141
|
+
labels=AxesLabels(
|
|
130
142
|
xlabel=ax.get_xlabel() or None,
|
|
131
143
|
ylabel=ax.get_ylabel() or None,
|
|
132
144
|
title=ax.get_title() or None,
|
|
@@ -137,8 +149,8 @@ def save_layered_pltz_bundle(
|
|
|
137
149
|
# Extract traces from lines
|
|
138
150
|
for line_idx, line in enumerate(ax.get_lines()):
|
|
139
151
|
label = line.get_label()
|
|
140
|
-
if label is None or label.startswith(
|
|
141
|
-
label = f
|
|
152
|
+
if label is None or label.startswith("_"):
|
|
153
|
+
label = f"series_{line_idx}"
|
|
142
154
|
|
|
143
155
|
trace_id = f"{ax_id}-line-{line_idx}"
|
|
144
156
|
xdata, ydata = line.get_data()
|
|
@@ -149,7 +161,7 @@ def save_layered_pltz_bundle(
|
|
|
149
161
|
extracted_data[x_col] = np.array(xdata)
|
|
150
162
|
extracted_data[y_col] = np.array(ydata)
|
|
151
163
|
|
|
152
|
-
trace =
|
|
164
|
+
trace = TraceSpec(
|
|
153
165
|
id=trace_id,
|
|
154
166
|
type="line",
|
|
155
167
|
axes_index=ax_idx,
|
|
@@ -165,13 +177,16 @@ def save_layered_pltz_bundle(
|
|
|
165
177
|
if extracted_data:
|
|
166
178
|
# Use extracted data from matplotlib artists (captures axhline, etc.)
|
|
167
179
|
import pandas as pd
|
|
180
|
+
|
|
168
181
|
max_len = max(len(v) for v in extracted_data.values())
|
|
169
182
|
padded = {}
|
|
170
183
|
for k, v in extracted_data.items():
|
|
171
184
|
# Convert to float for NaN padding compatibility
|
|
172
185
|
v_float = np.array(v, dtype=float)
|
|
173
186
|
if len(v_float) < max_len:
|
|
174
|
-
padded[k] = np.pad(
|
|
187
|
+
padded[k] = np.pad(
|
|
188
|
+
v_float, (0, max_len - len(v_float)), constant_values=np.nan
|
|
189
|
+
)
|
|
175
190
|
else:
|
|
176
191
|
padded[k] = v_float
|
|
177
192
|
csv_df = pd.DataFrame(padded)
|
|
@@ -185,9 +200,9 @@ def save_layered_pltz_bundle(
|
|
|
185
200
|
csv_hash = f"sha256:{hashlib.sha256(csv_str.encode()).hexdigest()[:16]}"
|
|
186
201
|
|
|
187
202
|
# Create spec
|
|
188
|
-
spec =
|
|
203
|
+
spec = PlotSpec(
|
|
189
204
|
plot_id=basename,
|
|
190
|
-
data=
|
|
205
|
+
data=DataSourceSpec(
|
|
191
206
|
csv=f"{basename}.csv",
|
|
192
207
|
format="wide",
|
|
193
208
|
hash=csv_hash,
|
|
@@ -196,33 +211,36 @@ def save_layered_pltz_bundle(
|
|
|
196
211
|
traces=traces,
|
|
197
212
|
)
|
|
198
213
|
|
|
199
|
-
# === Build
|
|
214
|
+
# === Build PlotStyle (appearance) ===
|
|
200
215
|
# Detect theme from figure
|
|
201
216
|
theme_mode = "light"
|
|
202
|
-
if hasattr(fig,
|
|
217
|
+
if hasattr(fig, "_scitex_theme"):
|
|
203
218
|
theme_mode = fig._scitex_theme
|
|
204
219
|
|
|
205
220
|
trace_styles = []
|
|
206
221
|
for ax_idx, ax in enumerate(fig.axes):
|
|
207
222
|
for line_idx, line in enumerate(ax.get_lines()):
|
|
208
223
|
label = line.get_label()
|
|
209
|
-
if label and not label.startswith(
|
|
224
|
+
if label and not label.startswith("_"):
|
|
210
225
|
# Get line color
|
|
211
226
|
import matplotlib.colors as mcolors
|
|
227
|
+
|
|
212
228
|
color = line.get_color()
|
|
213
229
|
if isinstance(color, (list, tuple)):
|
|
214
230
|
color = mcolors.to_hex(color)
|
|
215
231
|
|
|
216
232
|
trace_id = f"ax{ax_idx}-line-{line_idx}"
|
|
217
|
-
trace_styles.append(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
233
|
+
trace_styles.append(
|
|
234
|
+
TraceStyleSpec(
|
|
235
|
+
trace_id=trace_id,
|
|
236
|
+
color=color,
|
|
237
|
+
linewidth=line.get_linewidth(),
|
|
238
|
+
alpha=line.get_alpha(),
|
|
239
|
+
)
|
|
240
|
+
)
|
|
223
241
|
|
|
224
242
|
# Extract legend configuration from first axes with legend
|
|
225
|
-
legend_spec =
|
|
243
|
+
legend_spec = LegendSpec(visible=True, location="best")
|
|
226
244
|
for ax in fig.axes:
|
|
227
245
|
legend = ax.get_legend()
|
|
228
246
|
if legend is not None:
|
|
@@ -230,9 +248,17 @@ def save_layered_pltz_bundle(
|
|
|
230
248
|
# matplotlib legend._loc can be int or string
|
|
231
249
|
loc = legend._loc
|
|
232
250
|
loc_map = {
|
|
233
|
-
0: "best",
|
|
234
|
-
|
|
235
|
-
|
|
251
|
+
0: "best",
|
|
252
|
+
1: "upper right",
|
|
253
|
+
2: "upper left",
|
|
254
|
+
3: "lower left",
|
|
255
|
+
4: "lower right",
|
|
256
|
+
5: "right",
|
|
257
|
+
6: "center left",
|
|
258
|
+
7: "center right",
|
|
259
|
+
8: "lower center",
|
|
260
|
+
9: "upper center",
|
|
261
|
+
10: "center",
|
|
236
262
|
}
|
|
237
263
|
if isinstance(loc, int):
|
|
238
264
|
location = loc_map.get(loc, "best")
|
|
@@ -269,18 +295,18 @@ def save_layered_pltz_bundle(
|
|
|
269
295
|
pass # Keep "best" if we can't determine
|
|
270
296
|
|
|
271
297
|
# Extract other legend properties
|
|
272
|
-
legend_spec =
|
|
298
|
+
legend_spec = LegendSpec(
|
|
273
299
|
visible=legend.get_visible(),
|
|
274
300
|
location=location,
|
|
275
301
|
frameon=legend.get_frame_on(),
|
|
276
|
-
fontsize=legend._fontsize if hasattr(legend,
|
|
277
|
-
ncols=legend._ncols if hasattr(legend,
|
|
302
|
+
fontsize=legend._fontsize if hasattr(legend, "_fontsize") else None,
|
|
303
|
+
ncols=legend._ncols if hasattr(legend, "_ncols") else 1,
|
|
278
304
|
title=legend.get_title().get_text() if legend.get_title() else None,
|
|
279
305
|
)
|
|
280
306
|
break # Use first legend found
|
|
281
307
|
|
|
282
|
-
style =
|
|
283
|
-
theme=
|
|
308
|
+
style = PlotStyle(
|
|
309
|
+
theme=ThemeSpec(
|
|
284
310
|
mode=theme_mode,
|
|
285
311
|
colors={
|
|
286
312
|
"background": "transparent",
|
|
@@ -290,11 +316,11 @@ def save_layered_pltz_bundle(
|
|
|
290
316
|
"tick": "black" if theme_mode == "light" else "#e8e8e8",
|
|
291
317
|
},
|
|
292
318
|
),
|
|
293
|
-
size=
|
|
319
|
+
size=SizeSpec(
|
|
294
320
|
width_mm=round(fig_width_inch * 25.4, 1),
|
|
295
321
|
height_mm=round(fig_height_inch * 25.4, 1),
|
|
296
322
|
),
|
|
297
|
-
font=
|
|
323
|
+
font=FontSpec(family="sans-serif", size_pt=8.0),
|
|
298
324
|
traces=trace_styles,
|
|
299
325
|
legend=legend_spec,
|
|
300
326
|
)
|
|
@@ -353,22 +379,25 @@ def save_layered_pltz_bundle(
|
|
|
353
379
|
tmp_path = Path(tmp_dir)
|
|
354
380
|
|
|
355
381
|
with warnings.catch_warnings():
|
|
356
|
-
warnings.filterwarnings(
|
|
382
|
+
warnings.filterwarnings("ignore", message=".*tight_layout.*")
|
|
357
383
|
|
|
358
384
|
# Save PNG at full figure size (crop function will handle tight cropping)
|
|
359
385
|
# This ensures coordinate transformations are accurate
|
|
360
386
|
png_path = exports_dir / f"{basename}.png"
|
|
361
|
-
fig.savefig(png_path, dpi=dpi, format=
|
|
387
|
+
fig.savefig(png_path, dpi=dpi, format="png", transparent=True)
|
|
362
388
|
|
|
363
389
|
# Save SVG with tight bbox (separate concern, no coord issues)
|
|
364
390
|
svg_path = exports_dir / f"{basename}.svg"
|
|
365
|
-
fig.savefig(svg_path, bbox_inches=
|
|
391
|
+
fig.savefig(svg_path, bbox_inches="tight", format="svg")
|
|
366
392
|
|
|
367
393
|
# Generate hitmap
|
|
368
394
|
from scitex.plt.utils._hitmap import (
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
395
|
+
HITMAP_AXES_COLOR,
|
|
396
|
+
HITMAP_BACKGROUND_COLOR,
|
|
397
|
+
apply_hitmap_colors,
|
|
398
|
+
extract_path_data,
|
|
399
|
+
extract_selectable_regions,
|
|
400
|
+
restore_original_colors,
|
|
372
401
|
)
|
|
373
402
|
|
|
374
403
|
original_props, color_map, groups = apply_hitmap_colors(fig)
|
|
@@ -386,8 +415,9 @@ def save_layered_pltz_bundle(
|
|
|
386
415
|
|
|
387
416
|
# Save hitmap at full figure size (will crop with same box as main PNG)
|
|
388
417
|
hitmap_path = exports_dir / f"{basename}_hitmap.png"
|
|
389
|
-
fig.savefig(
|
|
390
|
-
|
|
418
|
+
fig.savefig(
|
|
419
|
+
hitmap_path, dpi=dpi, format="png", facecolor=HITMAP_BACKGROUND_COLOR
|
|
420
|
+
)
|
|
391
421
|
|
|
392
422
|
# Restore colors
|
|
393
423
|
restore_original_colors(original_props)
|
|
@@ -401,17 +431,26 @@ def save_layered_pltz_bundle(
|
|
|
401
431
|
from scitex.plt.utils._crop import crop
|
|
402
432
|
|
|
403
433
|
_, margin_crop_box = crop(
|
|
404
|
-
str(png_path),
|
|
405
|
-
|
|
434
|
+
str(png_path),
|
|
435
|
+
output_path=str(png_path),
|
|
436
|
+
overwrite=True,
|
|
437
|
+
margin=12,
|
|
438
|
+
verbose=False,
|
|
439
|
+
return_offset=True,
|
|
406
440
|
)
|
|
407
441
|
|
|
408
|
-
crop(
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
442
|
+
crop(
|
|
443
|
+
str(hitmap_path),
|
|
444
|
+
output_path=str(hitmap_path),
|
|
445
|
+
overwrite=True,
|
|
446
|
+
crop_box=(
|
|
447
|
+
margin_crop_box["left"],
|
|
448
|
+
margin_crop_box["upper"],
|
|
449
|
+
margin_crop_box["right"],
|
|
450
|
+
margin_crop_box["lower"],
|
|
451
|
+
),
|
|
452
|
+
verbose=False,
|
|
453
|
+
)
|
|
415
454
|
except Exception as e:
|
|
416
455
|
logger.debug(f"Crop failed: {e}")
|
|
417
456
|
|
|
@@ -432,7 +471,7 @@ def save_layered_pltz_bundle(
|
|
|
432
471
|
total_offset_left = margin_crop_box["left"] if margin_crop_box else 0
|
|
433
472
|
total_offset_upper = margin_crop_box["upper"] if margin_crop_box else 0
|
|
434
473
|
|
|
435
|
-
# === Build
|
|
474
|
+
# === Build PlotGeometry (cache) ===
|
|
436
475
|
# Extract at export DPI so coords are in full figure space (matches saved PNG before crop)
|
|
437
476
|
path_data = extract_path_data(fig)
|
|
438
477
|
selectable_regions = extract_selectable_regions(fig)
|
|
@@ -456,35 +495,51 @@ def save_layered_pltz_bundle(
|
|
|
456
495
|
rendered_axes = []
|
|
457
496
|
for ax_idx, ax_data in enumerate(path_data.get("axes", [])):
|
|
458
497
|
bbox_data = ax_data.get("bbox_px", {})
|
|
459
|
-
rendered_axes.append(
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
498
|
+
rendered_axes.append(
|
|
499
|
+
RenderedAxes(
|
|
500
|
+
id=f"ax{ax_idx}",
|
|
501
|
+
xlim=ax_data.get("xlim", [0, 1]),
|
|
502
|
+
ylim=ax_data.get("ylim", [0, 1]),
|
|
503
|
+
bbox_px=BboxPx(
|
|
504
|
+
x0=bbox_data.get("x0", 0),
|
|
505
|
+
y0=bbox_data.get("y0", 0),
|
|
506
|
+
width=bbox_data.get(
|
|
507
|
+
"width", bbox_data.get("x1", 0) - bbox_data.get("x0", 0)
|
|
508
|
+
),
|
|
509
|
+
height=bbox_data.get(
|
|
510
|
+
"height", bbox_data.get("y1", 0) - bbox_data.get("y0", 0)
|
|
511
|
+
),
|
|
512
|
+
),
|
|
513
|
+
)
|
|
514
|
+
)
|
|
470
515
|
|
|
471
516
|
rendered_artists = []
|
|
472
517
|
for artist in path_data.get("artists", []):
|
|
473
518
|
bbox_data = artist.get("bbox_px", {})
|
|
474
|
-
rendered_artists.append(
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
519
|
+
rendered_artists.append(
|
|
520
|
+
RenderedArtist(
|
|
521
|
+
id=str(artist.get("id", "")),
|
|
522
|
+
type=artist.get("type", "unknown"),
|
|
523
|
+
axes_index=artist.get("axes_index", 0),
|
|
524
|
+
bbox_px=(
|
|
525
|
+
BboxPx(
|
|
526
|
+
x0=bbox_data.get("x0", 0),
|
|
527
|
+
y0=bbox_data.get("y0", 0),
|
|
528
|
+
width=bbox_data.get(
|
|
529
|
+
"width", bbox_data.get("x1", 0) - bbox_data.get("x0", 0)
|
|
530
|
+
),
|
|
531
|
+
height=bbox_data.get(
|
|
532
|
+
"height", bbox_data.get("y1", 0) - bbox_data.get("y0", 0)
|
|
533
|
+
),
|
|
534
|
+
)
|
|
535
|
+
if bbox_data
|
|
536
|
+
else None
|
|
537
|
+
),
|
|
538
|
+
path_px=artist.get("path_px"),
|
|
539
|
+
)
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
geometry = PlotGeometry(
|
|
488
543
|
source_hash=csv_hash or "",
|
|
489
544
|
figure_px=final_image_size_px, # Final cropped image size
|
|
490
545
|
dpi=dpi, # Export DPI (stored for consumers)
|
|
@@ -497,7 +552,7 @@ def save_layered_pltz_bundle(
|
|
|
497
552
|
"groups": groups,
|
|
498
553
|
# Store DPI info for consumers that need to retrieve from data
|
|
499
554
|
"fig_dpi": fig_dpi, # Original matplotlib fig.dpi
|
|
500
|
-
"export_dpi": dpi,
|
|
555
|
+
"export_dpi": dpi, # Export DPI used for PNG
|
|
501
556
|
"dpi_scale": dpi_scale, # export_dpi / fig_dpi
|
|
502
557
|
},
|
|
503
558
|
selectable_regions=selectable_regions,
|
|
@@ -510,34 +565,55 @@ def save_layered_pltz_bundle(
|
|
|
510
565
|
# spec.json
|
|
511
566
|
spec_path = bundle_dir / "spec.json"
|
|
512
567
|
with open(spec_path, "w") as f:
|
|
513
|
-
json.dump(
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
568
|
+
json.dump(
|
|
569
|
+
{
|
|
570
|
+
"schema": {"name": "scitex.plt.spec", "version": PLOT_SPEC_VERSION},
|
|
571
|
+
**asdict(spec),
|
|
572
|
+
},
|
|
573
|
+
f,
|
|
574
|
+
indent=2,
|
|
575
|
+
default=str,
|
|
576
|
+
)
|
|
517
577
|
|
|
518
578
|
# style.json
|
|
519
579
|
style_path = bundle_dir / "style.json"
|
|
520
580
|
with open(style_path, "w") as f:
|
|
521
|
-
json.dump(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
581
|
+
json.dump(
|
|
582
|
+
{
|
|
583
|
+
"schema": {"name": "scitex.plt.style", "version": PLOT_STYLE_VERSION},
|
|
584
|
+
**asdict(style),
|
|
585
|
+
},
|
|
586
|
+
f,
|
|
587
|
+
indent=2,
|
|
588
|
+
default=str,
|
|
589
|
+
)
|
|
525
590
|
|
|
526
591
|
# cache/geometry_px.json
|
|
527
592
|
geometry_path = cache_dir / "geometry_px.json"
|
|
528
593
|
with open(geometry_path, "w") as f:
|
|
529
|
-
json.dump(
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
594
|
+
json.dump(
|
|
595
|
+
{
|
|
596
|
+
"schema": {
|
|
597
|
+
"name": "scitex.plt.geometry",
|
|
598
|
+
"version": PLOT_GEOMETRY_VERSION,
|
|
599
|
+
},
|
|
600
|
+
"_comment": "CACHE - can be deleted and regenerated from spec + style",
|
|
601
|
+
**asdict(geometry),
|
|
602
|
+
},
|
|
603
|
+
f,
|
|
604
|
+
indent=2,
|
|
605
|
+
default=str,
|
|
606
|
+
)
|
|
534
607
|
|
|
535
608
|
# cache/render_manifest.json
|
|
536
609
|
spec_hash = hashlib.sha256(open(spec_path, "rb").read()).hexdigest()[:16]
|
|
537
610
|
style_hash = hashlib.sha256(open(style_path, "rb").read()).hexdigest()[:16]
|
|
538
|
-
manifest =
|
|
611
|
+
manifest = RenderManifest(
|
|
539
612
|
source_hash=f"{spec_hash}:{style_hash}",
|
|
540
|
-
panel_size_mm=[
|
|
613
|
+
panel_size_mm=[
|
|
614
|
+
round(fig_width_inch * 25.4, 1),
|
|
615
|
+
round(fig_height_inch * 25.4, 1),
|
|
616
|
+
],
|
|
541
617
|
dpi=dpi,
|
|
542
618
|
render_px=final_image_size_px,
|
|
543
619
|
overview_png=f"exports/{basename}.png",
|
|
@@ -546,10 +622,18 @@ def save_layered_pltz_bundle(
|
|
|
546
622
|
)
|
|
547
623
|
manifest_path = cache_dir / "render_manifest.json"
|
|
548
624
|
with open(manifest_path, "w") as f:
|
|
549
|
-
json.dump(
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
625
|
+
json.dump(
|
|
626
|
+
{
|
|
627
|
+
"schema": {
|
|
628
|
+
"name": "scitex.plt.render_manifest",
|
|
629
|
+
"version": PLOT_GEOMETRY_VERSION,
|
|
630
|
+
},
|
|
631
|
+
**asdict(manifest),
|
|
632
|
+
},
|
|
633
|
+
f,
|
|
634
|
+
indent=2,
|
|
635
|
+
default=str,
|
|
636
|
+
)
|
|
553
637
|
|
|
554
638
|
# Save CSV
|
|
555
639
|
if csv_df is not None:
|
|
@@ -557,26 +641,26 @@ def save_layered_pltz_bundle(
|
|
|
557
641
|
csv_df.to_csv(csv_path, index=False)
|
|
558
642
|
|
|
559
643
|
# Generate overview showing main image and hitmap side by side
|
|
560
|
-
|
|
644
|
+
_generate_plot_overview(exports_dir, basename)
|
|
561
645
|
|
|
562
646
|
# Generate dynamic README.md
|
|
563
|
-
|
|
647
|
+
_generate_plot_readme(bundle_dir, basename, spec, style, geometry, manifest)
|
|
564
648
|
|
|
565
|
-
logger.debug(f"Saved layered
|
|
649
|
+
logger.debug(f"Saved layered plot bundle: {bundle_dir}")
|
|
566
650
|
|
|
567
651
|
|
|
568
|
-
def
|
|
652
|
+
def _generate_plot_overview(exports_dir: Path, basename: str) -> None:
|
|
569
653
|
"""Generate comprehensive overview with plot, hitmap, overlay, bboxes, and JSON info.
|
|
570
654
|
|
|
571
655
|
Args:
|
|
572
656
|
exports_dir: Path to exports directory.
|
|
573
657
|
basename: Base filename for the bundle.
|
|
574
658
|
"""
|
|
575
|
-
import matplotlib.pyplot as plt
|
|
576
|
-
import matplotlib.patches as patches
|
|
577
|
-
from PIL import Image
|
|
578
659
|
import warnings
|
|
660
|
+
|
|
661
|
+
import matplotlib.pyplot as plt
|
|
579
662
|
import numpy as np
|
|
663
|
+
from PIL import Image
|
|
580
664
|
|
|
581
665
|
bundle_dir = exports_dir.parent
|
|
582
666
|
png_path = exports_dir / f"{basename}.png"
|
|
@@ -602,16 +686,16 @@ def _generate_pltz_overview(exports_dir: Path, basename: str) -> None:
|
|
|
602
686
|
manifest_path = bundle_dir / "cache" / "render_manifest.json"
|
|
603
687
|
|
|
604
688
|
if spec_path.exists():
|
|
605
|
-
with open(spec_path
|
|
689
|
+
with open(spec_path) as f:
|
|
606
690
|
spec_data = json.load(f)
|
|
607
691
|
if style_path.exists():
|
|
608
|
-
with open(style_path
|
|
692
|
+
with open(style_path) as f:
|
|
609
693
|
style_data = json.load(f)
|
|
610
694
|
if geometry_path.exists():
|
|
611
|
-
with open(geometry_path
|
|
695
|
+
with open(geometry_path) as f:
|
|
612
696
|
geometry_data = json.load(f)
|
|
613
697
|
if manifest_path.exists():
|
|
614
|
-
with open(manifest_path
|
|
698
|
+
with open(manifest_path) as f:
|
|
615
699
|
manifest_data = json.load(f)
|
|
616
700
|
|
|
617
701
|
# Get DPI and panel size for mm scaler
|
|
@@ -659,12 +743,28 @@ def _generate_pltz_overview(exports_dir: Path, basename: str) -> None:
|
|
|
659
743
|
if color_map_id in color_map:
|
|
660
744
|
label = color_map[color_map_id].get("label", label)
|
|
661
745
|
|
|
662
|
-
ax_hitmap.text(
|
|
663
|
-
|
|
664
|
-
|
|
746
|
+
ax_hitmap.text(
|
|
747
|
+
cx,
|
|
748
|
+
cy,
|
|
749
|
+
label,
|
|
750
|
+
fontsize=8,
|
|
751
|
+
ha="center",
|
|
752
|
+
va="center",
|
|
753
|
+
color="white",
|
|
754
|
+
fontweight="bold",
|
|
755
|
+
bbox=dict(
|
|
756
|
+
boxstyle="round,pad=0.2", facecolor="black", alpha=0.7
|
|
757
|
+
),
|
|
758
|
+
)
|
|
665
759
|
else:
|
|
666
|
-
ax_hitmap.text(
|
|
667
|
-
|
|
760
|
+
ax_hitmap.text(
|
|
761
|
+
0.5,
|
|
762
|
+
0.5,
|
|
763
|
+
"No hitmap",
|
|
764
|
+
ha="center",
|
|
765
|
+
va="center",
|
|
766
|
+
transform=ax_hitmap.transAxes,
|
|
767
|
+
)
|
|
668
768
|
ax_hitmap.axis("off")
|
|
669
769
|
|
|
670
770
|
# 3. Overlay (plot + hitmap with transparency)
|
|
@@ -686,7 +786,7 @@ def _generate_pltz_overview(exports_dir: Path, basename: str) -> None:
|
|
|
686
786
|
ax_bboxes.imshow(main_img)
|
|
687
787
|
|
|
688
788
|
# Note: bbox_px coordinates are already in final image space
|
|
689
|
-
# (adjusted during
|
|
789
|
+
# (adjusted during save_layered_plot_bundle), so no offset needed
|
|
690
790
|
|
|
691
791
|
# Draw bboxes from geometry
|
|
692
792
|
colors = ["red", "blue", "green", "orange", "purple", "cyan"]
|
|
@@ -740,12 +840,18 @@ def _generate_pltz_overview(exports_dir: Path, basename: str) -> None:
|
|
|
740
840
|
|
|
741
841
|
# Format JSON summary with limited depth
|
|
742
842
|
json_text = _format_json_summary(
|
|
743
|
-
{"spec": spec_data, "style": style_data},
|
|
744
|
-
|
|
843
|
+
{"spec": spec_data, "style": style_data}, max_depth=2
|
|
844
|
+
)
|
|
845
|
+
ax_json.text(
|
|
846
|
+
0.02,
|
|
847
|
+
0.98,
|
|
848
|
+
json_text,
|
|
849
|
+
transform=ax_json.transAxes,
|
|
850
|
+
fontsize=7,
|
|
851
|
+
fontfamily="monospace",
|
|
852
|
+
verticalalignment="top",
|
|
853
|
+
bbox=dict(boxstyle="round", facecolor="wheat", alpha=0.5),
|
|
745
854
|
)
|
|
746
|
-
ax_json.text(0.02, 0.98, json_text, transform=ax_json.transAxes,
|
|
747
|
-
fontsize=7, fontfamily="monospace", verticalalignment="top",
|
|
748
|
-
bbox=dict(boxstyle="round", facecolor="wheat", alpha=0.5))
|
|
749
855
|
|
|
750
856
|
# 6. mm Scaler
|
|
751
857
|
ax_scale = fig.add_subplot(gs[1, 2])
|
|
@@ -756,11 +862,11 @@ def _generate_pltz_overview(exports_dir: Path, basename: str) -> None:
|
|
|
756
862
|
|
|
757
863
|
# Add grid lines every 10mm
|
|
758
864
|
for x in range(0, int(panel_size_mm[0]) + 1, 10):
|
|
759
|
-
ax_scale.axvline(x, color=
|
|
865
|
+
ax_scale.axvline(x, color="gray", linewidth=0.5, alpha=0.5)
|
|
760
866
|
if x > 0:
|
|
761
867
|
ax_scale.text(x, -1, f"{x}", ha="center", fontsize=7)
|
|
762
868
|
for y in range(0, int(panel_size_mm[1]) + 1, 10):
|
|
763
|
-
ax_scale.axhline(y, color=
|
|
869
|
+
ax_scale.axhline(y, color="gray", linewidth=0.5, alpha=0.5)
|
|
764
870
|
if y > 0:
|
|
765
871
|
ax_scale.text(-1, y, f"{y}", ha="right", va="center", fontsize=7)
|
|
766
872
|
|
|
@@ -771,31 +877,43 @@ def _generate_pltz_overview(exports_dir: Path, basename: str) -> None:
|
|
|
771
877
|
|
|
772
878
|
# Add size text
|
|
773
879
|
size_text = f"Panel: {panel_size_mm[0]:.1f} × {panel_size_mm[1]:.1f} mm\nDPI: {dpi}\nPixels: {img_width} × {img_height}"
|
|
774
|
-
ax_scale.text(
|
|
775
|
-
|
|
776
|
-
|
|
880
|
+
ax_scale.text(
|
|
881
|
+
panel_size_mm[0] * 0.95,
|
|
882
|
+
panel_size_mm[1] * 0.95,
|
|
883
|
+
size_text,
|
|
884
|
+
ha="right",
|
|
885
|
+
va="bottom",
|
|
886
|
+
fontsize=8,
|
|
887
|
+
bbox=dict(boxstyle="round", facecolor="white", alpha=0.8),
|
|
888
|
+
)
|
|
777
889
|
|
|
778
890
|
fig.suptitle(f"Overview: {basename}", fontsize=14, fontweight="bold", y=0.98)
|
|
779
891
|
|
|
780
892
|
overview_path = exports_dir / f"{basename}_overview.png"
|
|
781
893
|
with warnings.catch_warnings():
|
|
782
894
|
warnings.filterwarnings("ignore", message=".*tight_layout.*")
|
|
783
|
-
fig.savefig(
|
|
895
|
+
fig.savefig(
|
|
896
|
+
overview_path,
|
|
897
|
+
dpi=get_preview_dpi(),
|
|
898
|
+
bbox_inches="tight",
|
|
899
|
+
facecolor="white",
|
|
900
|
+
)
|
|
784
901
|
plt.close(fig)
|
|
785
902
|
|
|
786
903
|
except Exception as e:
|
|
787
|
-
logger.debug(f"Could not generate
|
|
904
|
+
logger.debug(f"Could not generate plot overview: {e}")
|
|
788
905
|
import traceback
|
|
906
|
+
|
|
789
907
|
logger.debug(traceback.format_exc())
|
|
790
908
|
|
|
791
909
|
|
|
792
|
-
def
|
|
910
|
+
def _generate_plot_readme(
|
|
793
911
|
bundle_dir: Path,
|
|
794
912
|
basename: str,
|
|
795
|
-
spec: "
|
|
796
|
-
style: "
|
|
797
|
-
geometry: "
|
|
798
|
-
manifest: "
|
|
913
|
+
spec: "PlotSpec",
|
|
914
|
+
style: "PlotStyle",
|
|
915
|
+
geometry: "PlotGeometry",
|
|
916
|
+
manifest: "RenderManifest",
|
|
799
917
|
) -> None:
|
|
800
918
|
"""Generate a dynamic README.md describing the bundle.
|
|
801
919
|
|
|
@@ -805,13 +923,13 @@ def _generate_pltz_readme(
|
|
|
805
923
|
Path to the bundle directory.
|
|
806
924
|
basename : str
|
|
807
925
|
Base filename for the bundle.
|
|
808
|
-
spec :
|
|
926
|
+
spec : PlotSpec
|
|
809
927
|
The plot specification.
|
|
810
|
-
style :
|
|
928
|
+
style : PlotStyle
|
|
811
929
|
The plot style.
|
|
812
|
-
geometry :
|
|
930
|
+
geometry : PlotGeometry
|
|
813
931
|
The rendered geometry.
|
|
814
|
-
manifest :
|
|
932
|
+
manifest : RenderManifest
|
|
815
933
|
The render manifest.
|
|
816
934
|
"""
|
|
817
935
|
from datetime import datetime
|
|
@@ -826,7 +944,7 @@ def _generate_pltz_readme(
|
|
|
826
944
|
dpi = manifest.dpi
|
|
827
945
|
render_px = manifest.render_px
|
|
828
946
|
|
|
829
|
-
readme_content = f"""# {basename}.
|
|
947
|
+
readme_content = f"""# {basename}.plot
|
|
830
948
|
|
|
831
949
|
> SciTeX Layered Plot Bundle - Auto-generated README
|
|
832
950
|
|
|
@@ -837,7 +955,7 @@ def _generate_pltz_readme(
|
|
|
837
955
|
## Bundle Structure
|
|
838
956
|
|
|
839
957
|
```
|
|
840
|
-
{basename}.
|
|
958
|
+
{basename}.plot/
|
|
841
959
|
├── spec.json # WHAT to plot (semantic, editable)
|
|
842
960
|
├── style.json # HOW it looks (appearance, editable)
|
|
843
961
|
├── {basename}.csv # Raw data (immutable)
|
|
@@ -862,7 +980,7 @@ def _generate_pltz_readme(
|
|
|
862
980
|
| Size | {width_mm:.1f} × {height_mm:.1f} mm |
|
|
863
981
|
| DPI | {dpi} |
|
|
864
982
|
| Pixels | {render_px[0]} × {render_px[1]} |
|
|
865
|
-
| Theme | {style.theme.mode if style.theme else
|
|
983
|
+
| Theme | {style.theme.mode if style.theme else "light"} |
|
|
866
984
|
|
|
867
985
|
## Coordinate System
|
|
868
986
|
|
|
@@ -893,7 +1011,7 @@ Original Figure (at export DPI)
|
|
|
893
1011
|
import scitex as stx
|
|
894
1012
|
|
|
895
1013
|
# Load the bundle
|
|
896
|
-
bundle = stx.plt.io.
|
|
1014
|
+
bundle = stx.plt.io.load_layered_plot_bundle("{bundle_dir}")
|
|
897
1015
|
|
|
898
1016
|
# Access components
|
|
899
1017
|
spec = bundle["spec"] # What to plot
|
|
@@ -920,7 +1038,7 @@ stx.plt.io.regenerate_cache("{bundle_dir}")
|
|
|
920
1038
|
|
|
921
1039
|
---
|
|
922
1040
|
|
|
923
|
-
*Generated: {datetime.now().strftime(
|
|
1041
|
+
*Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}*
|
|
924
1042
|
*Schema: scitex.plt v{PLOT_SPEC_VERSION}*
|
|
925
1043
|
"""
|
|
926
1044
|
|
|
@@ -953,6 +1071,7 @@ def _adjust_coords_for_offset(
|
|
|
953
1071
|
selectable_regions with adjusted coordinates.
|
|
954
1072
|
"""
|
|
955
1073
|
import copy
|
|
1074
|
+
|
|
956
1075
|
result = copy.deepcopy(selectable_regions)
|
|
957
1076
|
|
|
958
1077
|
def adjust_bbox(bbox: List[float]) -> List[float]:
|
|
@@ -1030,6 +1149,7 @@ def _adjust_path_data_for_offset(
|
|
|
1030
1149
|
path_data with adjusted coordinates.
|
|
1031
1150
|
"""
|
|
1032
1151
|
import copy
|
|
1152
|
+
|
|
1033
1153
|
result = copy.deepcopy(path_data)
|
|
1034
1154
|
|
|
1035
1155
|
# Adjust axes bbox_px
|
|
@@ -1089,6 +1209,7 @@ def _adjust_path_data_for_crop(
|
|
|
1089
1209
|
path_data with adjusted coordinates.
|
|
1090
1210
|
"""
|
|
1091
1211
|
import copy
|
|
1212
|
+
|
|
1092
1213
|
result = copy.deepcopy(path_data)
|
|
1093
1214
|
|
|
1094
1215
|
# Adjust axes bbox_px
|
|
@@ -1134,12 +1255,22 @@ def _draw_bbox(ax, bbox: List, color: str, label: str, lw: float = 2) -> None:
|
|
|
1134
1255
|
x0, y0, x1, y1 = bbox
|
|
1135
1256
|
width = x1 - x0
|
|
1136
1257
|
height = y1 - y0
|
|
1137
|
-
rect = patches.Rectangle(
|
|
1138
|
-
|
|
1258
|
+
rect = patches.Rectangle(
|
|
1259
|
+
(x0, y0), width, height, linewidth=lw, edgecolor=color, facecolor="none"
|
|
1260
|
+
)
|
|
1139
1261
|
ax.add_patch(rect)
|
|
1140
1262
|
# Place label at top-left corner inside the box with background
|
|
1141
|
-
ax.text(
|
|
1142
|
-
|
|
1263
|
+
ax.text(
|
|
1264
|
+
x0 + 2,
|
|
1265
|
+
y0 + 2,
|
|
1266
|
+
label,
|
|
1267
|
+
fontsize=6,
|
|
1268
|
+
color="white",
|
|
1269
|
+
va="top",
|
|
1270
|
+
ha="left",
|
|
1271
|
+
fontweight="bold",
|
|
1272
|
+
bbox=dict(boxstyle="round,pad=0.1", facecolor=color, alpha=0.8),
|
|
1273
|
+
)
|
|
1143
1274
|
|
|
1144
1275
|
|
|
1145
1276
|
def _format_json_summary(data: Dict, max_depth: int = 2, current_depth: int = 0) -> str:
|
|
@@ -1184,14 +1315,14 @@ def _format_json_summary(data: Dict, max_depth: int = 2, current_depth: int = 0)
|
|
|
1184
1315
|
return "\n".join(lines[:40]) # Limit total lines
|
|
1185
1316
|
|
|
1186
1317
|
|
|
1187
|
-
def
|
|
1318
|
+
def load_layered_plot_bundle(bundle_dir: Path) -> Dict[str, Any]:
|
|
1188
1319
|
"""
|
|
1189
|
-
Load layered .
|
|
1320
|
+
Load layered .plot bundle and return merged spec for editor.
|
|
1190
1321
|
|
|
1191
1322
|
Parameters
|
|
1192
1323
|
----------
|
|
1193
1324
|
bundle_dir : Path
|
|
1194
|
-
Path to .
|
|
1325
|
+
Path to .plot bundle.
|
|
1195
1326
|
|
|
1196
1327
|
Returns
|
|
1197
1328
|
-------
|
|
@@ -1211,20 +1342,20 @@ def load_layered_pltz_bundle(bundle_dir: Path) -> Dict[str, Any]:
|
|
|
1211
1342
|
# Load spec.json
|
|
1212
1343
|
spec_path = bundle_dir / "spec.json"
|
|
1213
1344
|
if spec_path.exists():
|
|
1214
|
-
with open(spec_path
|
|
1345
|
+
with open(spec_path) as f:
|
|
1215
1346
|
result["spec"] = json.load(f)
|
|
1216
1347
|
result["basename"] = result["spec"].get("plot_id", "plot")
|
|
1217
1348
|
|
|
1218
1349
|
# Load style.json
|
|
1219
1350
|
style_path = bundle_dir / "style.json"
|
|
1220
1351
|
if style_path.exists():
|
|
1221
|
-
with open(style_path
|
|
1352
|
+
with open(style_path) as f:
|
|
1222
1353
|
result["style"] = json.load(f)
|
|
1223
1354
|
|
|
1224
1355
|
# Load geometry from cache
|
|
1225
1356
|
geometry_path = bundle_dir / "cache" / "geometry_px.json"
|
|
1226
1357
|
if geometry_path.exists():
|
|
1227
|
-
with open(geometry_path
|
|
1358
|
+
with open(geometry_path) as f:
|
|
1228
1359
|
result["geometry"] = json.load(f)
|
|
1229
1360
|
|
|
1230
1361
|
# Create merged view for backward compatibility with editor
|
|
@@ -1266,7 +1397,11 @@ def merge_layered_bundle(
|
|
|
1266
1397
|
merged["size"] = {
|
|
1267
1398
|
"width_mm": style["size"].get("width_mm", 80),
|
|
1268
1399
|
"height_mm": style["size"].get("height_mm", 68),
|
|
1269
|
-
"dpi":
|
|
1400
|
+
"dpi": (
|
|
1401
|
+
geometry.get("dpi", get_default_dpi())
|
|
1402
|
+
if geometry
|
|
1403
|
+
else get_default_dpi()
|
|
1404
|
+
),
|
|
1270
1405
|
}
|
|
1271
1406
|
|
|
1272
1407
|
# Merge axes from spec + style + geometry
|