scitex 2.7.0__py3-none-any.whl → 2.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scitex/__init__.py +6 -2
- scitex/__version__.py +1 -1
- scitex/audio/README.md +52 -0
- scitex/audio/__init__.py +384 -0
- scitex/audio/__main__.py +129 -0
- scitex/audio/_tts.py +334 -0
- scitex/audio/engines/__init__.py +44 -0
- scitex/audio/engines/base.py +275 -0
- scitex/audio/engines/elevenlabs_engine.py +143 -0
- scitex/audio/engines/gtts_engine.py +162 -0
- scitex/audio/engines/pyttsx3_engine.py +131 -0
- scitex/audio/mcp_server.py +757 -0
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_vis.py +1 -1
- scitex/dev/plt/__init__.py +272 -0
- scitex/dev/plt/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/plot_mpl_bar.py +29 -0
- scitex/dev/plt/plot_mpl_barh.py +29 -0
- scitex/dev/plt/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/plot_mpl_contour.py +31 -0
- scitex/dev/plt/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/plot_mpl_fill.py +30 -0
- scitex/dev/plt/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/plot_mpl_hist.py +28 -0
- scitex/dev/plt/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/plot_mpl_pie.py +29 -0
- scitex/dev/plt/plot_mpl_plot.py +29 -0
- scitex/dev/plt/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/plot_mpl_stem.py +29 -0
- scitex/dev/plt/plot_mpl_step.py +29 -0
- scitex/dev/plt/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/plot_sns_barplot.py +29 -0
- scitex/dev/plt/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/plot_sns_histplot.py +29 -0
- scitex/dev/plt/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/plot_stx_bar.py +29 -0
- scitex/dev/plt/plot_stx_barh.py +29 -0
- scitex/dev/plt/plot_stx_box.py +28 -0
- scitex/dev/plt/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/plot_stx_contour.py +31 -0
- scitex/dev/plt/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/plot_stx_fillv.py +28 -0
- scitex/dev/plt/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/plot_stx_image.py +28 -0
- scitex/dev/plt/plot_stx_imshow.py +28 -0
- scitex/dev/plt/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/plot_stx_kde.py +28 -0
- scitex/dev/plt/plot_stx_line.py +28 -0
- scitex/dev/plt/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/plot_stx_raster.py +28 -0
- scitex/dev/plt/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/plot_stx_scatter.py +29 -0
- scitex/dev/plt/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/plot_stx_violin.py +28 -0
- scitex/dev/plt/plot_stx_violinplot.py +28 -0
- scitex/diagram/README.md +197 -0
- scitex/diagram/__init__.py +48 -0
- scitex/diagram/_compile.py +312 -0
- scitex/diagram/_diagram.py +355 -0
- scitex/diagram/_presets.py +173 -0
- scitex/diagram/_schema.py +182 -0
- scitex/diagram/_split.py +278 -0
- scitex/fig/__init__.py +352 -0
- scitex/{vis → fig}/backend/_parser.py +1 -1
- scitex/{vis → fig}/canvas.py +1 -1
- scitex/{vis → fig}/editor/__init__.py +5 -2
- scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
- scitex/{vis → fig}/editor/_defaults.py +70 -5
- scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
- scitex/{vis → fig}/editor/_qt_editor.py +182 -2
- scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
- scitex/fig/editor/edit/__init__.py +50 -0
- scitex/fig/editor/edit/backend_detector.py +109 -0
- scitex/fig/editor/edit/bundle_resolver.py +240 -0
- scitex/fig/editor/edit/editor_launcher.py +239 -0
- scitex/fig/editor/edit/manual_handler.py +53 -0
- scitex/fig/editor/edit/panel_loader.py +232 -0
- scitex/fig/editor/edit/path_resolver.py +67 -0
- scitex/fig/editor/flask_editor/_bbox.py +1299 -0
- scitex/fig/editor/flask_editor/_core.py +1429 -0
- scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
- scitex/fig/editor/flask_editor/_renderer.py +813 -0
- scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
- scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
- scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
- scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
- scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
- scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
- scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
- scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
- scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
- scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
- scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
- scitex/fig/editor/flask_editor/static/css/index.css +31 -0
- scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
- scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
- scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
- scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
- scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
- scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
- scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
- scitex/fig/editor/flask_editor/static/js/main.js +426 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
- scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
- scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
- scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
- scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
- scitex/fig/editor/flask_editor/templates/_html.py +852 -0
- scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
- scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
- scitex/{vis → fig}/io/__init__.py +13 -1
- scitex/fig/io/_bundle.py +1058 -0
- scitex/{vis → fig}/io/_canvas.py +1 -1
- scitex/{vis → fig}/io/_data.py +1 -1
- scitex/{vis → fig}/io/_export.py +1 -1
- scitex/{vis → fig}/io/_load.py +1 -1
- scitex/{vis → fig}/io/_panel.py +1 -1
- scitex/{vis → fig}/io/_save.py +1 -1
- scitex/{vis → fig}/model/__init__.py +1 -1
- scitex/{vis → fig}/model/_annotations.py +1 -1
- scitex/{vis → fig}/model/_axes.py +1 -1
- scitex/{vis → fig}/model/_figure.py +1 -1
- scitex/{vis → fig}/model/_guides.py +1 -1
- scitex/{vis → fig}/model/_plot.py +1 -1
- scitex/{vis → fig}/model/_styles.py +1 -1
- scitex/{vis → fig}/utils/__init__.py +1 -1
- scitex/io/__init__.py +22 -26
- scitex/io/_bundle.py +493 -0
- scitex/io/_flush.py +5 -2
- scitex/io/_load.py +98 -0
- scitex/io/_load_modules/_H5Explorer.py +5 -2
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_image.py +3 -4
- scitex/io/_load_modules/_txt.py +4 -2
- scitex/io/_metadata.py +34 -324
- scitex/io/_metadata_modules/__init__.py +46 -0
- scitex/io/_metadata_modules/_embed.py +70 -0
- scitex/io/_metadata_modules/_read.py +64 -0
- scitex/io/_metadata_modules/_utils.py +79 -0
- scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
- scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
- scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
- scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
- scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
- scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
- scitex/io/_metadata_modules/read_metadata_png.py +39 -0
- scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
- scitex/io/_qr_utils.py +5 -3
- scitex/io/_save.py +548 -30
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_image.py +5 -9
- scitex/io/_save_modules/_tex.py +7 -4
- scitex/io/_zip_bundle.py +439 -0
- scitex/io/utils/h5_to_zarr.py +11 -9
- scitex/msword/__init__.py +255 -0
- scitex/msword/profiles.py +357 -0
- scitex/msword/reader.py +753 -0
- scitex/msword/utils.py +289 -0
- scitex/msword/writer.py +362 -0
- scitex/plt/__init__.py +5 -2
- scitex/plt/_subplots/_AxesWrapper.py +6 -6
- scitex/plt/_subplots/_AxisWrapper.py +15 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
- scitex/plt/_subplots/_FigWrapper.py +6 -6
- scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
- scitex/plt/_subplots/_export_as_csv.py +35 -5
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
- scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
- scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
- scitex/plt/gallery/_generate.py +421 -14
- scitex/plt/io/__init__.py +53 -0
- scitex/plt/io/_bundle.py +490 -0
- scitex/plt/io/_layered_bundle.py +1343 -0
- scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
- scitex/plt/styles/__init__.py +14 -0
- scitex/plt/styles/presets.py +78 -0
- scitex/plt/utils/__init__.py +13 -1
- scitex/plt/utils/_collect_figure_metadata.py +10 -14
- scitex/plt/utils/_configure_mpl.py +6 -18
- scitex/plt/utils/_crop.py +32 -14
- scitex/plt/utils/_csv_column_naming.py +54 -0
- scitex/plt/utils/_figure_mm.py +116 -1
- scitex/plt/utils/_hitmap.py +1643 -0
- scitex/plt/utils/metadata/__init__.py +25 -0
- scitex/plt/utils/metadata/_core.py +9 -10
- scitex/plt/utils/metadata/_dimensions.py +6 -3
- scitex/plt/utils/metadata/_editable_export.py +405 -0
- scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
- scitex/schema/__init__.py +109 -16
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_plot.py +1015 -0
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +117 -0
- scitex/stats/io/__init__.py +29 -0
- scitex/stats/io/_bundle.py +156 -0
- scitex/tex/__init__.py +4 -0
- scitex/tex/_export.py +890 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
- scitex/io/memo.md +0 -2827
- scitex/plt/REQUESTS.md +0 -191
- scitex/plt/_subplots/TODO.md +0 -53
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
- scitex/vis/__init__.py +0 -177
- scitex/vis/editor/_edit.py +0 -390
- scitex/vis/editor/flask_editor/_bbox.py +0 -529
- scitex/vis/editor/flask_editor/_core.py +0 -168
- scitex/vis/editor/flask_editor/_renderer.py +0 -393
- scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
- scitex/vis/editor/flask_editor/templates/_html.py +0 -513
- scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
- scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
- /scitex/{vis → fig}/README.md +0 -0
- /scitex/{vis → fig}/backend/__init__.py +0 -0
- /scitex/{vis → fig}/backend/_export.py +0 -0
- /scitex/{vis → fig}/backend/_render.py +0 -0
- /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
- /scitex/{vis → fig}/io/_directory.py +0 -0
- /scitex/{vis → fig}/model/_plot_types.py +0 -0
- /scitex/{vis → fig}/utils/_defaults.py +0 -0
- /scitex/{vis → fig}/utils/_validate.py +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,6 +18,8 @@ assert_csv_json_consistency : function
|
|
|
18
18
|
Assert CSV columns match JSON metadata
|
|
19
19
|
verify_csv_json_consistency : function
|
|
20
20
|
Verify CSV-JSON consistency and return detailed results
|
|
21
|
+
export_editable_figure : function
|
|
22
|
+
Export figure with geometry data for interactive editing (schema v0.3)
|
|
21
23
|
"""
|
|
22
24
|
|
|
23
25
|
# Import public API from core module
|
|
@@ -27,10 +29,33 @@ from ._data_linkage import (
|
|
|
27
29
|
verify_csv_json_consistency,
|
|
28
30
|
collect_recipe_metadata,
|
|
29
31
|
)
|
|
32
|
+
from ._geometry_extraction import (
|
|
33
|
+
extract_axes_bbox_px,
|
|
34
|
+
data_to_axes_px,
|
|
35
|
+
extract_line_geometry,
|
|
36
|
+
extract_scatter_geometry,
|
|
37
|
+
extract_polygon_geometry,
|
|
38
|
+
extract_rectangle_geometry,
|
|
39
|
+
extract_bar_group_geometry,
|
|
40
|
+
extract_text_geometry,
|
|
41
|
+
extract_image_geometry,
|
|
42
|
+
)
|
|
43
|
+
from ._editable_export import export_editable_figure
|
|
30
44
|
|
|
31
45
|
__all__ = [
|
|
32
46
|
"collect_figure_metadata",
|
|
33
47
|
"collect_recipe_metadata",
|
|
34
48
|
"assert_csv_json_consistency",
|
|
35
49
|
"verify_csv_json_consistency",
|
|
50
|
+
"export_editable_figure",
|
|
51
|
+
# Geometry extraction
|
|
52
|
+
"extract_axes_bbox_px",
|
|
53
|
+
"data_to_axes_px",
|
|
54
|
+
"extract_line_geometry",
|
|
55
|
+
"extract_scatter_geometry",
|
|
56
|
+
"extract_polygon_geometry",
|
|
57
|
+
"extract_rectangle_geometry",
|
|
58
|
+
"extract_bar_group_geometry",
|
|
59
|
+
"extract_text_geometry",
|
|
60
|
+
"extract_image_geometry",
|
|
36
61
|
]
|
|
@@ -12,6 +12,10 @@ the collection of all metadata from matplotlib figures and axes.
|
|
|
12
12
|
from typing import Dict
|
|
13
13
|
import numpy as np
|
|
14
14
|
|
|
15
|
+
from scitex import logging
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
15
19
|
# Import sub-modules
|
|
16
20
|
from ._figure_metadata import _initialize_metadata_structure, _collect_figure_metadata
|
|
17
21
|
from ._axes_metadata import _collect_all_axes_metadata
|
|
@@ -87,16 +91,14 @@ def collect_figure_metadata(fig, ax=None) -> Dict:
|
|
|
87
91
|
try:
|
|
88
92
|
metadata["figure"] = _extract_figure_dimensions(fig)
|
|
89
93
|
except Exception as e:
|
|
90
|
-
|
|
91
|
-
warnings.warn(f"Could not extract figure dimension info: {e}")
|
|
94
|
+
logger.warning(f"Could not extract figure dimension info: {e}")
|
|
92
95
|
|
|
93
96
|
# Collect axes metadata
|
|
94
97
|
if all_axes:
|
|
95
98
|
try:
|
|
96
99
|
metadata["axes"] = _collect_all_axes_metadata(all_axes, fig, grid_shape)
|
|
97
100
|
except Exception as e:
|
|
98
|
-
|
|
99
|
-
warnings.warn(f"Could not extract axes metadata: {e}")
|
|
101
|
+
logger.warning(f"Could not extract axes metadata: {e}")
|
|
100
102
|
|
|
101
103
|
# Extract style metadata
|
|
102
104
|
try:
|
|
@@ -136,10 +138,8 @@ def collect_figure_metadata(fig, ax=None) -> Dict:
|
|
|
136
138
|
metadata["style"]["global"]["fonts"]["family_actual"] = actual_font
|
|
137
139
|
|
|
138
140
|
if requested_font != actual_font:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
f"Font mismatch: Requested '{requested_font}' but using '{actual_font}'",
|
|
142
|
-
UserWarning,
|
|
141
|
+
logger.warning(
|
|
142
|
+
f"Font mismatch: Requested '{requested_font}' but using '{actual_font}'"
|
|
143
143
|
)
|
|
144
144
|
else:
|
|
145
145
|
metadata["runtime"]["font_family_actual"] = actual_font
|
|
@@ -199,8 +199,7 @@ def collect_figure_metadata(fig, ax=None) -> Dict:
|
|
|
199
199
|
pass # Data hash is optional
|
|
200
200
|
|
|
201
201
|
except Exception as e:
|
|
202
|
-
|
|
203
|
-
warnings.warn(f"Could not extract plot content: {e}")
|
|
202
|
+
logger.warning(f"Could not extract plot content: {e}")
|
|
204
203
|
|
|
205
204
|
# Apply precision rounding
|
|
206
205
|
metadata = _round_metadata(metadata)
|
|
@@ -9,9 +9,12 @@ This module provides functions to extract size, position, and bounding box
|
|
|
9
9
|
information from matplotlib figures and axes in multiple units (mm, inch, px).
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import warnings
|
|
13
12
|
from typing import Dict
|
|
14
13
|
|
|
14
|
+
from scitex import logging
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
15
18
|
|
|
16
19
|
def _extract_axes_dimensions(fig, ax, ax_index: int) -> dict:
|
|
17
20
|
"""
|
|
@@ -80,7 +83,7 @@ def _extract_axes_dimensions(fig, ax, ax_index: int) -> dict:
|
|
|
80
83
|
ax_metadata["bbox_inch"] = _normalize_bbox(bbox)
|
|
81
84
|
|
|
82
85
|
except Exception as e:
|
|
83
|
-
|
|
86
|
+
logger.warning(f"Could not extract dimension info for axes {ax_index}: {e}")
|
|
84
87
|
|
|
85
88
|
return ax_metadata
|
|
86
89
|
|
|
@@ -190,7 +193,7 @@ def _extract_figure_dimensions(fig) -> dict:
|
|
|
190
193
|
fig_metadata["size_px"] = [width_inch * dpi, height_inch * dpi]
|
|
191
194
|
|
|
192
195
|
except Exception as e:
|
|
193
|
-
|
|
196
|
+
logger.warning(f"Could not extract figure dimensions: {e}")
|
|
194
197
|
|
|
195
198
|
return fig_metadata
|
|
196
199
|
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-12-10 (ywatanabe)"
|
|
4
|
+
# File: scitex/plt/utils/metadata/_editable_export.py
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Main export function for Schema v0.3 (scitex.plt.figure.editable).
|
|
8
|
+
|
|
9
|
+
This module provides the top-level export_editable_figure() function that
|
|
10
|
+
orchestrates geometry extraction for all elements in a matplotlib figure,
|
|
11
|
+
producing a JSON-serializable dictionary suitable for interactive editing.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Dict, Any, Optional, List
|
|
15
|
+
import numpy as np
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
|
|
18
|
+
from ._geometry_extraction import (
|
|
19
|
+
extract_axes_bbox_px,
|
|
20
|
+
extract_line_geometry,
|
|
21
|
+
extract_scatter_geometry,
|
|
22
|
+
extract_polygon_geometry,
|
|
23
|
+
extract_rectangle_geometry,
|
|
24
|
+
extract_bar_group_geometry,
|
|
25
|
+
extract_text_geometry,
|
|
26
|
+
extract_image_geometry,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def export_editable_figure(
|
|
31
|
+
fig,
|
|
32
|
+
title: str = "",
|
|
33
|
+
description: str = "",
|
|
34
|
+
include_full_paths: bool = False,
|
|
35
|
+
simplify_threshold: float = 0.5,
|
|
36
|
+
) -> Dict[str, Any]:
|
|
37
|
+
"""
|
|
38
|
+
Export a matplotlib figure with geometry data for interactive editing.
|
|
39
|
+
|
|
40
|
+
This produces a Schema v0.3 compliant dictionary with axes-local pixel
|
|
41
|
+
coordinates for all visual elements, suitable for shape-based hit testing.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
fig : matplotlib.figure.Figure or scitex.plt.FigureWrapper
|
|
46
|
+
The figure to export
|
|
47
|
+
title : str
|
|
48
|
+
Optional title for the figure metadata
|
|
49
|
+
description : str
|
|
50
|
+
Optional description for the figure metadata
|
|
51
|
+
include_full_paths : bool
|
|
52
|
+
If True, include full (unsimplified) paths for lines. Default False.
|
|
53
|
+
simplify_threshold : float
|
|
54
|
+
Maximum pixel error for path simplification. Default 0.5.
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
dict
|
|
59
|
+
Schema v0.3 compliant dictionary with structure:
|
|
60
|
+
- scitex_schema: "scitex.plt.figure.editable"
|
|
61
|
+
- scitex_schema_version: "0.3.0"
|
|
62
|
+
- figure: {size_px, dpi, ...}
|
|
63
|
+
- axes: {ax_id: {bbox_px, ...}, ...}
|
|
64
|
+
- elements: {elem_id: {geometry_px, style, ...}, ...}
|
|
65
|
+
- statistics: {} (placeholder for stats integration)
|
|
66
|
+
- data: {} (placeholder for linked data)
|
|
67
|
+
- output: {} (rendering metadata)
|
|
68
|
+
"""
|
|
69
|
+
# Handle scitex FigureWrapper
|
|
70
|
+
mpl_fig = fig.figure if hasattr(fig, 'figure') else fig
|
|
71
|
+
|
|
72
|
+
# Ensure we have a renderer
|
|
73
|
+
try:
|
|
74
|
+
renderer = mpl_fig.canvas.get_renderer()
|
|
75
|
+
except Exception:
|
|
76
|
+
mpl_fig.canvas.draw()
|
|
77
|
+
renderer = mpl_fig.canvas.get_renderer()
|
|
78
|
+
|
|
79
|
+
# Figure dimensions
|
|
80
|
+
fig_width_px = int(mpl_fig.get_figwidth() * mpl_fig.dpi)
|
|
81
|
+
fig_height_px = int(mpl_fig.get_figheight() * mpl_fig.dpi)
|
|
82
|
+
|
|
83
|
+
# Build output structure
|
|
84
|
+
output = {
|
|
85
|
+
"scitex_schema": "scitex.plt.figure.editable",
|
|
86
|
+
"scitex_schema_version": "0.3.0",
|
|
87
|
+
"meta": {
|
|
88
|
+
"title": title,
|
|
89
|
+
"description": description,
|
|
90
|
+
"exported_at": datetime.now().isoformat(),
|
|
91
|
+
},
|
|
92
|
+
"figure": {
|
|
93
|
+
"size_px": [fig_width_px, fig_height_px],
|
|
94
|
+
"dpi": mpl_fig.dpi,
|
|
95
|
+
"figsize_inches": [mpl_fig.get_figwidth(), mpl_fig.get_figheight()],
|
|
96
|
+
},
|
|
97
|
+
"axes": {},
|
|
98
|
+
"elements": {},
|
|
99
|
+
"statistics": {},
|
|
100
|
+
"data": {},
|
|
101
|
+
"output": {
|
|
102
|
+
"format": "png",
|
|
103
|
+
"width_px": fig_width_px,
|
|
104
|
+
"height_px": fig_height_px,
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Get all axes
|
|
109
|
+
axes_list = mpl_fig.get_axes()
|
|
110
|
+
|
|
111
|
+
for ax_idx, ax in enumerate(axes_list):
|
|
112
|
+
# Handle scitex AxisWrapper
|
|
113
|
+
mpl_ax = ax._axis_mpl if hasattr(ax, '_axis_mpl') else ax
|
|
114
|
+
|
|
115
|
+
ax_id = f"ax_{ax_idx:02d}"
|
|
116
|
+
|
|
117
|
+
# Extract axes bbox
|
|
118
|
+
axes_bbox = extract_axes_bbox_px(mpl_ax, mpl_fig)
|
|
119
|
+
output["axes"][ax_id] = {
|
|
120
|
+
"bbox_px": axes_bbox,
|
|
121
|
+
"position": list(mpl_ax.get_position().bounds),
|
|
122
|
+
"xlim": list(mpl_ax.get_xlim()),
|
|
123
|
+
"ylim": list(mpl_ax.get_ylim()),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Extract elements from this axes
|
|
127
|
+
elements = _extract_axes_elements(
|
|
128
|
+
mpl_ax, mpl_fig, ax_id, renderer,
|
|
129
|
+
include_full_paths=include_full_paths,
|
|
130
|
+
simplify_threshold=simplify_threshold,
|
|
131
|
+
)
|
|
132
|
+
output["elements"].update(elements)
|
|
133
|
+
|
|
134
|
+
return output
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _extract_axes_elements(
|
|
138
|
+
ax, fig, ax_id: str, renderer,
|
|
139
|
+
include_full_paths: bool = False,
|
|
140
|
+
simplify_threshold: float = 0.5,
|
|
141
|
+
) -> Dict[str, Any]:
|
|
142
|
+
"""
|
|
143
|
+
Extract all visual elements from a single axes.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
ax : matplotlib.axes.Axes
|
|
148
|
+
The axes object
|
|
149
|
+
fig : matplotlib.figure.Figure
|
|
150
|
+
The figure object
|
|
151
|
+
ax_id : str
|
|
152
|
+
Identifier for the axes (e.g., "ax_00")
|
|
153
|
+
renderer : RendererBase
|
|
154
|
+
Matplotlib renderer for text bbox extraction
|
|
155
|
+
include_full_paths : bool
|
|
156
|
+
Include unsimplified paths
|
|
157
|
+
simplify_threshold : float
|
|
158
|
+
Path simplification threshold in pixels
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
dict
|
|
163
|
+
Dictionary of {element_id: element_data}
|
|
164
|
+
"""
|
|
165
|
+
elements = {}
|
|
166
|
+
zorder_counter = 0
|
|
167
|
+
|
|
168
|
+
# Extract lines (Line2D)
|
|
169
|
+
for idx, line in enumerate(ax.lines):
|
|
170
|
+
elem_id = f"{ax_id}_line_{idx:02d}"
|
|
171
|
+
label = line.get_label()
|
|
172
|
+
if label is None or label.startswith('_'):
|
|
173
|
+
label = f"line_{idx}"
|
|
174
|
+
|
|
175
|
+
geom = extract_line_geometry(line, ax, fig, simplify_threshold)
|
|
176
|
+
|
|
177
|
+
elements[elem_id] = {
|
|
178
|
+
"id": elem_id,
|
|
179
|
+
"axes_id": ax_id,
|
|
180
|
+
"element_type": "line",
|
|
181
|
+
"label": label,
|
|
182
|
+
"geometry_px": geom,
|
|
183
|
+
"style": _extract_line_style(line),
|
|
184
|
+
"editable_styles": ["color", "linewidth", "linestyle", "alpha", "marker"],
|
|
185
|
+
"zorder": line.get_zorder(),
|
|
186
|
+
"visible": line.get_visible(),
|
|
187
|
+
}
|
|
188
|
+
zorder_counter += 1
|
|
189
|
+
|
|
190
|
+
# Extract collections (scatter, polygon fills, etc.)
|
|
191
|
+
for idx, coll in enumerate(ax.collections):
|
|
192
|
+
coll_type = type(coll).__name__
|
|
193
|
+
|
|
194
|
+
if coll_type == "PathCollection":
|
|
195
|
+
# Scatter plot
|
|
196
|
+
elem_id = f"{ax_id}_scatter_{idx:02d}"
|
|
197
|
+
label = coll.get_label()
|
|
198
|
+
if label is None or label.startswith('_'):
|
|
199
|
+
label = f"scatter_{idx}"
|
|
200
|
+
|
|
201
|
+
geom = extract_scatter_geometry(coll, ax, fig)
|
|
202
|
+
|
|
203
|
+
elements[elem_id] = {
|
|
204
|
+
"id": elem_id,
|
|
205
|
+
"axes_id": ax_id,
|
|
206
|
+
"element_type": "scatter",
|
|
207
|
+
"label": label,
|
|
208
|
+
"geometry_px": geom,
|
|
209
|
+
"style": _extract_scatter_style(coll),
|
|
210
|
+
"editable_styles": ["facecolor", "edgecolor", "s", "alpha", "marker"],
|
|
211
|
+
"zorder": coll.get_zorder(),
|
|
212
|
+
"visible": coll.get_visible(),
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
elif coll_type in ("PolyCollection", "FillBetweenPolyCollection"):
|
|
216
|
+
# Fill_between, violin, etc.
|
|
217
|
+
elem_id = f"{ax_id}_fill_{idx:02d}"
|
|
218
|
+
label = coll.get_label()
|
|
219
|
+
if label is None or label.startswith('_'):
|
|
220
|
+
label = f"fill_{idx}"
|
|
221
|
+
|
|
222
|
+
geom = extract_polygon_geometry(coll, ax, fig)
|
|
223
|
+
|
|
224
|
+
elements[elem_id] = {
|
|
225
|
+
"id": elem_id,
|
|
226
|
+
"axes_id": ax_id,
|
|
227
|
+
"element_type": "fill",
|
|
228
|
+
"label": label,
|
|
229
|
+
"geometry_px": geom,
|
|
230
|
+
"style": _extract_polygon_style(coll),
|
|
231
|
+
"editable_styles": ["facecolor", "edgecolor", "alpha"],
|
|
232
|
+
"zorder": coll.get_zorder(),
|
|
233
|
+
"visible": coll.get_visible(),
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
elif coll_type == "LineCollection":
|
|
237
|
+
# Multiple lines (e.g., errorbar caps)
|
|
238
|
+
elem_id = f"{ax_id}_linecoll_{idx:02d}"
|
|
239
|
+
elements[elem_id] = {
|
|
240
|
+
"id": elem_id,
|
|
241
|
+
"axes_id": ax_id,
|
|
242
|
+
"element_type": "line_collection",
|
|
243
|
+
"label": f"linecoll_{idx}",
|
|
244
|
+
"geometry_px": {"coord_space": "axes", "bbox": None},
|
|
245
|
+
"style": {},
|
|
246
|
+
"editable_styles": ["color", "linewidth", "alpha"],
|
|
247
|
+
"zorder": coll.get_zorder(),
|
|
248
|
+
"visible": coll.get_visible(),
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# Extract patches (bars, rectangles)
|
|
252
|
+
bar_patches = []
|
|
253
|
+
other_patches = []
|
|
254
|
+
|
|
255
|
+
for patch in ax.patches:
|
|
256
|
+
patch_type = type(patch).__name__
|
|
257
|
+
if patch_type == "Rectangle":
|
|
258
|
+
# Check if it's part of a bar plot (has nonzero width and height)
|
|
259
|
+
if patch.get_width() != 0 and patch.get_height() != 0:
|
|
260
|
+
bar_patches.append(patch)
|
|
261
|
+
else:
|
|
262
|
+
other_patches.append(patch)
|
|
263
|
+
else:
|
|
264
|
+
other_patches.append(patch)
|
|
265
|
+
|
|
266
|
+
# Group bar patches by similar x position (bars) or y position (horizontal bars)
|
|
267
|
+
if bar_patches:
|
|
268
|
+
elem_id = f"{ax_id}_bars"
|
|
269
|
+
geom = extract_bar_group_geometry(bar_patches, ax, fig)
|
|
270
|
+
|
|
271
|
+
elements[elem_id] = {
|
|
272
|
+
"id": elem_id,
|
|
273
|
+
"axes_id": ax_id,
|
|
274
|
+
"element_type": "bar",
|
|
275
|
+
"label": "bars",
|
|
276
|
+
"geometry_px": geom,
|
|
277
|
+
"style": _extract_bar_style(bar_patches[0] if bar_patches else None),
|
|
278
|
+
"editable_styles": ["facecolor", "edgecolor", "alpha", "linewidth"],
|
|
279
|
+
"zorder": bar_patches[0].get_zorder() if bar_patches else 0,
|
|
280
|
+
"visible": True,
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
# Extract images
|
|
284
|
+
for idx, img in enumerate(ax.images):
|
|
285
|
+
elem_id = f"{ax_id}_image_{idx:02d}"
|
|
286
|
+
geom = extract_image_geometry(img, ax, fig)
|
|
287
|
+
|
|
288
|
+
elements[elem_id] = {
|
|
289
|
+
"id": elem_id,
|
|
290
|
+
"axes_id": ax_id,
|
|
291
|
+
"element_type": "image",
|
|
292
|
+
"label": f"image_{idx}",
|
|
293
|
+
"geometry_px": geom,
|
|
294
|
+
"style": {"cmap": str(img.get_cmap().name), "alpha": img.get_alpha()},
|
|
295
|
+
"editable_styles": ["cmap", "alpha", "clim"],
|
|
296
|
+
"zorder": img.get_zorder(),
|
|
297
|
+
"visible": img.get_visible(),
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# Extract texts (excluding axis labels and title which are handled separately)
|
|
301
|
+
for idx, text in enumerate(ax.texts):
|
|
302
|
+
if not text.get_text():
|
|
303
|
+
continue
|
|
304
|
+
|
|
305
|
+
elem_id = f"{ax_id}_text_{idx:02d}"
|
|
306
|
+
geom = extract_text_geometry(text, ax, fig)
|
|
307
|
+
|
|
308
|
+
elements[elem_id] = {
|
|
309
|
+
"id": elem_id,
|
|
310
|
+
"axes_id": ax_id,
|
|
311
|
+
"element_type": "text",
|
|
312
|
+
"label": text.get_text()[:20],
|
|
313
|
+
"text": text.get_text(),
|
|
314
|
+
"geometry_px": geom,
|
|
315
|
+
"style": _extract_text_style(text),
|
|
316
|
+
"editable_styles": ["fontsize", "color", "fontweight", "alpha"],
|
|
317
|
+
"zorder": text.get_zorder(),
|
|
318
|
+
"visible": text.get_visible(),
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return elements
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _extract_line_style(line) -> Dict[str, Any]:
|
|
325
|
+
"""Extract style properties from a Line2D."""
|
|
326
|
+
color = line.get_color()
|
|
327
|
+
if isinstance(color, np.ndarray):
|
|
328
|
+
color = color.tolist()
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
"color": color,
|
|
332
|
+
"linewidth": line.get_linewidth(),
|
|
333
|
+
"linestyle": line.get_linestyle(),
|
|
334
|
+
"alpha": line.get_alpha(),
|
|
335
|
+
"marker": line.get_marker(),
|
|
336
|
+
"markersize": line.get_markersize(),
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _extract_scatter_style(coll) -> Dict[str, Any]:
|
|
341
|
+
"""Extract style properties from a PathCollection (scatter)."""
|
|
342
|
+
facecolors = coll.get_facecolors()
|
|
343
|
+
edgecolors = coll.get_edgecolors()
|
|
344
|
+
|
|
345
|
+
fc = facecolors[0].tolist() if len(facecolors) > 0 else [0, 0, 0, 1]
|
|
346
|
+
ec = edgecolors[0].tolist() if len(edgecolors) > 0 else [0, 0, 0, 1]
|
|
347
|
+
|
|
348
|
+
sizes = coll.get_sizes()
|
|
349
|
+
s = float(sizes[0]) if len(sizes) > 0 else 36
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
"facecolor": fc,
|
|
353
|
+
"edgecolor": ec,
|
|
354
|
+
"s": s,
|
|
355
|
+
"alpha": coll.get_alpha(),
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def _extract_polygon_style(coll) -> Dict[str, Any]:
|
|
360
|
+
"""Extract style properties from a PolyCollection."""
|
|
361
|
+
facecolors = coll.get_facecolors()
|
|
362
|
+
edgecolors = coll.get_edgecolors()
|
|
363
|
+
|
|
364
|
+
fc = facecolors[0].tolist() if len(facecolors) > 0 else [0, 0, 0, 0.3]
|
|
365
|
+
ec = edgecolors[0].tolist() if len(edgecolors) > 0 else [0, 0, 0, 1]
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
"facecolor": fc,
|
|
369
|
+
"edgecolor": ec,
|
|
370
|
+
"alpha": coll.get_alpha(),
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _extract_bar_style(patch) -> Dict[str, Any]:
|
|
375
|
+
"""Extract style properties from a Rectangle patch."""
|
|
376
|
+
if patch is None:
|
|
377
|
+
return {}
|
|
378
|
+
|
|
379
|
+
fc = patch.get_facecolor()
|
|
380
|
+
ec = patch.get_edgecolor()
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
"facecolor": list(fc) if hasattr(fc, '__iter__') else fc,
|
|
384
|
+
"edgecolor": list(ec) if hasattr(ec, '__iter__') else ec,
|
|
385
|
+
"linewidth": patch.get_linewidth(),
|
|
386
|
+
"alpha": patch.get_alpha(),
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _extract_text_style(text) -> Dict[str, Any]:
|
|
391
|
+
"""Extract style properties from a Text object."""
|
|
392
|
+
return {
|
|
393
|
+
"fontsize": text.get_fontsize(),
|
|
394
|
+
"color": text.get_color(),
|
|
395
|
+
"fontweight": text.get_fontweight(),
|
|
396
|
+
"fontstyle": text.get_fontstyle(),
|
|
397
|
+
"fontfamily": text.get_fontfamily(),
|
|
398
|
+
"alpha": text.get_alpha(),
|
|
399
|
+
"ha": text.get_ha(),
|
|
400
|
+
"va": text.get_va(),
|
|
401
|
+
"rotation": text.get_rotation(),
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
# EOF
|