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,538 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# File: ./src/scitex/vis/backend/render.py
|
|
3
|
-
"""Render figure models to matplotlib figures using scitex.plt."""
|
|
4
|
-
|
|
5
|
-
from typing import Any, Dict
|
|
6
|
-
|
|
7
|
-
import numpy as np
|
|
8
|
-
|
|
9
|
-
import scitex.logging as logging
|
|
10
|
-
|
|
11
|
-
from .._models import AnnotationModel, AxesModel, FigureModel, GuideModel, PlotModel
|
|
12
|
-
from ._parser import parse_figure_json
|
|
13
|
-
|
|
14
|
-
logger = logging.getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def render_figure(fig_model: FigureModel):
|
|
18
|
-
"""
|
|
19
|
-
Render FigureModel to matplotlib figure using scitex.plt.
|
|
20
|
-
|
|
21
|
-
Parameters
|
|
22
|
-
----------
|
|
23
|
-
fig_model : FigureModel
|
|
24
|
-
Figure model to render
|
|
25
|
-
|
|
26
|
-
Returns
|
|
27
|
-
-------
|
|
28
|
-
tuple
|
|
29
|
-
(fig, axes) where fig is FigWrapper and axes is list of AxisWrapper
|
|
30
|
-
|
|
31
|
-
Examples
|
|
32
|
-
--------
|
|
33
|
-
>>> fig_model = FigureModel(width_mm=180, height_mm=120)
|
|
34
|
-
>>> fig, axes = render_figure(fig_model)
|
|
35
|
-
"""
|
|
36
|
-
import scitex as stx
|
|
37
|
-
|
|
38
|
-
# Validate before rendering
|
|
39
|
-
fig_model.validate()
|
|
40
|
-
|
|
41
|
-
# Convert mm to inches for matplotlib
|
|
42
|
-
MM_TO_INCH = 1 / 25.4
|
|
43
|
-
figsize_inch = (
|
|
44
|
-
fig_model.width_mm * MM_TO_INCH,
|
|
45
|
-
fig_model.height_mm * MM_TO_INCH,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
# Create figure with mm dimensions
|
|
49
|
-
fig, axes = stx.plt.subplots(
|
|
50
|
-
nrows=fig_model.nrows,
|
|
51
|
-
ncols=fig_model.ncols,
|
|
52
|
-
figsize=figsize_inch,
|
|
53
|
-
dpi=fig_model.dpi,
|
|
54
|
-
facecolor=fig_model.facecolor,
|
|
55
|
-
edgecolor=fig_model.edgecolor,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
# Ensure axes is always a list
|
|
59
|
-
if not isinstance(axes, (list, np.ndarray)):
|
|
60
|
-
axes = [axes]
|
|
61
|
-
elif isinstance(axes, np.ndarray):
|
|
62
|
-
axes = axes.flatten().tolist()
|
|
63
|
-
|
|
64
|
-
# TODO: Apply mm-based spacing (left_mm, right_mm, etc.)
|
|
65
|
-
# Currently, matplotlib subplots_adjust uses figure coordinates (0-1), not mm
|
|
66
|
-
# Future: Pass margin_left_mm, margin_right_mm etc. to stx.plt.subplots()
|
|
67
|
-
# to leverage scitex.plt's mm-control system
|
|
68
|
-
|
|
69
|
-
# Add suptitle if specified
|
|
70
|
-
if fig_model.suptitle:
|
|
71
|
-
suptitle_kwargs = {"t": fig_model.suptitle}
|
|
72
|
-
if fig_model.suptitle_fontsize is not None:
|
|
73
|
-
suptitle_kwargs["fontsize"] = fig_model.suptitle_fontsize
|
|
74
|
-
if fig_model.suptitle_fontweight is not None:
|
|
75
|
-
suptitle_kwargs["fontweight"] = fig_model.suptitle_fontweight
|
|
76
|
-
if fig_model.suptitle_y is not None:
|
|
77
|
-
suptitle_kwargs["y"] = fig_model.suptitle_y
|
|
78
|
-
fig.suptitle(**suptitle_kwargs)
|
|
79
|
-
|
|
80
|
-
# Render each axes
|
|
81
|
-
for i, axes_config in enumerate(fig_model.axes):
|
|
82
|
-
if i < len(axes):
|
|
83
|
-
axes_model = AxesModel.from_dict(axes_config)
|
|
84
|
-
render_axes(axes[i], axes_model)
|
|
85
|
-
|
|
86
|
-
return fig, axes
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def render_axes(ax, axes_model: AxesModel):
|
|
90
|
-
"""
|
|
91
|
-
Render AxesModel onto a matplotlib axis.
|
|
92
|
-
|
|
93
|
-
Parameters
|
|
94
|
-
----------
|
|
95
|
-
ax : AxisWrapper or matplotlib Axes
|
|
96
|
-
Target axis
|
|
97
|
-
axes_model : AxesModel
|
|
98
|
-
Axes model to render
|
|
99
|
-
"""
|
|
100
|
-
# Validate
|
|
101
|
-
axes_model.validate()
|
|
102
|
-
|
|
103
|
-
# Render plots first (background layer)
|
|
104
|
-
for plot_config in axes_model.plots:
|
|
105
|
-
plot_model = PlotModel.from_dict(plot_config)
|
|
106
|
-
render_plot(ax, plot_model)
|
|
107
|
-
|
|
108
|
-
# Render guides (middle layer - reference lines/spans over plots)
|
|
109
|
-
# Note: Use zorder in guide config to control layering if needed
|
|
110
|
-
for guide_config in axes_model.guides:
|
|
111
|
-
guide_model = GuideModel.from_dict(guide_config)
|
|
112
|
-
render_guide(ax, guide_model)
|
|
113
|
-
|
|
114
|
-
# Render annotations last (foreground layer)
|
|
115
|
-
for annotation_config in axes_model.annotations:
|
|
116
|
-
annotation_model = AnnotationModel.from_dict(annotation_config)
|
|
117
|
-
render_annotation(ax, annotation_model)
|
|
118
|
-
|
|
119
|
-
# Apply axis properties
|
|
120
|
-
if axes_model.xlabel:
|
|
121
|
-
ax.set_xlabel(axes_model.xlabel)
|
|
122
|
-
|
|
123
|
-
if axes_model.ylabel:
|
|
124
|
-
ax.set_ylabel(axes_model.ylabel)
|
|
125
|
-
|
|
126
|
-
if axes_model.title:
|
|
127
|
-
ax.set_title(axes_model.title)
|
|
128
|
-
|
|
129
|
-
# Set axis limits
|
|
130
|
-
if axes_model.xlim:
|
|
131
|
-
ax.set_xlim(axes_model.xlim)
|
|
132
|
-
|
|
133
|
-
if axes_model.ylim:
|
|
134
|
-
ax.set_ylim(axes_model.ylim)
|
|
135
|
-
|
|
136
|
-
# Set axis scales
|
|
137
|
-
ax.set_xscale(axes_model.xscale)
|
|
138
|
-
ax.set_yscale(axes_model.yscale)
|
|
139
|
-
|
|
140
|
-
# Ticks
|
|
141
|
-
if axes_model.xticks is not None:
|
|
142
|
-
ax.set_xticks(axes_model.xticks)
|
|
143
|
-
|
|
144
|
-
if axes_model.yticks is not None:
|
|
145
|
-
ax.set_yticks(axes_model.yticks)
|
|
146
|
-
|
|
147
|
-
if axes_model.xticklabels is not None:
|
|
148
|
-
ax.set_xticklabels(axes_model.xticklabels)
|
|
149
|
-
|
|
150
|
-
if axes_model.yticklabels is not None:
|
|
151
|
-
ax.set_yticklabels(axes_model.yticklabels)
|
|
152
|
-
|
|
153
|
-
# Apply style properties
|
|
154
|
-
style = axes_model.style
|
|
155
|
-
|
|
156
|
-
# Grid
|
|
157
|
-
if style.grid:
|
|
158
|
-
ax.grid(
|
|
159
|
-
alpha=style.grid_alpha,
|
|
160
|
-
linestyle=style.grid_linestyle,
|
|
161
|
-
linewidth=style.grid_linewidth,
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
# Spines
|
|
165
|
-
for spine, visible in style.spines_visible.items():
|
|
166
|
-
if hasattr(ax, "spines") and spine in ax.spines:
|
|
167
|
-
ax.spines[spine].set_visible(visible)
|
|
168
|
-
|
|
169
|
-
# Tick label size
|
|
170
|
-
if style.tick_fontsize is not None:
|
|
171
|
-
ax.tick_params(labelsize=style.tick_fontsize)
|
|
172
|
-
|
|
173
|
-
# Legend
|
|
174
|
-
if style.legend:
|
|
175
|
-
legend_kwargs = {
|
|
176
|
-
"loc": style.legend_loc,
|
|
177
|
-
"frameon": style.legend_frameon,
|
|
178
|
-
}
|
|
179
|
-
if style.legend_fontsize is not None:
|
|
180
|
-
legend_kwargs["fontsize"] = style.legend_fontsize
|
|
181
|
-
ax.legend(**legend_kwargs)
|
|
182
|
-
|
|
183
|
-
# Aspect
|
|
184
|
-
if style.aspect:
|
|
185
|
-
ax.set_aspect(style.aspect)
|
|
186
|
-
|
|
187
|
-
# Background color
|
|
188
|
-
if style.facecolor:
|
|
189
|
-
ax.set_facecolor(style.facecolor)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def render_plot(ax, plot_model: PlotModel):
|
|
193
|
-
"""
|
|
194
|
-
Render PlotModel onto an axis.
|
|
195
|
-
|
|
196
|
-
Parameters
|
|
197
|
-
----------
|
|
198
|
-
ax : AxisWrapper or matplotlib Axes
|
|
199
|
-
Target axis
|
|
200
|
-
plot_model : PlotModel
|
|
201
|
-
Plot model to render
|
|
202
|
-
"""
|
|
203
|
-
# Validate
|
|
204
|
-
plot_model.validate()
|
|
205
|
-
|
|
206
|
-
data = plot_model.data
|
|
207
|
-
|
|
208
|
-
# Build kwargs from style (clean separation of concerns)
|
|
209
|
-
# Only include relevant kwargs for each plot type
|
|
210
|
-
style_dict = plot_model.style.to_dict()
|
|
211
|
-
|
|
212
|
-
# Common kwargs for all plot types
|
|
213
|
-
common_keys = {"color", "alpha", "zorder"}
|
|
214
|
-
|
|
215
|
-
# Define relevant keys for each plot type
|
|
216
|
-
plot_type_keys = {
|
|
217
|
-
"line": {"linewidth", "linestyle", "marker", "markersize"},
|
|
218
|
-
"scatter": {"marker", "markersize"},
|
|
219
|
-
"errorbar": {"linewidth", "linestyle", "marker", "xerr", "yerr", "capsize"},
|
|
220
|
-
"bar": {"width"},
|
|
221
|
-
"barh": {"width"}, # Will be renamed to height
|
|
222
|
-
"hist": {"bins", "density", "cumulative"},
|
|
223
|
-
"fill_between": {"linewidth", "fill_color", "fill_alpha"},
|
|
224
|
-
"heatmap": {"cmap", "vmin", "vmax", "interpolation"},
|
|
225
|
-
"imshow": {"cmap", "vmin", "vmax", "interpolation"},
|
|
226
|
-
"contour": {"cmap", "vmin", "vmax"},
|
|
227
|
-
"contourf": {"cmap", "vmin", "vmax"},
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
# Get relevant keys for this plot type
|
|
231
|
-
relevant_keys = common_keys | plot_type_keys.get(plot_model.plot_type, set())
|
|
232
|
-
|
|
233
|
-
# Filter kwargs to only include relevant ones
|
|
234
|
-
kwargs = {k: v for k, v in style_dict.items() if k in relevant_keys}
|
|
235
|
-
|
|
236
|
-
# Add label if present
|
|
237
|
-
if plot_model.label:
|
|
238
|
-
kwargs["label"] = plot_model.label
|
|
239
|
-
|
|
240
|
-
# Merge extra kwargs
|
|
241
|
-
kwargs.update(plot_model.extra_kwargs)
|
|
242
|
-
|
|
243
|
-
# Render based on plot type
|
|
244
|
-
if plot_model.plot_type == "line":
|
|
245
|
-
ax.plot(data["x"], data["y"], **kwargs)
|
|
246
|
-
|
|
247
|
-
elif plot_model.plot_type == "scatter":
|
|
248
|
-
# Rename markersize -> s for scatter
|
|
249
|
-
if "markersize" in kwargs:
|
|
250
|
-
kwargs["s"] = kwargs.pop("markersize")
|
|
251
|
-
ax.scatter(data["x"], data["y"], **kwargs)
|
|
252
|
-
|
|
253
|
-
elif plot_model.plot_type == "errorbar":
|
|
254
|
-
ax.errorbar(data["x"], data["y"], **kwargs)
|
|
255
|
-
|
|
256
|
-
elif plot_model.plot_type == "bar":
|
|
257
|
-
height = data.get("height", data.get("y"))
|
|
258
|
-
ax.bar(data["x"], height, **kwargs)
|
|
259
|
-
|
|
260
|
-
elif plot_model.plot_type == "barh":
|
|
261
|
-
# Rename width -> height for horizontal bars
|
|
262
|
-
if "width" in kwargs:
|
|
263
|
-
kwargs["height"] = kwargs.pop("width")
|
|
264
|
-
width = data.get("width", data.get("x"))
|
|
265
|
-
ax.barh(data["y"], width, **kwargs)
|
|
266
|
-
|
|
267
|
-
elif plot_model.plot_type == "hist":
|
|
268
|
-
ax.hist(data["x"], **kwargs)
|
|
269
|
-
|
|
270
|
-
elif plot_model.plot_type == "fill_between":
|
|
271
|
-
# Use fill_alpha and fill_color if present
|
|
272
|
-
if "fill_alpha" in kwargs:
|
|
273
|
-
kwargs["alpha"] = kwargs.pop("fill_alpha")
|
|
274
|
-
if "fill_color" in kwargs:
|
|
275
|
-
kwargs["color"] = kwargs.pop("fill_color")
|
|
276
|
-
ax.fill_between(data["x"], data["y1"], data["y2"], **kwargs)
|
|
277
|
-
|
|
278
|
-
elif plot_model.plot_type in ["heatmap", "imshow"]:
|
|
279
|
-
img_data = data.get("z", data.get("img"))
|
|
280
|
-
ax.imshow(img_data, **kwargs)
|
|
281
|
-
|
|
282
|
-
elif plot_model.plot_type == "contour":
|
|
283
|
-
ax.contour(data["x"], data["y"], data["z"], **kwargs)
|
|
284
|
-
|
|
285
|
-
elif plot_model.plot_type == "contourf":
|
|
286
|
-
ax.contourf(data["x"], data["y"], data["z"], **kwargs)
|
|
287
|
-
|
|
288
|
-
else:
|
|
289
|
-
raise ValueError(f"Unsupported plot type: {plot_model.plot_type}")
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
def render_guide(ax, guide_model: GuideModel):
|
|
293
|
-
"""
|
|
294
|
-
Render GuideModel onto an axis.
|
|
295
|
-
|
|
296
|
-
Parameters
|
|
297
|
-
----------
|
|
298
|
-
ax : AxisWrapper or matplotlib Axes
|
|
299
|
-
Target axis
|
|
300
|
-
guide_model : GuideModel
|
|
301
|
-
Guide model to render
|
|
302
|
-
"""
|
|
303
|
-
# Validate
|
|
304
|
-
guide_model.validate()
|
|
305
|
-
|
|
306
|
-
# Build kwargs from style
|
|
307
|
-
kwargs = guide_model.style.to_dict()
|
|
308
|
-
|
|
309
|
-
# Add label if present
|
|
310
|
-
if guide_model.label:
|
|
311
|
-
kwargs["label"] = guide_model.label
|
|
312
|
-
|
|
313
|
-
# Render based on guide type
|
|
314
|
-
if guide_model.guide_type == "axhline":
|
|
315
|
-
ax.axhline(guide_model.y, **kwargs)
|
|
316
|
-
|
|
317
|
-
elif guide_model.guide_type == "axvline":
|
|
318
|
-
ax.axvline(guide_model.x, **kwargs)
|
|
319
|
-
|
|
320
|
-
elif guide_model.guide_type == "axhspan":
|
|
321
|
-
ax.axhspan(guide_model.ymin, guide_model.ymax, **kwargs)
|
|
322
|
-
|
|
323
|
-
elif guide_model.guide_type == "axvspan":
|
|
324
|
-
ax.axvspan(guide_model.xmin, guide_model.xmax, **kwargs)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
def render_annotation(ax, annotation_model: AnnotationModel):
|
|
328
|
-
"""
|
|
329
|
-
Render AnnotationModel onto an axis.
|
|
330
|
-
|
|
331
|
-
Parameters
|
|
332
|
-
----------
|
|
333
|
-
ax : AxisWrapper or matplotlib Axes
|
|
334
|
-
Target axis
|
|
335
|
-
annotation_model : AnnotationModel
|
|
336
|
-
Annotation model to render
|
|
337
|
-
"""
|
|
338
|
-
# Validate
|
|
339
|
-
annotation_model.validate()
|
|
340
|
-
|
|
341
|
-
# Build kwargs from style
|
|
342
|
-
kwargs = annotation_model.style.to_dict()
|
|
343
|
-
|
|
344
|
-
# Render based on annotation type
|
|
345
|
-
if annotation_model.annotation_type == "text":
|
|
346
|
-
ax.text(
|
|
347
|
-
annotation_model.x,
|
|
348
|
-
annotation_model.y,
|
|
349
|
-
annotation_model.text,
|
|
350
|
-
**kwargs,
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
elif annotation_model.annotation_type == "annotate":
|
|
354
|
-
ax.annotate(
|
|
355
|
-
annotation_model.text,
|
|
356
|
-
xy=(annotation_model.x, annotation_model.y),
|
|
357
|
-
xytext=annotation_model.xytext,
|
|
358
|
-
**kwargs,
|
|
359
|
-
)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
def build_figure_from_json(fig_json: Dict[str, Any]):
|
|
363
|
-
"""
|
|
364
|
-
Build matplotlib figure from figure JSON.
|
|
365
|
-
|
|
366
|
-
This is the main entry point for rendering.
|
|
367
|
-
|
|
368
|
-
Parameters
|
|
369
|
-
----------
|
|
370
|
-
fig_json : Dict[str, Any]
|
|
371
|
-
Figure JSON specification
|
|
372
|
-
|
|
373
|
-
Returns
|
|
374
|
-
-------
|
|
375
|
-
tuple
|
|
376
|
-
(fig, axes) where fig is FigWrapper and axes is list of AxisWrapper
|
|
377
|
-
|
|
378
|
-
Examples
|
|
379
|
-
--------
|
|
380
|
-
>>> fig_json = {...}
|
|
381
|
-
>>> fig, axes = build_figure_from_json(fig_json)
|
|
382
|
-
>>> import scitex as stx
|
|
383
|
-
>>> stx.io.save(fig, "output.png")
|
|
384
|
-
"""
|
|
385
|
-
fig_model = parse_figure_json(fig_json)
|
|
386
|
-
return render_figure(fig_model)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
def _find_column(data, col_name):
|
|
390
|
-
"""Find a column in data, supporting prefixed column names.
|
|
391
|
-
|
|
392
|
-
Parameters
|
|
393
|
-
----------
|
|
394
|
-
data : pd.DataFrame
|
|
395
|
-
Data to search
|
|
396
|
-
col_name : str
|
|
397
|
-
Column name to find (can be simple like "x" or prefixed)
|
|
398
|
-
|
|
399
|
-
Returns
|
|
400
|
-
-------
|
|
401
|
-
str or None
|
|
402
|
-
Actual column name in data, or None if not found
|
|
403
|
-
"""
|
|
404
|
-
if col_name in data.columns:
|
|
405
|
-
return col_name
|
|
406
|
-
|
|
407
|
-
# Try to find a column that ends with the variable name
|
|
408
|
-
# e.g., "ax-row-0-col-0_trace-id-plot-0_variable-x" matches "x"
|
|
409
|
-
suffix = f"_variable-{col_name}"
|
|
410
|
-
for col in data.columns:
|
|
411
|
-
if col.endswith(suffix):
|
|
412
|
-
return col
|
|
413
|
-
|
|
414
|
-
# Try partial match at the end
|
|
415
|
-
for col in data.columns:
|
|
416
|
-
if col.endswith(f"-{col_name}"):
|
|
417
|
-
return col
|
|
418
|
-
|
|
419
|
-
return None
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
def _is_special_trace(trace_id):
|
|
423
|
-
"""Check if trace is a special type that doesn't require x/y encoding.
|
|
424
|
-
|
|
425
|
-
Parameters
|
|
426
|
-
----------
|
|
427
|
-
trace_id : str
|
|
428
|
-
Trace identifier
|
|
429
|
-
|
|
430
|
-
Returns
|
|
431
|
-
-------
|
|
432
|
-
bool
|
|
433
|
-
True if this is a special trace type
|
|
434
|
-
"""
|
|
435
|
-
special_patterns = [
|
|
436
|
-
"fill-between",
|
|
437
|
-
"fill_between",
|
|
438
|
-
"axhline",
|
|
439
|
-
"axvline",
|
|
440
|
-
"axhspan",
|
|
441
|
-
"axvspan",
|
|
442
|
-
"text",
|
|
443
|
-
"annotation",
|
|
444
|
-
]
|
|
445
|
-
trace_lower = trace_id.lower()
|
|
446
|
-
return any(pattern in trace_lower for pattern in special_patterns)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
def render_traces(ax, trace, data, theme=None):
|
|
450
|
-
"""
|
|
451
|
-
Render a TraceEncoding onto an axis.
|
|
452
|
-
|
|
453
|
-
Parameters
|
|
454
|
-
----------
|
|
455
|
-
ax : matplotlib Axes
|
|
456
|
-
Target axis
|
|
457
|
-
trace : TraceEncoding
|
|
458
|
-
Trace encoding specification
|
|
459
|
-
data : pd.DataFrame or None
|
|
460
|
-
Data to plot
|
|
461
|
-
theme : Theme, optional
|
|
462
|
-
Theme for styling
|
|
463
|
-
|
|
464
|
-
Raises
|
|
465
|
-
------
|
|
466
|
-
ValueError
|
|
467
|
-
If data is None or required columns are missing (for standard traces)
|
|
468
|
-
"""
|
|
469
|
-
# Skip special traces that don't require standard x/y encoding
|
|
470
|
-
if _is_special_trace(trace.trace_id):
|
|
471
|
-
logger.debug(f"Skipping special trace '{trace.trace_id}' (no x/y rendering)")
|
|
472
|
-
return
|
|
473
|
-
|
|
474
|
-
if data is None:
|
|
475
|
-
logger.warning(f"No data provided for trace '{trace.trace_id}', skipping")
|
|
476
|
-
return
|
|
477
|
-
|
|
478
|
-
# Get style from theme
|
|
479
|
-
color = None
|
|
480
|
-
linewidth = 1.0
|
|
481
|
-
alpha = 1.0
|
|
482
|
-
|
|
483
|
-
if theme:
|
|
484
|
-
# Check for trace-specific theme
|
|
485
|
-
if hasattr(theme, "traces") and theme.traces:
|
|
486
|
-
for t in theme.traces:
|
|
487
|
-
if t.trace_id == trace.trace_id:
|
|
488
|
-
color = t.color
|
|
489
|
-
if t.line_width_pt:
|
|
490
|
-
linewidth = t.line_width_pt
|
|
491
|
-
if t.alpha:
|
|
492
|
-
alpha = t.alpha
|
|
493
|
-
break
|
|
494
|
-
|
|
495
|
-
# Fall back to default colors
|
|
496
|
-
if not color and hasattr(theme, "colors"):
|
|
497
|
-
palette = theme.colors.palette
|
|
498
|
-
if palette:
|
|
499
|
-
color = palette[0]
|
|
500
|
-
|
|
501
|
-
if hasattr(theme, "lines"):
|
|
502
|
-
linewidth = theme.lines.width_pt
|
|
503
|
-
|
|
504
|
-
# Default color if none set
|
|
505
|
-
if not color:
|
|
506
|
-
color = "#1f77b4"
|
|
507
|
-
|
|
508
|
-
# Get column names from encoding
|
|
509
|
-
x_col_name = trace.x.column if trace.x else None
|
|
510
|
-
y_col_name = trace.y.column if trace.y else None
|
|
511
|
-
|
|
512
|
-
if not x_col_name or not y_col_name:
|
|
513
|
-
logger.warning(f"Trace '{trace.trace_id}' missing x or y encoding, skipping")
|
|
514
|
-
return
|
|
515
|
-
|
|
516
|
-
# Find actual columns in data (handles prefixed names)
|
|
517
|
-
x_col = _find_column(data, x_col_name)
|
|
518
|
-
y_col = _find_column(data, y_col_name)
|
|
519
|
-
|
|
520
|
-
if x_col is None:
|
|
521
|
-
logger.warning(f"Column '{x_col_name}' not found in data for trace '{trace.trace_id}'. Available: {list(data.columns)[:5]}...")
|
|
522
|
-
return
|
|
523
|
-
if y_col is None:
|
|
524
|
-
logger.warning(f"Column '{y_col_name}' not found in data for trace '{trace.trace_id}'. Available: {list(data.columns)[:5]}...")
|
|
525
|
-
return
|
|
526
|
-
|
|
527
|
-
# Plot the data
|
|
528
|
-
ax.plot(
|
|
529
|
-
data[x_col],
|
|
530
|
-
data[y_col],
|
|
531
|
-
color=color,
|
|
532
|
-
linewidth=linewidth,
|
|
533
|
-
alpha=alpha,
|
|
534
|
-
label=trace.trace_id,
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
# EOF
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# Timestamp: 2025-12-20
|
|
3
|
-
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/ChannelEncoding.py
|
|
4
|
-
|
|
5
|
-
"""ChannelEncoding - Single channel encoding."""
|
|
6
|
-
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from typing import Any, Dict, Optional
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@dataclass
|
|
12
|
-
class ChannelEncoding:
|
|
13
|
-
"""Encoding for a single visual channel (x, y, color, size, etc.)."""
|
|
14
|
-
|
|
15
|
-
column: Optional[str] = None
|
|
16
|
-
scale: str = "linear" # linear, log, categorical, ordinal, time
|
|
17
|
-
domain: Optional[tuple] = None # Min/max or category list
|
|
18
|
-
range: Optional[tuple] = None # Output range for mapping
|
|
19
|
-
|
|
20
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
21
|
-
result = {}
|
|
22
|
-
if self.column:
|
|
23
|
-
result["column"] = self.column
|
|
24
|
-
if self.scale != "linear":
|
|
25
|
-
result["scale"] = self.scale
|
|
26
|
-
if self.domain:
|
|
27
|
-
result["domain"] = list(self.domain)
|
|
28
|
-
if self.range:
|
|
29
|
-
result["range"] = list(self.range)
|
|
30
|
-
return result
|
|
31
|
-
|
|
32
|
-
@classmethod
|
|
33
|
-
def from_dict(cls, data: Dict[str, Any]) -> "ChannelEncoding":
|
|
34
|
-
domain = data.get("domain")
|
|
35
|
-
range_ = data.get("range")
|
|
36
|
-
return cls(
|
|
37
|
-
column=data.get("column"),
|
|
38
|
-
scale=data.get("scale", "linear"),
|
|
39
|
-
domain=tuple(domain) if domain else None,
|
|
40
|
-
range=tuple(range_) if range_ else None,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
__all__ = ["ChannelEncoding"]
|
|
45
|
-
|
|
46
|
-
# EOF
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# Timestamp: 2025-12-20
|
|
3
|
-
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/Encoding.py
|
|
4
|
-
|
|
5
|
-
"""Encoding - Complete encoding specification for a bundle."""
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
from dataclasses import dataclass, field
|
|
9
|
-
from typing import Any, Dict, List
|
|
10
|
-
|
|
11
|
-
from ._TraceEncoding import TraceEncoding
|
|
12
|
-
|
|
13
|
-
ENCODING_VERSION = "1.0.0"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@dataclass
|
|
17
|
-
class AxesConfig:
|
|
18
|
-
"""Axis configuration for encoding."""
|
|
19
|
-
|
|
20
|
-
title: str = ""
|
|
21
|
-
type: str = "quantitative"
|
|
22
|
-
|
|
23
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
24
|
-
result = {}
|
|
25
|
-
if self.title:
|
|
26
|
-
result["title"] = self.title
|
|
27
|
-
if self.type != "quantitative":
|
|
28
|
-
result["type"] = self.type
|
|
29
|
-
return result
|
|
30
|
-
|
|
31
|
-
@classmethod
|
|
32
|
-
def from_dict(cls, data: Dict[str, Any]) -> "AxesConfig":
|
|
33
|
-
return cls(
|
|
34
|
-
title=data.get("title", ""),
|
|
35
|
-
type=data.get("type", "quantitative"),
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@dataclass
|
|
40
|
-
class Encoding:
|
|
41
|
-
"""Complete encoding specification for a bundle.
|
|
42
|
-
|
|
43
|
-
Stored in encoding.json at bundle root.
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
traces: List[TraceEncoding] = field(default_factory=list)
|
|
47
|
-
axes: Dict[str, AxesConfig] = field(default_factory=dict) # {"x": AxesConfig, "y": AxesConfig}
|
|
48
|
-
|
|
49
|
-
# Schema metadata
|
|
50
|
-
schema_name: str = "fsb.encoding"
|
|
51
|
-
schema_version: str = ENCODING_VERSION
|
|
52
|
-
|
|
53
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
54
|
-
result = {
|
|
55
|
-
"traces": [t.to_dict() for t in self.traces],
|
|
56
|
-
}
|
|
57
|
-
if self.axes:
|
|
58
|
-
result["axes"] = {k: v.to_dict() for k, v in self.axes.items()}
|
|
59
|
-
return result
|
|
60
|
-
|
|
61
|
-
def to_json(self, indent: int = 2) -> str:
|
|
62
|
-
return json.dumps(self.to_dict(), indent=indent)
|
|
63
|
-
|
|
64
|
-
@classmethod
|
|
65
|
-
def from_dict(cls, data: Dict[str, Any]) -> "Encoding":
|
|
66
|
-
axes = {}
|
|
67
|
-
axes_data = data.get("axes", {})
|
|
68
|
-
for key, val in axes_data.items():
|
|
69
|
-
axes[key] = AxesConfig.from_dict(val) if isinstance(val, dict) else AxesConfig()
|
|
70
|
-
return cls(
|
|
71
|
-
traces=[TraceEncoding.from_dict(t) for t in data.get("traces", [])],
|
|
72
|
-
axes=axes,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
@classmethod
|
|
76
|
-
def from_json(cls, json_str: str) -> "Encoding":
|
|
77
|
-
return cls.from_dict(json.loads(json_str))
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
__all__ = ["ENCODING_VERSION", "AxesConfig", "Encoding"]
|
|
81
|
-
|
|
82
|
-
# EOF
|