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
scitex/plt/gallery/_generate.py
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
"""Gallery generation functionality."""
|
|
7
7
|
|
|
8
|
+
import json
|
|
8
9
|
import os
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
11
14
|
from ._plots import PLOT_FUNCTIONS
|
|
12
15
|
from ._registry import CATEGORIES
|
|
13
16
|
|
|
@@ -20,9 +23,11 @@ def generate(
|
|
|
20
23
|
dpi=150,
|
|
21
24
|
save_csv=True,
|
|
22
25
|
save_png=True,
|
|
26
|
+
save_svg=True,
|
|
27
|
+
save_pltz=True,
|
|
23
28
|
verbose=True,
|
|
24
29
|
):
|
|
25
|
-
"""Generate gallery plots with CSVs.
|
|
30
|
+
"""Generate gallery plots with CSVs and optional .pltz bundles.
|
|
26
31
|
|
|
27
32
|
Parameters
|
|
28
33
|
----------
|
|
@@ -42,6 +47,10 @@ def generate(
|
|
|
42
47
|
Whether to save CSV data files.
|
|
43
48
|
save_png : bool
|
|
44
49
|
Whether to save PNG image files.
|
|
50
|
+
save_svg : bool
|
|
51
|
+
Whether to save SVG image files for element selection.
|
|
52
|
+
save_pltz : bool
|
|
53
|
+
Whether to save .pltz bundles (reproducible plot packages).
|
|
45
54
|
verbose : bool
|
|
46
55
|
Print progress messages.
|
|
47
56
|
|
|
@@ -69,7 +78,7 @@ def generate(
|
|
|
69
78
|
if verbose:
|
|
70
79
|
print(f"Generating {len(plots_to_generate)} plots to {output_dir}")
|
|
71
80
|
|
|
72
|
-
results = {"png": [], "csv": [], "errors": []}
|
|
81
|
+
results = {"png": [], "svg": [], "csv": [], "pltz": [], "errors": []}
|
|
73
82
|
|
|
74
83
|
for plot_name in plots_to_generate:
|
|
75
84
|
if plot_name not in PLOT_FUNCTIONS:
|
|
@@ -93,20 +102,70 @@ def generate(
|
|
|
93
102
|
# Generate plot
|
|
94
103
|
fig, ax = plot_func(fig, ax, stx)
|
|
95
104
|
|
|
96
|
-
# Save
|
|
105
|
+
# Save as .pltz bundle - this is the single source of truth
|
|
106
|
+
# All other formats (PNG, SVG, CSV, hitmap) are extracted from the bundle
|
|
107
|
+
pltz_path = cat_dir / f"{plot_name}.pltz.d"
|
|
108
|
+
stx.io.save(fig, pltz_path, dpi=dpi)
|
|
109
|
+
|
|
110
|
+
if verbose:
|
|
111
|
+
print(f" [PLTZ] {pltz_path}")
|
|
112
|
+
|
|
113
|
+
# Extract files from bundle to gallery directory
|
|
114
|
+
# The pltz bundle contains: plot.png, plot.svg, plot.pdf, plot.csv, plot.json, plot_hitmap.png
|
|
115
|
+
import shutil
|
|
116
|
+
|
|
97
117
|
if save_png:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
118
|
+
bundle_png = pltz_path / "plot.png"
|
|
119
|
+
gallery_png = cat_dir / f"{plot_name}.png"
|
|
120
|
+
if bundle_png.exists():
|
|
121
|
+
shutil.copy(bundle_png, gallery_png)
|
|
122
|
+
results["png"].append(str(gallery_png))
|
|
123
|
+
if verbose:
|
|
124
|
+
from PIL import Image
|
|
125
|
+
with Image.open(gallery_png) as img:
|
|
126
|
+
print(f" [PNG] {gallery_png} ({img.size[0]}x{img.size[1]})")
|
|
127
|
+
|
|
128
|
+
if save_csv:
|
|
129
|
+
bundle_csv = pltz_path / "plot.csv"
|
|
130
|
+
gallery_csv = cat_dir / f"{plot_name}.csv"
|
|
131
|
+
if bundle_csv.exists():
|
|
132
|
+
shutil.copy(bundle_csv, gallery_csv)
|
|
133
|
+
results["csv"].append(str(gallery_csv))
|
|
134
|
+
if verbose:
|
|
135
|
+
print(f" [CSV] {gallery_csv}")
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
137
|
+
if save_svg:
|
|
138
|
+
bundle_svg = pltz_path / "plot.svg"
|
|
139
|
+
gallery_svg = cat_dir / f"{plot_name}.svg"
|
|
140
|
+
if bundle_svg.exists():
|
|
141
|
+
shutil.copy(bundle_svg, gallery_svg)
|
|
142
|
+
results["svg"].append(str(gallery_svg))
|
|
108
143
|
if verbose:
|
|
109
|
-
print(f" [
|
|
144
|
+
print(f" [SVG] {gallery_svg}")
|
|
145
|
+
|
|
146
|
+
# Copy hitmap from bundle
|
|
147
|
+
bundle_hitmap = pltz_path / "plot_hitmap.png"
|
|
148
|
+
gallery_hitmap = cat_dir / f"{plot_name}_hitmap.png"
|
|
149
|
+
if bundle_hitmap.exists():
|
|
150
|
+
shutil.copy(bundle_hitmap, gallery_hitmap)
|
|
151
|
+
if verbose:
|
|
152
|
+
from PIL import Image
|
|
153
|
+
with Image.open(gallery_hitmap) as img:
|
|
154
|
+
print(f" [HITMAP] {gallery_hitmap} ({img.size[0]}x{img.size[1]})")
|
|
155
|
+
|
|
156
|
+
# Copy and update JSON from bundle
|
|
157
|
+
bundle_json = pltz_path / "plot.json"
|
|
158
|
+
gallery_json = cat_dir / f"{plot_name}.json"
|
|
159
|
+
if bundle_json.exists():
|
|
160
|
+
shutil.copy(bundle_json, gallery_json)
|
|
161
|
+
# Add element_bboxes to the copied JSON
|
|
162
|
+
_add_element_bboxes_to_json(fig, ax, dpi, gallery_json, verbose)
|
|
163
|
+
|
|
164
|
+
if save_pltz:
|
|
165
|
+
results["pltz"].append(str(pltz_path))
|
|
166
|
+
else:
|
|
167
|
+
# Remove bundle if not requested
|
|
168
|
+
shutil.rmtree(pltz_path)
|
|
110
169
|
|
|
111
170
|
stx.plt.close(fig._fig_mpl if hasattr(fig, "_fig_mpl") else fig)
|
|
112
171
|
|
|
@@ -116,7 +175,7 @@ def generate(
|
|
|
116
175
|
print(f" [ERROR] {plot_name}: {e}")
|
|
117
176
|
|
|
118
177
|
if verbose:
|
|
119
|
-
print(f"\nGenerated: {len(results['png'])} PNG, {len(results['csv'])} CSV")
|
|
178
|
+
print(f"\nGenerated: {len(results['png'])} PNG, {len(results['svg'])} SVG, {len(results['csv'])} CSV, {len(results['pltz'])} PLTZ")
|
|
120
179
|
if results["errors"]:
|
|
121
180
|
print(f"Errors: {len(results['errors'])}")
|
|
122
181
|
|
|
@@ -150,4 +209,352 @@ def _get_category_for_plot(plot_name):
|
|
|
150
209
|
return "uncategorized"
|
|
151
210
|
|
|
152
211
|
|
|
212
|
+
def _add_element_bboxes_to_json(fig, ax, dpi, json_path, verbose=True):
|
|
213
|
+
"""Add element_bboxes to an existing JSON metadata file.
|
|
214
|
+
|
|
215
|
+
Extracts bounding boxes and path_simplified for all figure elements
|
|
216
|
+
to enable interactive element selection in web editors.
|
|
217
|
+
|
|
218
|
+
Parameters
|
|
219
|
+
----------
|
|
220
|
+
fig : FigureWrapper or matplotlib.figure.Figure
|
|
221
|
+
The figure object
|
|
222
|
+
ax : AxisWrapper or matplotlib.axes.Axes
|
|
223
|
+
The axes object
|
|
224
|
+
dpi : int
|
|
225
|
+
DPI used for saving
|
|
226
|
+
json_path : Path
|
|
227
|
+
Path to the JSON file to update
|
|
228
|
+
verbose : bool
|
|
229
|
+
Print progress messages
|
|
230
|
+
"""
|
|
231
|
+
from PIL import Image
|
|
232
|
+
from scitex.plt.utils.metadata._geometry_extraction import (
|
|
233
|
+
extract_axes_bbox_px,
|
|
234
|
+
extract_line_geometry,
|
|
235
|
+
extract_scatter_geometry,
|
|
236
|
+
extract_polygon_geometry,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
# Get matplotlib objects
|
|
241
|
+
mpl_fig = fig._fig_mpl if hasattr(fig, "_fig_mpl") else fig
|
|
242
|
+
mpl_ax = ax._ax_mpl if hasattr(ax, "_ax_mpl") else ax
|
|
243
|
+
|
|
244
|
+
# Get renderer
|
|
245
|
+
try:
|
|
246
|
+
renderer = mpl_fig.canvas.get_renderer()
|
|
247
|
+
except Exception:
|
|
248
|
+
mpl_fig.canvas.draw()
|
|
249
|
+
renderer = mpl_fig.canvas.get_renderer()
|
|
250
|
+
|
|
251
|
+
# Get saved image dimensions (needed for coordinate transformation)
|
|
252
|
+
png_path = json_path.with_suffix('.png')
|
|
253
|
+
if png_path.exists():
|
|
254
|
+
with Image.open(png_path) as img:
|
|
255
|
+
img_width, img_height = img.size
|
|
256
|
+
else:
|
|
257
|
+
# Fallback to figure dimensions
|
|
258
|
+
img_width = int(mpl_fig.get_figwidth() * dpi)
|
|
259
|
+
img_height = int(mpl_fig.get_figheight() * dpi)
|
|
260
|
+
|
|
261
|
+
# Extract element bboxes using the same logic as vis_app
|
|
262
|
+
element_bboxes = _extract_element_bboxes_for_gallery(
|
|
263
|
+
mpl_fig, mpl_ax, renderer, img_width, img_height
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if not element_bboxes:
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
# Load existing JSON
|
|
270
|
+
with open(json_path, 'r') as f:
|
|
271
|
+
metadata = json.load(f)
|
|
272
|
+
|
|
273
|
+
# Add element_bboxes
|
|
274
|
+
metadata['element_bboxes'] = element_bboxes
|
|
275
|
+
|
|
276
|
+
# Also ensure figure_size_px is present
|
|
277
|
+
if 'dimensions' not in metadata:
|
|
278
|
+
metadata['dimensions'] = {}
|
|
279
|
+
metadata['dimensions']['figure_size_px'] = {
|
|
280
|
+
'width': img_width,
|
|
281
|
+
'height': img_height
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
# Save updated JSON
|
|
285
|
+
with open(json_path, 'w') as f:
|
|
286
|
+
json.dump(metadata, f, indent=4)
|
|
287
|
+
|
|
288
|
+
if verbose:
|
|
289
|
+
print(f" [BBOXES] Added {len(element_bboxes)} element bboxes to {json_path.name}")
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
if verbose:
|
|
293
|
+
print(f" [WARN] Could not add element_bboxes: {e}")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _extract_element_bboxes_for_gallery(fig, ax, renderer, img_width, img_height):
|
|
297
|
+
"""Extract element bounding boxes for gallery figures.
|
|
298
|
+
|
|
299
|
+
Similar to vis_app's extract_element_bboxes but simplified for gallery use.
|
|
300
|
+
"""
|
|
301
|
+
import numpy as np
|
|
302
|
+
from matplotlib.transforms import Bbox
|
|
303
|
+
|
|
304
|
+
# Get figure tight bbox in inches
|
|
305
|
+
fig_bbox = fig.get_tightbbox(renderer)
|
|
306
|
+
tight_x0 = fig_bbox.x0
|
|
307
|
+
tight_y0 = fig_bbox.y0
|
|
308
|
+
tight_width = fig_bbox.width
|
|
309
|
+
tight_height = fig_bbox.height
|
|
310
|
+
|
|
311
|
+
# bbox_inches='tight' adds pad_inches around the tight bbox
|
|
312
|
+
pad_inches = 0.1
|
|
313
|
+
saved_width_inches = tight_width + 2 * pad_inches
|
|
314
|
+
saved_height_inches = tight_height + 2 * pad_inches
|
|
315
|
+
|
|
316
|
+
# Scale factors for converting inches to pixels
|
|
317
|
+
scale_x = img_width / saved_width_inches
|
|
318
|
+
scale_y = img_height / saved_height_inches
|
|
319
|
+
|
|
320
|
+
bboxes = {}
|
|
321
|
+
|
|
322
|
+
def get_element_bbox(element, name):
|
|
323
|
+
"""Get element bbox in image pixel coordinates."""
|
|
324
|
+
try:
|
|
325
|
+
bbox = element.get_window_extent(renderer)
|
|
326
|
+
if not (np.isfinite(bbox.x0) and np.isfinite(bbox.x1) and
|
|
327
|
+
np.isfinite(bbox.y0) and np.isfinite(bbox.y1)):
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
elem_x0_inches = bbox.x0 / fig.dpi
|
|
331
|
+
elem_x1_inches = bbox.x1 / fig.dpi
|
|
332
|
+
elem_y0_inches = bbox.y0 / fig.dpi
|
|
333
|
+
elem_y1_inches = bbox.y1 / fig.dpi
|
|
334
|
+
|
|
335
|
+
x0_rel = elem_x0_inches - tight_x0 + pad_inches
|
|
336
|
+
x1_rel = elem_x1_inches - tight_x0 + pad_inches
|
|
337
|
+
y0_rel = saved_height_inches - (elem_y1_inches - tight_y0 + pad_inches)
|
|
338
|
+
y1_rel = saved_height_inches - (elem_y0_inches - tight_y0 + pad_inches)
|
|
339
|
+
|
|
340
|
+
bboxes[name] = {
|
|
341
|
+
"x0": max(0, int(x0_rel * scale_x)),
|
|
342
|
+
"y0": max(0, int(y0_rel * scale_y)),
|
|
343
|
+
"x1": min(img_width, int(x1_rel * scale_x)),
|
|
344
|
+
"y1": min(img_height, int(y1_rel * scale_y)),
|
|
345
|
+
"label": name.replace("_", " ").title(),
|
|
346
|
+
}
|
|
347
|
+
except Exception:
|
|
348
|
+
pass
|
|
349
|
+
|
|
350
|
+
def coords_to_img_points(data_coords):
|
|
351
|
+
"""Convert data coordinates to image pixel coordinates."""
|
|
352
|
+
if len(data_coords) == 0:
|
|
353
|
+
return []
|
|
354
|
+
transform = ax.transData
|
|
355
|
+
points_display = transform.transform(data_coords)
|
|
356
|
+
points_img = []
|
|
357
|
+
for px, py in points_display:
|
|
358
|
+
if not np.isfinite(px) or not np.isfinite(py):
|
|
359
|
+
continue
|
|
360
|
+
px_inches = px / fig.dpi
|
|
361
|
+
py_inches = py / fig.dpi
|
|
362
|
+
x_rel = px_inches - tight_x0 + pad_inches
|
|
363
|
+
y_rel = saved_height_inches - (py_inches - tight_y0 + pad_inches)
|
|
364
|
+
x_img = max(-10000, min(10000, int(x_rel * scale_x)))
|
|
365
|
+
y_img = max(-10000, min(10000, int(y_rel * scale_y)))
|
|
366
|
+
points_img.append([x_img, y_img])
|
|
367
|
+
# Downsample if too many points
|
|
368
|
+
if len(points_img) > 100:
|
|
369
|
+
step = len(points_img) // 100
|
|
370
|
+
points_img = points_img[::step]
|
|
371
|
+
return points_img
|
|
372
|
+
|
|
373
|
+
# Extract lines
|
|
374
|
+
line_idx = 0
|
|
375
|
+
for line in ax.get_lines():
|
|
376
|
+
try:
|
|
377
|
+
label = line.get_label()
|
|
378
|
+
if label.startswith("_"):
|
|
379
|
+
continue
|
|
380
|
+
|
|
381
|
+
trace_name = f"trace_{line_idx}"
|
|
382
|
+
get_element_bbox(line, trace_name)
|
|
383
|
+
|
|
384
|
+
if trace_name in bboxes:
|
|
385
|
+
bboxes[trace_name]["label"] = label or f"Line {line_idx}"
|
|
386
|
+
bboxes[trace_name]["trace_idx"] = line_idx
|
|
387
|
+
bboxes[trace_name]["element_type"] = "line"
|
|
388
|
+
|
|
389
|
+
xdata, ydata = line.get_xdata(), line.get_ydata()
|
|
390
|
+
if len(xdata) > 0:
|
|
391
|
+
# Use path_simplified for hit detection
|
|
392
|
+
bboxes[trace_name]["path_simplified"] = coords_to_img_points(
|
|
393
|
+
list(zip(xdata, ydata))
|
|
394
|
+
)
|
|
395
|
+
line_idx += 1
|
|
396
|
+
except Exception:
|
|
397
|
+
pass
|
|
398
|
+
|
|
399
|
+
# Extract scatter collections
|
|
400
|
+
scatter_idx = 0
|
|
401
|
+
for coll in ax.collections:
|
|
402
|
+
try:
|
|
403
|
+
coll_type = type(coll).__name__
|
|
404
|
+
if coll_type == "PathCollection":
|
|
405
|
+
label = coll.get_label()
|
|
406
|
+
if label and label.startswith("_"):
|
|
407
|
+
label = None
|
|
408
|
+
|
|
409
|
+
element_name = f"scatter_{scatter_idx}"
|
|
410
|
+
offsets = coll.get_offsets()
|
|
411
|
+
|
|
412
|
+
if len(offsets) > 0:
|
|
413
|
+
points_img = coords_to_img_points(offsets)
|
|
414
|
+
if points_img:
|
|
415
|
+
xs = [p[0] for p in points_img]
|
|
416
|
+
ys = [p[1] for p in points_img]
|
|
417
|
+
padding = 10
|
|
418
|
+
bboxes[element_name] = {
|
|
419
|
+
"x0": max(0, min(xs) - padding),
|
|
420
|
+
"y0": max(0, min(ys) - padding),
|
|
421
|
+
"x1": min(img_width, max(xs) + padding),
|
|
422
|
+
"y1": min(img_height, max(ys) + padding),
|
|
423
|
+
"label": label or f"Scatter {scatter_idx}",
|
|
424
|
+
"element_type": "scatter",
|
|
425
|
+
"points": points_img,
|
|
426
|
+
}
|
|
427
|
+
scatter_idx += 1
|
|
428
|
+
except Exception:
|
|
429
|
+
pass
|
|
430
|
+
|
|
431
|
+
# Extract bars
|
|
432
|
+
bar_idx = 0
|
|
433
|
+
for patch in ax.patches:
|
|
434
|
+
try:
|
|
435
|
+
patch_type = type(patch).__name__
|
|
436
|
+
if patch_type == "Rectangle":
|
|
437
|
+
label = patch.get_label()
|
|
438
|
+
element_name = f"bar_{bar_idx}"
|
|
439
|
+
get_element_bbox(patch, element_name)
|
|
440
|
+
if element_name in bboxes:
|
|
441
|
+
bboxes[element_name]["label"] = label or f"Bar {bar_idx}"
|
|
442
|
+
bboxes[element_name]["element_type"] = "bar"
|
|
443
|
+
bar_idx += 1
|
|
444
|
+
except Exception:
|
|
445
|
+
pass
|
|
446
|
+
|
|
447
|
+
return bboxes
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _generate_and_save_hitmap(fig, dpi, hitmap_path, json_path, verbose=True, crop_box=None):
|
|
451
|
+
"""Generate hitmap PNG and add color_map to JSON.
|
|
452
|
+
|
|
453
|
+
The hitmap is generated at the same size as the PNG by:
|
|
454
|
+
1. Rendering the full figure with ID colors (same as PNG before crop)
|
|
455
|
+
2. Applying the exact same crop coordinates as the PNG
|
|
456
|
+
|
|
457
|
+
Parameters
|
|
458
|
+
----------
|
|
459
|
+
fig : FigureWrapper or matplotlib.figure.Figure
|
|
460
|
+
The figure object (before closing)
|
|
461
|
+
dpi : int
|
|
462
|
+
DPI for hitmap rendering
|
|
463
|
+
hitmap_path : Path
|
|
464
|
+
Output path for hitmap PNG
|
|
465
|
+
json_path : Path
|
|
466
|
+
Path to JSON file to update with hitmap_color_map
|
|
467
|
+
verbose : bool
|
|
468
|
+
Print progress messages
|
|
469
|
+
crop_box : tuple, optional
|
|
470
|
+
Explicit crop coordinates (left, upper, right, lower) from PNG cropping.
|
|
471
|
+
If provided, applies the same crop to hitmap for exact size matching.
|
|
472
|
+
"""
|
|
473
|
+
from PIL import Image
|
|
474
|
+
from scitex.plt.utils._hitmap import generate_hitmap_id_colors
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
# Get matplotlib figure
|
|
478
|
+
mpl_fig = fig._fig_mpl if hasattr(fig, "_fig_mpl") else fig
|
|
479
|
+
|
|
480
|
+
# Generate hitmap at original figure size (same as PNG before cropping)
|
|
481
|
+
hitmap_array, color_map = generate_hitmap_id_colors(mpl_fig, dpi=dpi)
|
|
482
|
+
|
|
483
|
+
if not color_map:
|
|
484
|
+
if verbose:
|
|
485
|
+
print(f" [HITMAP] No elements found to map - generating empty hitmap")
|
|
486
|
+
# Still generate hitmap (all zeros/black) for consistency
|
|
487
|
+
# This ensures hitmap always exists with correct size
|
|
488
|
+
|
|
489
|
+
# Convert hitmap array to RGB PNG
|
|
490
|
+
h, w = hitmap_array.shape
|
|
491
|
+
rgb = np.zeros((h, w, 3), dtype=np.uint8)
|
|
492
|
+
rgb[:, :, 0] = (hitmap_array >> 16) & 0xFF
|
|
493
|
+
rgb[:, :, 1] = (hitmap_array >> 8) & 0xFF
|
|
494
|
+
rgb[:, :, 2] = hitmap_array & 0xFF
|
|
495
|
+
hitmap_full = Image.fromarray(rgb, mode='RGB')
|
|
496
|
+
|
|
497
|
+
# Save full hitmap first (before cropping)
|
|
498
|
+
hitmap_full.save(hitmap_path, format='PNG')
|
|
499
|
+
|
|
500
|
+
if verbose:
|
|
501
|
+
print(f" [HITMAP] Full size: {w}x{h}")
|
|
502
|
+
|
|
503
|
+
# Apply same crop as PNG if crop_box provided
|
|
504
|
+
if crop_box is not None:
|
|
505
|
+
from scitex.plt.utils._crop import crop
|
|
506
|
+
crop(
|
|
507
|
+
str(hitmap_path),
|
|
508
|
+
output_path=str(hitmap_path),
|
|
509
|
+
overwrite=True,
|
|
510
|
+
crop_box=crop_box,
|
|
511
|
+
verbose=False,
|
|
512
|
+
)
|
|
513
|
+
if verbose:
|
|
514
|
+
with Image.open(hitmap_path) as cropped:
|
|
515
|
+
print(f" [HITMAP] Cropped to: {cropped.size[0]}x{cropped.size[1]}")
|
|
516
|
+
|
|
517
|
+
# Validate sizes match
|
|
518
|
+
png_path = hitmap_path.parent / hitmap_path.name.replace('_hitmap.png', '.png')
|
|
519
|
+
if png_path.exists():
|
|
520
|
+
with Image.open(png_path) as png_img, Image.open(hitmap_path) as hitmap_img:
|
|
521
|
+
png_size = png_img.size
|
|
522
|
+
hitmap_size = hitmap_img.size
|
|
523
|
+
if png_size != hitmap_size:
|
|
524
|
+
print(f" [ERROR] Size mismatch! PNG={png_size}, Hitmap={hitmap_size}")
|
|
525
|
+
raise ValueError(f"Hitmap size {hitmap_size} doesn't match PNG size {png_size}")
|
|
526
|
+
elif verbose:
|
|
527
|
+
print(f" [HITMAP] Size validated: {hitmap_size[0]}x{hitmap_size[1]} (matches PNG)")
|
|
528
|
+
|
|
529
|
+
if verbose:
|
|
530
|
+
print(f" [HITMAP] {hitmap_path.name} ({len(color_map)} elements)")
|
|
531
|
+
|
|
532
|
+
# Add hitmap_color_map to JSON
|
|
533
|
+
if json_path.exists():
|
|
534
|
+
with open(json_path, 'r') as f:
|
|
535
|
+
metadata = json.load(f)
|
|
536
|
+
|
|
537
|
+
# Store color_map with string keys for JSON compatibility
|
|
538
|
+
metadata['hitmap_color_map'] = {str(k): v for k, v in color_map.items()}
|
|
539
|
+
metadata['hitmap_file'] = hitmap_path.name
|
|
540
|
+
|
|
541
|
+
# Store crop_box in metadata for reference
|
|
542
|
+
if crop_box is not None:
|
|
543
|
+
metadata['hitmap_crop_box'] = {
|
|
544
|
+
'left': int(crop_box[0]),
|
|
545
|
+
'upper': int(crop_box[1]),
|
|
546
|
+
'right': int(crop_box[2]),
|
|
547
|
+
'lower': int(crop_box[3]),
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
with open(json_path, 'w') as f:
|
|
551
|
+
json.dump(metadata, f, indent=4)
|
|
552
|
+
|
|
553
|
+
except Exception as e:
|
|
554
|
+
if verbose:
|
|
555
|
+
print(f" [WARN] Could not generate hitmap: {e}")
|
|
556
|
+
import traceback
|
|
557
|
+
traceback.print_exc()
|
|
558
|
+
|
|
559
|
+
|
|
153
560
|
# EOF
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-12-13 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/plt/io/__init__.py
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
I/O operations for scitex.plt - Plot bundles (.pltz).
|
|
8
|
+
|
|
9
|
+
This module handles:
|
|
10
|
+
- .pltz bundle load/save operations (legacy and layered formats)
|
|
11
|
+
- CSV data export/import
|
|
12
|
+
- Hitmap generation and storage
|
|
13
|
+
- Bundle overview generation
|
|
14
|
+
|
|
15
|
+
Layered Format (v2.0):
|
|
16
|
+
plot.pltz.d/
|
|
17
|
+
spec.json # Semantic: WHAT to plot
|
|
18
|
+
style.json # Appearance: HOW it looks
|
|
19
|
+
data.csv # Raw data
|
|
20
|
+
exports/ # Preview images
|
|
21
|
+
cache/ # Regenerable geometry data
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from ._bundle import (
|
|
25
|
+
validate_pltz_spec,
|
|
26
|
+
load_pltz_bundle,
|
|
27
|
+
save_pltz_bundle,
|
|
28
|
+
generate_bundle_overview,
|
|
29
|
+
PLTZ_SCHEMA_SPEC,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from ._layered_bundle import (
|
|
33
|
+
save_layered_pltz_bundle,
|
|
34
|
+
load_layered_pltz_bundle,
|
|
35
|
+
merge_layered_bundle,
|
|
36
|
+
is_layered_bundle,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# Legacy bundle operations
|
|
41
|
+
"validate_pltz_spec",
|
|
42
|
+
"load_pltz_bundle",
|
|
43
|
+
"save_pltz_bundle",
|
|
44
|
+
"generate_bundle_overview",
|
|
45
|
+
"PLTZ_SCHEMA_SPEC",
|
|
46
|
+
# Layered bundle operations (v2.0)
|
|
47
|
+
"save_layered_pltz_bundle",
|
|
48
|
+
"load_layered_pltz_bundle",
|
|
49
|
+
"merge_layered_bundle",
|
|
50
|
+
"is_layered_bundle",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
# EOF
|