scitex 2.7.3__py3-none-any.whl → 2.10.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 +15 -7
- scitex/__version__.py +1 -2
- scitex/_install_guide.py +250 -0
- scitex/_optional_deps.py +206 -39
- scitex/ai/_gen_ai/_Groq.py +2 -4
- scitex/ai/_gen_ai/_OpenAI.py +5 -2
- scitex/ai/_gen_ai/_Perplexity.py +20 -6
- scitex/audio/__init__.py +24 -15
- scitex/audio/_cross_process_lock.py +139 -0
- scitex/audio/_mcp_handlers.py +256 -0
- scitex/audio/_mcp_tool_schemas.py +203 -0
- scitex/audio/engines/elevenlabs_engine.py +5 -2
- scitex/audio/mcp_server.py +98 -457
- scitex/bridge/__init__.py +30 -19
- scitex/bridge/_figrecipe.py +245 -0
- scitex/bridge/_helpers.py +2 -1
- scitex/bridge/_plt_vis.py +23 -10
- scitex/bridge/_stats_plt.py +18 -5
- scitex/bridge/_stats_vis.py +16 -2
- scitex/browser/__init__.py +84 -44
- scitex/browser/automation/__init__.py +5 -1
- scitex/browser/core/BrowserMixin.py +17 -4
- scitex/browser/core/__init__.py +11 -2
- scitex/browser/remote/CaptchaHandler.py +1 -1
- scitex/browser/remote/ZenRowsAPIClient.py +1 -1
- scitex/capture/grid.py +487 -0
- scitex/capture/mcp_handlers.py +401 -0
- scitex/capture/mcp_tool_defs.py +192 -0
- scitex/capture/mcp_tools.py +241 -0
- scitex/capture/mcp_utils.py +30 -0
- scitex/cli/convert.py +421 -0
- scitex/cli/main.py +6 -4
- scitex/datetime/__init__.py +46 -0
- scitex/datetime/_linspace.py +100 -0
- scitex/datetime/_normalize_timestamp.py +306 -0
- scitex/db/_delete_duplicates.py +4 -4
- scitex/db/_sqlite3/_delete_duplicates.py +11 -2
- scitex/dev/plt/__init__.py +61 -62
- scitex/dev/plt/demo_plotters/__init__.py +0 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
- scitex/dev/plt/mpl/get_dir_ax.py +46 -0
- scitex/dev/plt/mpl/get_signatures.py +176 -0
- scitex/dev/plt/mpl/get_signatures_details.py +522 -0
- scitex/dev/plt/plot_mpl_axhline.py +0 -0
- scitex/dev/plt/plot_mpl_axhspan.py +0 -0
- scitex/dev/plt/plot_mpl_axvline.py +0 -0
- scitex/dev/plt/plot_mpl_axvspan.py +0 -0
- scitex/dev/plt/plot_mpl_bar.py +0 -0
- scitex/dev/plt/plot_mpl_barh.py +0 -0
- scitex/dev/plt/plot_mpl_boxplot.py +0 -0
- scitex/dev/plt/plot_mpl_contour.py +0 -0
- scitex/dev/plt/plot_mpl_contourf.py +0 -0
- scitex/dev/plt/plot_mpl_errorbar.py +0 -0
- scitex/dev/plt/plot_mpl_eventplot.py +0 -0
- scitex/dev/plt/plot_mpl_fill.py +0 -0
- scitex/dev/plt/plot_mpl_fill_between.py +0 -0
- scitex/dev/plt/plot_mpl_hexbin.py +0 -0
- scitex/dev/plt/plot_mpl_hist.py +0 -0
- scitex/dev/plt/plot_mpl_hist2d.py +0 -0
- scitex/dev/plt/plot_mpl_imshow.py +0 -0
- scitex/dev/plt/plot_mpl_pcolormesh.py +0 -0
- scitex/dev/plt/plot_mpl_pie.py +0 -0
- scitex/dev/plt/plot_mpl_plot.py +0 -0
- scitex/dev/plt/plot_mpl_quiver.py +0 -0
- scitex/dev/plt/plot_mpl_scatter.py +0 -0
- scitex/dev/plt/plot_mpl_stackplot.py +0 -0
- scitex/dev/plt/plot_mpl_stem.py +0 -0
- scitex/dev/plt/plot_mpl_step.py +0 -0
- scitex/dev/plt/plot_mpl_violinplot.py +0 -0
- scitex/dev/plt/plot_sns_barplot.py +0 -0
- scitex/dev/plt/plot_sns_boxplot.py +0 -0
- scitex/dev/plt/plot_sns_heatmap.py +0 -0
- scitex/dev/plt/plot_sns_histplot.py +0 -0
- scitex/dev/plt/plot_sns_kdeplot.py +0 -0
- scitex/dev/plt/plot_sns_lineplot.py +0 -0
- scitex/dev/plt/plot_sns_scatterplot.py +0 -0
- scitex/dev/plt/plot_sns_stripplot.py +0 -0
- scitex/dev/plt/plot_sns_swarmplot.py +0 -0
- scitex/dev/plt/plot_sns_violinplot.py +0 -0
- scitex/dev/plt/plot_stx_bar.py +0 -0
- scitex/dev/plt/plot_stx_barh.py +0 -0
- scitex/dev/plt/plot_stx_box.py +0 -0
- scitex/dev/plt/plot_stx_boxplot.py +0 -0
- scitex/dev/plt/plot_stx_conf_mat.py +0 -0
- scitex/dev/plt/plot_stx_contour.py +0 -0
- scitex/dev/plt/plot_stx_ecdf.py +0 -0
- scitex/dev/plt/plot_stx_errorbar.py +0 -0
- scitex/dev/plt/plot_stx_fill_between.py +0 -0
- scitex/dev/plt/plot_stx_fillv.py +0 -0
- scitex/dev/plt/plot_stx_heatmap.py +0 -0
- scitex/dev/plt/plot_stx_image.py +0 -0
- scitex/dev/plt/plot_stx_imshow.py +0 -0
- scitex/dev/plt/plot_stx_joyplot.py +0 -0
- scitex/dev/plt/plot_stx_kde.py +0 -0
- scitex/dev/plt/plot_stx_line.py +0 -0
- scitex/dev/plt/plot_stx_mean_ci.py +0 -0
- scitex/dev/plt/plot_stx_mean_std.py +0 -0
- scitex/dev/plt/plot_stx_median_iqr.py +0 -0
- scitex/dev/plt/plot_stx_raster.py +0 -0
- scitex/dev/plt/plot_stx_rectangle.py +0 -0
- scitex/dev/plt/plot_stx_scatter.py +0 -0
- scitex/dev/plt/plot_stx_shaded_line.py +0 -0
- scitex/dev/plt/plot_stx_violin.py +0 -0
- scitex/dev/plt/plot_stx_violinplot.py +0 -0
- scitex/diagram/README.md +197 -0
- scitex/diagram/__init__.py +48 -0
- scitex/diagram/_compile.py +312 -0
- scitex/diagram/_diagram.py +355 -0
- scitex/diagram/_presets.py +173 -0
- scitex/diagram/_schema.py +182 -0
- scitex/diagram/_split.py +278 -0
- scitex/dict/_pop_keys.py +1 -7
- scitex/dsp/__init__.py +15 -10
- scitex/dsp/add_noise.py +5 -2
- scitex/dsp/example.py +35 -22
- scitex/dsp/filt.py +8 -3
- scitex/dsp/reference.py +3 -2
- scitex/dsp/utils/__init__.py +2 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
- scitex/dt/__init__.py +39 -2
- scitex/errors.py +82 -521
- scitex/fig/__init__.py +4 -4
- scitex/fig/editor/__init__.py +5 -2
- scitex/fig/editor/_dearpygui_editor.py +1 -1
- scitex/fig/editor/_mpl_editor.py +1 -1
- scitex/fig/editor/_qt_editor.py +1 -1
- scitex/fig/editor/_tkinter_editor.py +1 -1
- scitex/fig/editor/edit/__init__.py +50 -0
- scitex/fig/editor/edit/backend_detector.py +109 -0
- scitex/fig/editor/edit/bundle_resolver.py +240 -0
- scitex/fig/editor/edit/editor_launcher.py +239 -0
- scitex/fig/editor/edit/manual_handler.py +53 -0
- scitex/fig/editor/edit/panel_loader.py +232 -0
- scitex/fig/editor/edit/path_resolver.py +67 -0
- scitex/fig/editor/flask_editor/_bbox.py +23 -0
- scitex/fig/editor/flask_editor/_core.py +908 -103
- scitex/fig/editor/flask_editor/_renderer.py +74 -0
- scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
- scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
- scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
- scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
- scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
- scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
- scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
- scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
- scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
- scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
- scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
- scitex/fig/editor/flask_editor/static/css/index.css +31 -0
- scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
- scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
- scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
- scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
- scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
- scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
- scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
- scitex/fig/editor/flask_editor/static/js/main.js +426 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
- scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
- scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
- scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
- scitex/fig/editor/flask_editor/templates/__init__.py +95 -5
- scitex/fig/editor/flask_editor/templates/_html.py +27 -9
- scitex/fig/editor/flask_editor/templates/_scripts.py +1928 -131
- scitex/fig/editor/flask_editor/templates/_styles.py +363 -51
- scitex/fig/io/_bundle.py +104 -19
- scitex/fts/README.md +262 -0
- scitex/fts/TODO.md +66 -0
- scitex/fts/__init__.py +90 -0
- scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
- scitex/fts/_bundle/_FTS.py +657 -0
- scitex/fts/_bundle/__init__.py +38 -0
- scitex/fts/_bundle/_children.py +216 -0
- scitex/fts/_bundle/_conversion/__init__.py +15 -0
- scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
- scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
- scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
- scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
- scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
- scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
- scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
- scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
- scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
- scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
- scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
- scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
- scitex/fts/_bundle/_extractors/__init__.py +32 -0
- scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
- scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
- scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
- scitex/fts/_bundle/_loader.py +134 -0
- scitex/fts/_bundle/_mpl_helpers.py +389 -0
- scitex/fts/_bundle/_saver.py +269 -0
- scitex/fts/_bundle/_storage.py +200 -0
- scitex/fts/_bundle/_utils/__init__.py +55 -0
- scitex/fts/_bundle/_utils/_const.py +26 -0
- scitex/fts/_bundle/_utils/_errors.py +73 -0
- scitex/fts/_bundle/_utils/_generate.py +21 -0
- scitex/fts/_bundle/_utils/_types.py +76 -0
- scitex/fts/_bundle/_validation.py +434 -0
- scitex/fts/_bundle/_zipbundle.py +165 -0
- scitex/fts/_fig/__init__.py +22 -0
- scitex/fts/_fig/_backend/__init__.py +53 -0
- scitex/fts/_fig/_backend/_export.py +165 -0
- scitex/fts/_fig/_backend/_parser.py +188 -0
- scitex/fts/_fig/_backend/_render.py +538 -0
- scitex/fts/_fig/_composite.py +345 -0
- scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
- scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_fig/_dataclasses/__init__.py +47 -0
- scitex/fts/_fig/_editor/__init__.py +14 -0
- scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
- scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
- scitex/fts/_fig/_editor/_defaults.py +300 -0
- scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
- scitex/fts/_fig/_models/_Annotations.py +115 -0
- scitex/fts/_fig/_models/_Axes.py +152 -0
- scitex/fts/_fig/_models/_Figure.py +138 -0
- scitex/fts/_fig/_models/_Guides.py +104 -0
- scitex/fts/_fig/_models/_Plot.py +123 -0
- scitex/fts/_fig/_models/_Styles.py +245 -0
- scitex/fts/_fig/_models/__init__.py +80 -0
- scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
- scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
- scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
- scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_fig/_utils/__init__.py +129 -0
- scitex/fts/_fig/_utils/_auto_layout.py +127 -0
- scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
- scitex/fts/_fig/_utils/_const_sizes.py +48 -0
- scitex/fts/_fig/_utils/_convert_coords.py +77 -0
- scitex/fts/_fig/_utils/_get_template.py +178 -0
- scitex/fts/_fig/_utils/_normalize.py +73 -0
- scitex/fts/_fig/_utils/_plot_layout.py +397 -0
- scitex/fts/_fig/_utils/_validate.py +197 -0
- scitex/fts/_kinds/__init__.py +45 -0
- scitex/fts/_kinds/_figure/__init__.py +19 -0
- scitex/fts/_kinds/_figure/_composite.py +345 -0
- scitex/fts/_kinds/_plot/__init__.py +25 -0
- scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
- scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
- scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
- scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
- scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
- scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
- scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
- scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
- scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
- scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
- scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
- scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
- scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
- scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
- scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
- scitex/fts/_kinds/_shape/__init__.py +141 -0
- scitex/fts/_kinds/_stats/__init__.py +56 -0
- scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_kinds/_table/__init__.py +72 -0
- scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
- scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
- scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
- scitex/fts/_kinds/_table/_latex/_export.py +279 -0
- scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
- scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
- scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
- scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
- scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
- scitex/fts/_kinds/_text/__init__.py +77 -0
- scitex/fts/_schemas/data_info.schema.json +75 -0
- scitex/fts/_schemas/encoding.schema.json +90 -0
- scitex/fts/_schemas/node.schema.json +145 -0
- scitex/fts/_schemas/render_manifest.schema.json +62 -0
- scitex/fts/_schemas/stats.schema.json +132 -0
- scitex/fts/_schemas/theme.schema.json +141 -0
- scitex/fts/_stats/__init__.py +48 -0
- scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_tables/__init__.py +65 -0
- scitex/fts/_tables/_latex/__init__.py +93 -0
- scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
- scitex/fts/_tables/_latex/_editor/_app.py +725 -0
- scitex/fts/_tables/_latex/_export.py +279 -0
- scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
- scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
- scitex/fts/_tables/_latex/_table_exporter.py +362 -0
- scitex/fts/_tables/_latex/_utils.py +369 -0
- scitex/fts/_tables/_latex/_validator.py +445 -0
- scitex/gen/__init__.py +66 -25
- scitex/gen/misc.py +28 -0
- scitex/io/__init__.py +47 -20
- scitex/io/_load.py +87 -36
- scitex/io/_load_modules/__init__.py +10 -7
- scitex/io/_load_modules/_pandas.py +6 -1
- scitex/io/_save.py +299 -1556
- scitex/io/_save_modules/__init__.py +76 -19
- scitex/io/_save_modules/_figure_utils.py +90 -0
- scitex/io/_save_modules/_image_csv.py +497 -0
- scitex/io/_save_modules/_legends.py +91 -0
- scitex/io/_save_modules/_pltz_bundle.py +356 -0
- scitex/io/_save_modules/_pltz_stx.py +536 -0
- scitex/io/_save_modules/_stx_bundle.py +104 -0
- scitex/io/_save_modules/_symlink.py +96 -0
- scitex/io/_save_modules/_yaml.py +1 -1
- scitex/io/_save_modules/_zarr.py +64 -18
- scitex/io/bundle/README.md +212 -0
- scitex/io/bundle/__init__.py +110 -0
- scitex/io/{_bundle.py → bundle/_core.py} +219 -89
- scitex/io/bundle/_nested.py +713 -0
- scitex/io/bundle/_types.py +74 -0
- scitex/io/bundle/_zip.py +487 -0
- scitex/io/utils/h5_to_zarr.py +1 -1
- scitex/logging/__init__.py +108 -13
- scitex/logging/_errors.py +508 -0
- scitex/logging/_formatters.py +30 -6
- scitex/logging/_warnings.py +261 -0
- scitex/plt/__init__.py +4 -1
- scitex/plt/_figrecipe.py +236 -0
- scitex/plt/_subplots/_AxisWrapper.py +6 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
- scitex/plt/_subplots/_FigWrapper.py +15 -0
- scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
- scitex/plt/_subplots/_export_as_csv.py +11 -0
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
- scitex/plt/_subplots/_fonts.py +71 -0
- scitex/plt/_subplots/_mm_layout.py +282 -0
- scitex/plt/gallery/__init__.py +99 -2
- scitex/plt/io/_layered_bundle.py +0 -0
- scitex/plt/styles/_plot_postprocess.py +3 -1
- scitex/plt/utils/_configure_mpl.py +16 -19
- scitex/repro/_RandomStateManager.py +13 -8
- scitex/resource/__init__.py +19 -1
- scitex/resource/_utils/_get_env_info.py +13 -25
- scitex/schema/__init__.py +149 -160
- scitex/schema/_encoding.py +273 -0
- scitex/schema/_figure_elements.py +406 -0
- scitex/schema/_plot.py +0 -0
- scitex/schema/_theme.py +360 -0
- scitex/schema/_validation.py +0 -98
- scitex/scholar/__init__.py +56 -14
- scitex/scholar/auth/ScholarAuthManager.py +1 -1
- scitex/scholar/auth/__init__.py +11 -2
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
- scitex/scholar/config/ScholarConfig.py +1 -1
- scitex/scholar/core/Scholar.py +1 -1
- scitex/session/_decorator.py +18 -16
- scitex/session/_lifecycle.py +9 -11
- scitex/session/template.py +9 -8
- scitex/sh/test_sh.py +72 -0
- scitex/sh/test_sh_simple.py +61 -0
- scitex/stats/__init__.py +221 -97
- scitex/stats/_schema.py +21 -22
- scitex/stats/descriptive/_circular.py +212 -351
- scitex/stats/descriptive/_describe.py +81 -132
- scitex/stats/descriptive/_nan.py +205 -433
- scitex/stats/descriptive/_real.py +127 -141
- scitex/str/_format_plot_text.py +5 -5
- scitex/str/_latex.py +26 -84
- scitex/str/_latex_fallback.py +53 -47
- scitex/web/_search_pubmed.py +5 -4
- scitex/writer/tests/test_diff_between.py +451 -0
- scitex/writer/tests/test_document_section.py +311 -0
- scitex/writer/tests/test_document_workflow.py +393 -0
- scitex/writer/tests/test_writer.py +361 -0
- scitex/writer/tests/test_writer_integration.py +303 -0
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/RECORD +479 -108
- scitex/fig/editor/_edit.py +0 -751
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fts/_fig/_composite.py
|
|
4
|
+
|
|
5
|
+
"""Composite figure renderer for FTS bundles.
|
|
6
|
+
|
|
7
|
+
Renders composite figures (kind=figure) containing multiple children.
|
|
8
|
+
ALWAYS re-renders from child's canonical (exports = optional cache only).
|
|
9
|
+
|
|
10
|
+
Design principles:
|
|
11
|
+
- Re-render children from canonical/encoding.json + payload/data.csv
|
|
12
|
+
- Recursively render nested figures (NOT from exports/)
|
|
13
|
+
- Apply container's theme for unified styling
|
|
14
|
+
- Generate geometry_px.json with flattened child geometry
|
|
15
|
+
- Cache key includes canonical_hash + effective_theme_hash + renderer_version
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import io
|
|
19
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from matplotlib.axes import Axes as MplAxes
|
|
23
|
+
from matplotlib.figure import Figure as MplFigure
|
|
24
|
+
|
|
25
|
+
from .._bundle._FTS import FTS
|
|
26
|
+
from ._dataclasses import Theme
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def render_composite(
|
|
30
|
+
children: Dict[str, "FTS"],
|
|
31
|
+
layout: Dict,
|
|
32
|
+
size_mm: Optional[Dict[str, float]] = None,
|
|
33
|
+
theme: Optional["Theme"] = None,
|
|
34
|
+
dpi: int = 300,
|
|
35
|
+
) -> Tuple["MplFigure", Dict]:
|
|
36
|
+
"""Render composite figure with children in grid layout.
|
|
37
|
+
|
|
38
|
+
ALWAYS re-renders from child's canonical (exports = optional cache only).
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
children: Dict mapping child_name -> FTS object
|
|
42
|
+
layout: Layout specification {rows, cols, panels: [...]}
|
|
43
|
+
size_mm: Figure size in mm (default: 170x85 for two-column)
|
|
44
|
+
theme: Theme to apply to all children
|
|
45
|
+
dpi: Output DPI
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
(figure, geometry_data)
|
|
49
|
+
"""
|
|
50
|
+
import matplotlib.pyplot as plt
|
|
51
|
+
from matplotlib.gridspec import GridSpec
|
|
52
|
+
|
|
53
|
+
# Default size
|
|
54
|
+
if size_mm is None:
|
|
55
|
+
size_mm = {"width": 170, "height": 85}
|
|
56
|
+
|
|
57
|
+
# Convert mm to inches (matplotlib uses inches)
|
|
58
|
+
width_in = size_mm.get("width", 170) / 25.4
|
|
59
|
+
height_in = size_mm.get("height", 85) / 25.4
|
|
60
|
+
|
|
61
|
+
# Create figure with gridspec
|
|
62
|
+
rows = layout.get("rows", 1)
|
|
63
|
+
cols = layout.get("cols", 1)
|
|
64
|
+
|
|
65
|
+
fig = plt.figure(figsize=(width_in, height_in), dpi=dpi)
|
|
66
|
+
gs = GridSpec(rows, cols, figure=fig)
|
|
67
|
+
|
|
68
|
+
# Collect geometry data for all elements
|
|
69
|
+
geometry = {
|
|
70
|
+
"elements": [],
|
|
71
|
+
"panels": [],
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Render each panel
|
|
75
|
+
panels = layout.get("panels", [])
|
|
76
|
+
for panel_info in panels:
|
|
77
|
+
child_name = panel_info.get("child")
|
|
78
|
+
row = panel_info.get("row", 0)
|
|
79
|
+
col = panel_info.get("col", 0)
|
|
80
|
+
row_span = panel_info.get("row_span", 1)
|
|
81
|
+
col_span = panel_info.get("col_span", 1)
|
|
82
|
+
label = panel_info.get("label")
|
|
83
|
+
|
|
84
|
+
if child_name not in children:
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
child = children[child_name]
|
|
88
|
+
|
|
89
|
+
# Create axes for this panel
|
|
90
|
+
ax = fig.add_subplot(gs[row : row + row_span, col : col + col_span])
|
|
91
|
+
|
|
92
|
+
# Render child into axes (ALWAYS re-render from canonical)
|
|
93
|
+
child_geometry = render_child_in_axes(ax, child, theme)
|
|
94
|
+
|
|
95
|
+
# Add panel label if specified
|
|
96
|
+
if label:
|
|
97
|
+
_add_panel_label(ax, label)
|
|
98
|
+
|
|
99
|
+
# Record panel geometry (flatten into parent's figure_px space)
|
|
100
|
+
panel_geometry = {
|
|
101
|
+
"child": child_name,
|
|
102
|
+
"child_id": panel_info.get("child_id"),
|
|
103
|
+
"label": label,
|
|
104
|
+
"position": {"row": row, "col": col, "row_span": row_span, "col_span": col_span},
|
|
105
|
+
"bbox_figure": _get_axes_bbox(ax, fig),
|
|
106
|
+
"child_elements": child_geometry.get("elements", []),
|
|
107
|
+
}
|
|
108
|
+
geometry["panels"].append(panel_geometry)
|
|
109
|
+
|
|
110
|
+
plt.tight_layout()
|
|
111
|
+
|
|
112
|
+
return fig, geometry
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def render_child_in_axes(
|
|
116
|
+
ax: "MplAxes",
|
|
117
|
+
child: "FTS",
|
|
118
|
+
theme: Optional["Theme"] = None,
|
|
119
|
+
) -> Dict:
|
|
120
|
+
"""Render child FTS into axes.
|
|
121
|
+
|
|
122
|
+
ALWAYS re-renders from canonical (exports = optional cache only):
|
|
123
|
+
- kind=plot: Re-render from canonical/spec.json + payload/data.csv
|
|
124
|
+
- kind=figure: Recursively render children (NOT use exports/figure.png)
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
ax: Matplotlib axes to render into
|
|
128
|
+
child: Child FTS bundle
|
|
129
|
+
theme: Theme to apply (overrides child's theme)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Geometry data for this child
|
|
133
|
+
"""
|
|
134
|
+
geometry = {"elements": []}
|
|
135
|
+
|
|
136
|
+
if child.node.is_leaf_kind():
|
|
137
|
+
# Leaf node: render from encoding + payload
|
|
138
|
+
geometry = _render_leaf_in_axes(ax, child, theme)
|
|
139
|
+
elif child.node.is_composite_kind():
|
|
140
|
+
# Composite: recursive render (nested figure)
|
|
141
|
+
geometry = _render_composite_in_axes(ax, child, theme)
|
|
142
|
+
|
|
143
|
+
return geometry
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _render_leaf_in_axes(
|
|
147
|
+
ax: "MplAxes",
|
|
148
|
+
child: "FTS",
|
|
149
|
+
theme: Optional["Theme"] = None,
|
|
150
|
+
) -> Dict:
|
|
151
|
+
"""Render leaf FTS (plot/table/stats) into axes.
|
|
152
|
+
|
|
153
|
+
Uses child's pre-rendered export (artifacts/exports/figure.png) to embed
|
|
154
|
+
the visualization. This approach ensures visual consistency with the
|
|
155
|
+
original rendered plot.
|
|
156
|
+
"""
|
|
157
|
+
import io
|
|
158
|
+
|
|
159
|
+
import matplotlib.pyplot as plt
|
|
160
|
+
|
|
161
|
+
geometry = {"elements": []}
|
|
162
|
+
|
|
163
|
+
# Try to use pre-rendered export image
|
|
164
|
+
try:
|
|
165
|
+
storage = child.storage
|
|
166
|
+
|
|
167
|
+
# Check for exported image
|
|
168
|
+
if storage.exists("artifacts/exports/figure.png"):
|
|
169
|
+
img_data = storage.read("artifacts/exports/figure.png")
|
|
170
|
+
img = plt.imread(io.BytesIO(img_data), format="png")
|
|
171
|
+
|
|
172
|
+
# Display image in axes, filling the entire axes
|
|
173
|
+
ax.imshow(img, aspect="auto", extent=[0, 1, 0, 1])
|
|
174
|
+
ax.set_xlim(0, 1)
|
|
175
|
+
ax.set_ylim(0, 1)
|
|
176
|
+
ax.axis("off")
|
|
177
|
+
|
|
178
|
+
geometry["elements"].append({
|
|
179
|
+
"type": "embedded_image",
|
|
180
|
+
"source": "artifacts/exports/figure.png",
|
|
181
|
+
})
|
|
182
|
+
return geometry
|
|
183
|
+
|
|
184
|
+
except Exception as e:
|
|
185
|
+
pass # Fall through to placeholder
|
|
186
|
+
|
|
187
|
+
# Fallback: draw placeholder with child name
|
|
188
|
+
ax.text(
|
|
189
|
+
0.5,
|
|
190
|
+
0.5,
|
|
191
|
+
f"[{child.node.name or child.node.id[:8]}]",
|
|
192
|
+
ha="center",
|
|
193
|
+
va="center",
|
|
194
|
+
transform=ax.transAxes,
|
|
195
|
+
fontsize=10,
|
|
196
|
+
)
|
|
197
|
+
ax.set_xlim(0, 1)
|
|
198
|
+
ax.set_ylim(0, 1)
|
|
199
|
+
|
|
200
|
+
return geometry
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _render_composite_in_axes(
|
|
204
|
+
ax: "MplAxes",
|
|
205
|
+
child: "FTS",
|
|
206
|
+
theme: Optional["Theme"] = None,
|
|
207
|
+
) -> Dict:
|
|
208
|
+
"""Render composite FTS (figure) into axes.
|
|
209
|
+
|
|
210
|
+
Creates a nested gridspec for the child's children.
|
|
211
|
+
"""
|
|
212
|
+
from .._bundle._children import load_embedded_children
|
|
213
|
+
|
|
214
|
+
geometry = {"elements": [], "nested_panels": []}
|
|
215
|
+
|
|
216
|
+
# Load child's children
|
|
217
|
+
child_children = child.load_children()
|
|
218
|
+
if not child_children:
|
|
219
|
+
ax.text(
|
|
220
|
+
0.5,
|
|
221
|
+
0.5,
|
|
222
|
+
f"[Empty: {child.node.name or child.node.id[:8]}]",
|
|
223
|
+
ha="center",
|
|
224
|
+
va="center",
|
|
225
|
+
transform=ax.transAxes,
|
|
226
|
+
)
|
|
227
|
+
return geometry
|
|
228
|
+
|
|
229
|
+
# Get child's layout
|
|
230
|
+
child_layout = child.node.layout or {"rows": 1, "cols": 1, "panels": []}
|
|
231
|
+
|
|
232
|
+
# For nested figures, we need to subdivide the axes
|
|
233
|
+
# This is a simplified approach - full implementation would use inset axes
|
|
234
|
+
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
235
|
+
|
|
236
|
+
ax.axis("off") # Hide the parent axes
|
|
237
|
+
|
|
238
|
+
# Create a figure-in-figure effect using inset axes
|
|
239
|
+
# For simplicity, render children side by side
|
|
240
|
+
n_children = len(child_children)
|
|
241
|
+
width = 1.0 / max(n_children, 1)
|
|
242
|
+
|
|
243
|
+
for i, (grandchild_name, grandchild) in enumerate(child_children.items()):
|
|
244
|
+
# Create inset axes
|
|
245
|
+
inset_bounds = [width * i, 0, width * 0.95, 1]
|
|
246
|
+
inset_ax = ax.inset_axes(inset_bounds)
|
|
247
|
+
|
|
248
|
+
# Recursively render grandchild
|
|
249
|
+
grandchild_geometry = render_child_in_axes(inset_ax, grandchild, theme)
|
|
250
|
+
|
|
251
|
+
geometry["nested_panels"].append(
|
|
252
|
+
{
|
|
253
|
+
"child": grandchild_name,
|
|
254
|
+
"child_id": grandchild.node.id if grandchild.node else None,
|
|
255
|
+
"elements": grandchild_geometry.get("elements", []),
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return geometry
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _add_panel_label(ax: "MplAxes", label: str) -> None:
|
|
263
|
+
"""Add panel label (A, B, C...) to axes."""
|
|
264
|
+
ax.text(
|
|
265
|
+
-0.1,
|
|
266
|
+
1.05,
|
|
267
|
+
label,
|
|
268
|
+
transform=ax.transAxes,
|
|
269
|
+
fontsize=14,
|
|
270
|
+
fontweight="bold",
|
|
271
|
+
va="bottom",
|
|
272
|
+
ha="right",
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _get_axes_bbox(ax: "MplAxes", fig: "MplFigure") -> Dict[str, float]:
|
|
277
|
+
"""Get axes bounding box in figure coordinates."""
|
|
278
|
+
bbox = ax.get_position()
|
|
279
|
+
return {
|
|
280
|
+
"x": bbox.x0,
|
|
281
|
+
"y": bbox.y0,
|
|
282
|
+
"width": bbox.width,
|
|
283
|
+
"height": bbox.height,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def check_cache_valid(
|
|
288
|
+
child: "FTS",
|
|
289
|
+
current_theme_hash: str,
|
|
290
|
+
renderer_version: str = "1.0.0",
|
|
291
|
+
) -> bool:
|
|
292
|
+
"""Check if child's cached exports are still valid.
|
|
293
|
+
|
|
294
|
+
Cache is valid only if ALL match:
|
|
295
|
+
1. artifacts/exports/figure.png exists
|
|
296
|
+
2. artifacts/cache/render_manifest.json exists
|
|
297
|
+
3. render_manifest.canonical_hash matches current hash
|
|
298
|
+
4. render_manifest.effective_theme_hash matches current theme
|
|
299
|
+
5. render_manifest.renderer_version matches current renderer
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
child: Child FTS bundle
|
|
303
|
+
current_theme_hash: Hash of current effective theme
|
|
304
|
+
renderer_version: Current renderer version
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
True if cache is valid and can be used
|
|
308
|
+
"""
|
|
309
|
+
from .._bundle._storage import get_storage
|
|
310
|
+
from .._bundle._saver import compute_canonical_hash
|
|
311
|
+
|
|
312
|
+
storage = get_storage(child.path)
|
|
313
|
+
|
|
314
|
+
# Check exports exist
|
|
315
|
+
if not storage.exists("artifacts/exports/figure.png"):
|
|
316
|
+
return False
|
|
317
|
+
|
|
318
|
+
# Check manifest exists
|
|
319
|
+
manifest = storage.read_json("artifacts/cache/render_manifest.json")
|
|
320
|
+
if manifest is None:
|
|
321
|
+
return False
|
|
322
|
+
|
|
323
|
+
# Check canonical hash
|
|
324
|
+
current_canonical_hash = compute_canonical_hash(storage)
|
|
325
|
+
if manifest.get("canonical_hash") != current_canonical_hash:
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
# Check theme hash
|
|
329
|
+
if manifest.get("effective_theme_hash") != current_theme_hash:
|
|
330
|
+
return False
|
|
331
|
+
|
|
332
|
+
# Check renderer version
|
|
333
|
+
if manifest.get("renderer_version") != renderer_version:
|
|
334
|
+
return False
|
|
335
|
+
|
|
336
|
+
return True
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
__all__ = [
|
|
340
|
+
"render_composite",
|
|
341
|
+
"render_child_in_axes",
|
|
342
|
+
"check_cache_valid",
|
|
343
|
+
]
|
|
344
|
+
|
|
345
|
+
# EOF
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-21
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fts/_kinds/_plot/__init__.py
|
|
4
|
+
|
|
5
|
+
"""Plot kind - Data visualization with encoding.
|
|
6
|
+
|
|
7
|
+
A plot bundle contains data (payload/data.csv) and an encoding
|
|
8
|
+
specification that maps data columns to visual channels (x, y, color, etc.).
|
|
9
|
+
|
|
10
|
+
Structure:
|
|
11
|
+
- payload/data.csv: Source data
|
|
12
|
+
- canonical/encoding.json: Data-to-visual mappings
|
|
13
|
+
- canonical/theme.json: Visual styling
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from ._dataclasses import Encoding, Theme
|
|
17
|
+
from ._backend._render import render_traces
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Encoding",
|
|
21
|
+
"Theme",
|
|
22
|
+
"render_traces",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# EOF
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# File: ./src/scitex/vis/backend/__init__.py
|
|
3
|
+
"""
|
|
4
|
+
Backend for rendering figure JSON to matplotlib figures.
|
|
5
|
+
|
|
6
|
+
This module bridges the gap between JSON specifications and
|
|
7
|
+
actual matplotlib figures using scitex.plt.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from ._export import (
|
|
11
|
+
export_figure,
|
|
12
|
+
export_figure_from_file,
|
|
13
|
+
export_multiple_formats,
|
|
14
|
+
)
|
|
15
|
+
from ._parser import (
|
|
16
|
+
parse_annotation_json,
|
|
17
|
+
parse_axes_json,
|
|
18
|
+
parse_figure_json,
|
|
19
|
+
parse_guide_json,
|
|
20
|
+
parse_plot_json,
|
|
21
|
+
validate_figure_json,
|
|
22
|
+
)
|
|
23
|
+
from ._render import (
|
|
24
|
+
build_figure_from_json,
|
|
25
|
+
render_annotation,
|
|
26
|
+
render_axes,
|
|
27
|
+
render_figure,
|
|
28
|
+
render_guide,
|
|
29
|
+
render_plot,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
# Parser
|
|
34
|
+
"parse_figure_json",
|
|
35
|
+
"parse_axes_json",
|
|
36
|
+
"parse_plot_json",
|
|
37
|
+
"parse_guide_json",
|
|
38
|
+
"parse_annotation_json",
|
|
39
|
+
"validate_figure_json",
|
|
40
|
+
# Renderer
|
|
41
|
+
"render_figure",
|
|
42
|
+
"render_axes",
|
|
43
|
+
"render_plot",
|
|
44
|
+
"render_guide",
|
|
45
|
+
"render_annotation",
|
|
46
|
+
"build_figure_from_json",
|
|
47
|
+
# Exporter
|
|
48
|
+
"export_figure",
|
|
49
|
+
"export_figure_from_file",
|
|
50
|
+
"export_multiple_formats",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
# EOF
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# File: ./src/scitex/vis/backend/export.py
|
|
3
|
+
"""Export figure models to image files via scitex.io."""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, Optional, Union
|
|
7
|
+
|
|
8
|
+
from ._render import build_figure_from_json
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def export_figure(
|
|
12
|
+
fig_json: Dict[str, Any],
|
|
13
|
+
output_path: Union[str, Path],
|
|
14
|
+
fmt: Optional[str] = None,
|
|
15
|
+
dpi: int = 300,
|
|
16
|
+
auto_crop: bool = False,
|
|
17
|
+
**kwargs,
|
|
18
|
+
) -> Path:
|
|
19
|
+
"""
|
|
20
|
+
Export figure JSON to image file.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
fig_json : Dict[str, Any]
|
|
25
|
+
Figure JSON specification
|
|
26
|
+
output_path : str or Path
|
|
27
|
+
Output file path
|
|
28
|
+
fmt : str, optional
|
|
29
|
+
Output format ("png", "pdf", "svg", etc.)
|
|
30
|
+
If None, inferred from output_path extension
|
|
31
|
+
dpi : int, optional
|
|
32
|
+
Resolution in dots per inch (default: 300)
|
|
33
|
+
auto_crop : bool, optional
|
|
34
|
+
Automatically crop whitespace (default: False)
|
|
35
|
+
**kwargs
|
|
36
|
+
Additional keyword arguments passed to scitex.io.save()
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
Path
|
|
41
|
+
Path to the saved file
|
|
42
|
+
|
|
43
|
+
Examples
|
|
44
|
+
--------
|
|
45
|
+
>>> fig_json = {"width_mm": 180, "height_mm": 120, ...}
|
|
46
|
+
>>> export_figure(fig_json, "output.png", dpi=300)
|
|
47
|
+
PosixPath('output.png')
|
|
48
|
+
|
|
49
|
+
>>> # Auto-crop for publication
|
|
50
|
+
>>> export_figure(fig_json, "figure.pdf", auto_crop=True)
|
|
51
|
+
PosixPath('figure.pdf')
|
|
52
|
+
"""
|
|
53
|
+
import scitex as stx
|
|
54
|
+
|
|
55
|
+
output_path = Path(output_path)
|
|
56
|
+
|
|
57
|
+
# Build figure from JSON
|
|
58
|
+
fig, axes = build_figure_from_json(fig_json)
|
|
59
|
+
|
|
60
|
+
# Save using scitex.io
|
|
61
|
+
save_kwargs = {"dpi": dpi, "auto_crop": auto_crop}
|
|
62
|
+
save_kwargs.update(kwargs)
|
|
63
|
+
|
|
64
|
+
if fmt:
|
|
65
|
+
save_kwargs["fmt"] = fmt
|
|
66
|
+
|
|
67
|
+
stx.io.save(fig, output_path, **save_kwargs)
|
|
68
|
+
|
|
69
|
+
return output_path
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def export_figure_from_file(
|
|
73
|
+
json_path: Union[str, Path],
|
|
74
|
+
output_path: Union[str, Path],
|
|
75
|
+
**kwargs,
|
|
76
|
+
) -> Path:
|
|
77
|
+
"""
|
|
78
|
+
Export figure from JSON file to image file.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
json_path : str or Path
|
|
83
|
+
Path to figure JSON file
|
|
84
|
+
output_path : str or Path
|
|
85
|
+
Output image file path
|
|
86
|
+
**kwargs
|
|
87
|
+
Additional keyword arguments passed to export_figure()
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
Path
|
|
92
|
+
Path to the saved file
|
|
93
|
+
|
|
94
|
+
Examples
|
|
95
|
+
--------
|
|
96
|
+
>>> export_figure_from_file("figure.json", "figure.png")
|
|
97
|
+
PosixPath('figure.png')
|
|
98
|
+
"""
|
|
99
|
+
import scitex as stx
|
|
100
|
+
|
|
101
|
+
json_path = Path(json_path)
|
|
102
|
+
|
|
103
|
+
# Load JSON
|
|
104
|
+
fig_json = stx.io.load(json_path)
|
|
105
|
+
|
|
106
|
+
# Export
|
|
107
|
+
return export_figure(fig_json, output_path, **kwargs)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def export_multiple_formats(
|
|
111
|
+
fig_json: Dict[str, Any],
|
|
112
|
+
output_dir: Union[str, Path],
|
|
113
|
+
base_name: str,
|
|
114
|
+
formats: list = None,
|
|
115
|
+
**kwargs,
|
|
116
|
+
) -> Dict[str, Path]:
|
|
117
|
+
"""
|
|
118
|
+
Export figure to multiple formats.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
fig_json : Dict[str, Any]
|
|
123
|
+
Figure JSON specification
|
|
124
|
+
output_dir : str or Path
|
|
125
|
+
Output directory
|
|
126
|
+
base_name : str
|
|
127
|
+
Base filename (without extension)
|
|
128
|
+
formats : list, optional
|
|
129
|
+
List of formats (default: ["png", "pdf", "svg"])
|
|
130
|
+
**kwargs
|
|
131
|
+
Additional keyword arguments passed to export_figure()
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
Dict[str, Path]
|
|
136
|
+
Dictionary mapping format to output path
|
|
137
|
+
|
|
138
|
+
Examples
|
|
139
|
+
--------
|
|
140
|
+
>>> fig_json = {...}
|
|
141
|
+
>>> paths = export_multiple_formats(
|
|
142
|
+
... fig_json,
|
|
143
|
+
... "output",
|
|
144
|
+
... "figure-01",
|
|
145
|
+
... formats=["png", "pdf"]
|
|
146
|
+
... )
|
|
147
|
+
>>> paths
|
|
148
|
+
{'png': PosixPath('output/figure-01.png'),
|
|
149
|
+
'pdf': PosixPath('output/figure-01.pdf')}
|
|
150
|
+
"""
|
|
151
|
+
if formats is None:
|
|
152
|
+
formats = ["png", "pdf", "svg"]
|
|
153
|
+
|
|
154
|
+
output_dir = Path(output_dir)
|
|
155
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
156
|
+
|
|
157
|
+
results = {}
|
|
158
|
+
for fmt in formats:
|
|
159
|
+
output_path = output_dir / f"{base_name}.{fmt}"
|
|
160
|
+
results[fmt] = export_figure(fig_json, output_path, fmt=fmt, **kwargs)
|
|
161
|
+
|
|
162
|
+
return results
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# EOF
|