figrecipe 0.6.0__py3-none-any.whl → 0.9.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.
- figrecipe/__init__.py +161 -1030
- figrecipe/__main__.py +12 -0
- figrecipe/_api/__init__.py +48 -0
- figrecipe/_api/_extract.py +108 -0
- figrecipe/_api/_notebook.py +61 -0
- figrecipe/_api/_panel.py +113 -0
- figrecipe/_api/_save.py +287 -0
- figrecipe/_api/_seaborn_proxy.py +34 -0
- figrecipe/_api/_style_manager.py +153 -0
- figrecipe/_api/_subplots.py +333 -0
- figrecipe/_api/_validate.py +82 -0
- figrecipe/_cli/__init__.py +7 -0
- figrecipe/_cli/_compose.py +87 -0
- figrecipe/_cli/_convert.py +117 -0
- figrecipe/_cli/_crop.py +82 -0
- figrecipe/_cli/_edit.py +70 -0
- figrecipe/_cli/_extract.py +128 -0
- figrecipe/_cli/_fonts.py +47 -0
- figrecipe/_cli/_info.py +67 -0
- figrecipe/_cli/_main.py +58 -0
- figrecipe/_cli/_reproduce.py +79 -0
- figrecipe/_cli/_style.py +77 -0
- figrecipe/_cli/_validate.py +66 -0
- figrecipe/_cli/_version.py +50 -0
- figrecipe/_composition/__init__.py +32 -0
- figrecipe/_composition/_alignment.py +452 -0
- figrecipe/_composition/_compose.py +179 -0
- figrecipe/_composition/_import_axes.py +127 -0
- figrecipe/_composition/_visibility.py +125 -0
- figrecipe/_dev/__init__.py +4 -93
- figrecipe/_dev/_plotters.py +76 -0
- figrecipe/_dev/_run_demos.py +56 -0
- figrecipe/_dev/browser/__init__.py +69 -0
- figrecipe/_dev/browser/_audio.py +240 -0
- figrecipe/_dev/browser/_caption.py +356 -0
- figrecipe/_dev/browser/_click_effect.py +146 -0
- figrecipe/_dev/browser/_cursor.py +196 -0
- figrecipe/_dev/browser/_highlight.py +105 -0
- figrecipe/_dev/browser/_narration.py +237 -0
- figrecipe/_dev/browser/_recorder.py +446 -0
- figrecipe/_dev/browser/_utils.py +178 -0
- figrecipe/_dev/browser/_video_trim/__init__.py +152 -0
- figrecipe/_dev/browser/_video_trim/_detection.py +223 -0
- figrecipe/_dev/browser/_video_trim/_markers.py +140 -0
- figrecipe/_dev/demo_plotters/__init__.py +35 -166
- figrecipe/_dev/demo_plotters/_categories.py +81 -0
- figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
- figrecipe/_dev/demo_plotters/_helpers.py +31 -0
- figrecipe/_dev/demo_plotters/_registry.py +50 -0
- figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
- figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
- figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
- figrecipe/_editor/__init__.py +61 -13
- figrecipe/_editor/_bbox/__init__.py +43 -0
- figrecipe/_editor/_bbox/_collections.py +177 -0
- figrecipe/_editor/_bbox/_elements.py +159 -0
- figrecipe/_editor/_bbox/_extract.py +402 -0
- figrecipe/_editor/_bbox/_extract_axes.py +370 -0
- figrecipe/_editor/_bbox/_extract_text.py +466 -0
- figrecipe/_editor/_bbox/_lines.py +173 -0
- figrecipe/_editor/_bbox/_transforms.py +146 -0
- figrecipe/_editor/_call_overrides.py +183 -0
- figrecipe/_editor/_datatable_plot_handlers.py +249 -0
- figrecipe/_editor/_figure_layout.py +211 -0
- figrecipe/_editor/_flask_app.py +200 -1030
- figrecipe/_editor/_helpers.py +251 -0
- figrecipe/_editor/_hitmap/__init__.py +76 -0
- figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
- figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
- figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
- figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
- figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
- figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
- figrecipe/_editor/_hitmap/_colors.py +181 -0
- figrecipe/_editor/_hitmap/_detect.py +194 -0
- figrecipe/_editor/_hitmap/_restore.py +154 -0
- figrecipe/_editor/_hitmap_main.py +182 -0
- figrecipe/_editor/_overrides.py +4 -1
- figrecipe/_editor/_plot_types_registry.py +190 -0
- figrecipe/_editor/_preferences.py +135 -0
- figrecipe/_editor/_render_overrides.py +507 -0
- figrecipe/_editor/_renderer.py +81 -186
- figrecipe/_editor/_routes_annotation.py +114 -0
- figrecipe/_editor/_routes_axis.py +482 -0
- figrecipe/_editor/_routes_captions.py +130 -0
- figrecipe/_editor/_routes_composition.py +270 -0
- figrecipe/_editor/_routes_core.py +126 -0
- figrecipe/_editor/_routes_datatable.py +364 -0
- figrecipe/_editor/_routes_element.py +335 -0
- figrecipe/_editor/_routes_files.py +443 -0
- figrecipe/_editor/_routes_image.py +200 -0
- figrecipe/_editor/_routes_snapshot.py +94 -0
- figrecipe/_editor/_routes_style.py +243 -0
- figrecipe/_editor/_templates/__init__.py +116 -1
- figrecipe/_editor/_templates/_html.py +154 -64
- figrecipe/_editor/_templates/_html_components/__init__.py +13 -0
- figrecipe/_editor/_templates/_html_components/_composition_toolbar.py +79 -0
- figrecipe/_editor/_templates/_html_components/_file_browser.py +41 -0
- figrecipe/_editor/_templates/_html_datatable.py +92 -0
- figrecipe/_editor/_templates/_scripts/__init__.py +178 -0
- figrecipe/_editor/_templates/_scripts/_accordion.py +328 -0
- figrecipe/_editor/_templates/_scripts/_annotation_drag.py +504 -0
- figrecipe/_editor/_templates/_scripts/_api.py +228 -0
- figrecipe/_editor/_templates/_scripts/_canvas_context_menu.py +182 -0
- figrecipe/_editor/_templates/_scripts/_captions.py +231 -0
- figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
- figrecipe/_editor/_templates/_scripts/_composition.py +283 -0
- figrecipe/_editor/_templates/_scripts/_core.py +493 -0
- figrecipe/_editor/_templates/_scripts/_datatable/__init__.py +59 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_cell_edit.py +97 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_clipboard.py +164 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_context_menu.py +221 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_core.py +150 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_editable.py +511 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_import.py +161 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_plot.py +261 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_selection.py +438 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_table.py +256 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_tabs.py +354 -0
- figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
- figrecipe/_editor/_templates/_scripts/_element_editor.py +325 -0
- figrecipe/_editor/_templates/_scripts/_files.py +429 -0
- figrecipe/_editor/_templates/_scripts/_files_context_menu.py +240 -0
- figrecipe/_editor/_templates/_scripts/_hitmap.py +512 -0
- figrecipe/_editor/_templates/_scripts/_image_drop.py +428 -0
- figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
- figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
- figrecipe/_editor/_templates/_scripts/_legend_drag.py +270 -0
- figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
- figrecipe/_editor/_templates/_scripts/_multi_select.py +198 -0
- figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag.py +505 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag_snapshot.py +33 -0
- figrecipe/_editor/_templates/_scripts/_panel_position.py +463 -0
- figrecipe/_editor/_templates/_scripts/_panel_resize.py +230 -0
- figrecipe/_editor/_templates/_scripts/_panel_snap.py +307 -0
- figrecipe/_editor/_templates/_scripts/_region_select.py +255 -0
- figrecipe/_editor/_templates/_scripts/_selection.py +244 -0
- figrecipe/_editor/_templates/_scripts/_sync.py +242 -0
- figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
- figrecipe/_editor/_templates/_scripts/_undo_redo.py +348 -0
- figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
- figrecipe/_editor/_templates/_scripts/_zoom.py +212 -0
- figrecipe/_editor/_templates/_styles/__init__.py +78 -0
- figrecipe/_editor/_templates/_styles/_base.py +111 -0
- figrecipe/_editor/_templates/_styles/_buttons.py +327 -0
- figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
- figrecipe/_editor/_templates/_styles/_composition.py +87 -0
- figrecipe/_editor/_templates/_styles/_controls.py +430 -0
- figrecipe/_editor/_templates/_styles/_datatable/__init__.py +40 -0
- figrecipe/_editor/_templates/_styles/_datatable/_editable.py +203 -0
- figrecipe/_editor/_templates/_styles/_datatable/_panel.py +268 -0
- figrecipe/_editor/_templates/_styles/_datatable/_table.py +479 -0
- figrecipe/_editor/_templates/_styles/_datatable/_toolbar.py +384 -0
- figrecipe/_editor/_templates/_styles/_datatable/_vars.py +123 -0
- figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
- figrecipe/_editor/_templates/_styles/_file_browser.py +466 -0
- figrecipe/_editor/_templates/_styles/_forms.py +224 -0
- figrecipe/_editor/_templates/_styles/_hitmap.py +191 -0
- figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
- figrecipe/_editor/_templates/_styles/_labels.py +118 -0
- figrecipe/_editor/_templates/_styles/_modals.py +127 -0
- figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
- figrecipe/_editor/_templates/_styles/_preview.py +430 -0
- figrecipe/_editor/_templates/_styles/_selection.py +73 -0
- figrecipe/_editor/_templates/_styles/_spinner.py +117 -0
- figrecipe/_editor/static/audio/click.mp3 +0 -0
- figrecipe/_editor/static/click.mp3 +0 -0
- figrecipe/_editor/static/icons/favicon.ico +0 -0
- figrecipe/_integrations/__init__.py +17 -0
- figrecipe/_integrations/_scitex_stats.py +298 -0
- figrecipe/_params/_DECORATION_METHODS.py +8 -0
- figrecipe/_recorder.py +63 -109
- figrecipe/_recorder_utils.py +124 -0
- figrecipe/_reproducer/__init__.py +18 -0
- figrecipe/_reproducer/_core.py +509 -0
- figrecipe/_reproducer/_custom_plots.py +279 -0
- figrecipe/_reproducer/_seaborn.py +100 -0
- figrecipe/_reproducer/_violin.py +186 -0
- figrecipe/_signatures/_kwargs.py +273 -0
- figrecipe/_signatures/_loader.py +21 -423
- figrecipe/_signatures/_parsing.py +147 -0
- figrecipe/_utils/__init__.py +3 -0
- figrecipe/_utils/_bundle.py +205 -0
- figrecipe/_wrappers/_axes.py +252 -895
- figrecipe/_wrappers/_axes_helpers.py +136 -0
- figrecipe/_wrappers/_axes_plots.py +418 -0
- figrecipe/_wrappers/_axes_seaborn.py +157 -0
- figrecipe/_wrappers/_caption_generator.py +218 -0
- figrecipe/_wrappers/_figure.py +188 -1
- figrecipe/_wrappers/_panel_labels.py +127 -0
- figrecipe/_wrappers/_plot_helpers.py +143 -0
- figrecipe/_wrappers/_stat_annotation.py +274 -0
- figrecipe/_wrappers/_violin_helpers.py +180 -0
- figrecipe/styles/__init__.py +8 -6
- figrecipe/styles/_dotdict.py +72 -0
- figrecipe/styles/_finalize.py +134 -0
- figrecipe/styles/_fonts.py +77 -0
- figrecipe/styles/_kwargs_converter.py +178 -0
- figrecipe/styles/_plot_styles.py +209 -0
- figrecipe/styles/_style_applier.py +42 -480
- figrecipe/styles/_style_loader.py +16 -192
- figrecipe/styles/_themes.py +151 -0
- figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
- figrecipe/styles/presets/SCITEX.yaml +40 -28
- figrecipe-0.9.0.dist-info/METADATA +427 -0
- figrecipe-0.9.0.dist-info/RECORD +277 -0
- figrecipe-0.9.0.dist-info/entry_points.txt +2 -0
- figrecipe/_editor/_bbox.py +0 -978
- figrecipe/_editor/_hitmap.py +0 -937
- figrecipe/_editor/_templates/_scripts.py +0 -2778
- figrecipe/_editor/_templates/_styles.py +0 -1326
- figrecipe/_reproducer.py +0 -975
- figrecipe-0.6.0.dist-info/METADATA +0 -394
- figrecipe-0.6.0.dist-info/RECORD +0 -90
- /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/WHEEL +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Panel drag-to-move JavaScript for the figure editor.
|
|
4
|
+
|
|
5
|
+
This module contains the JavaScript code for:
|
|
6
|
+
- Detecting mousedown on axes/panel elements
|
|
7
|
+
- Handling drag movement with visual feedback
|
|
8
|
+
- Updating panel position on drop
|
|
9
|
+
|
|
10
|
+
Coordinates are in mm with upper-left origin.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
SCRIPTS_PANEL_DRAG = """
|
|
14
|
+
// ===== PANEL DRAG-TO-MOVE (mm, upper-left origin) =====
|
|
15
|
+
|
|
16
|
+
let isDraggingPanel = false;
|
|
17
|
+
let draggedPanelIndex = null;
|
|
18
|
+
let dragStartPos = null;
|
|
19
|
+
let dragStartPanelPos = null;
|
|
20
|
+
let panelDragOverlay = null;
|
|
21
|
+
let panelBboxDragOverlay = null; // Outer panel bbox overlay
|
|
22
|
+
let panelHoverOverlay = null;
|
|
23
|
+
let hoveredPanelIndex = null;
|
|
24
|
+
let dragStartPanelBbox = null; // Initial panel bbox in pixels
|
|
25
|
+
let panelBboxOffset = null; // Offset from axis to panel bbox (in mm)
|
|
26
|
+
|
|
27
|
+
// Initialize panel drag functionality
|
|
28
|
+
function initPanelDrag() {
|
|
29
|
+
console.log('[PanelDrag] initPanelDrag called');
|
|
30
|
+
const zoomContainer = document.getElementById('zoom-container');
|
|
31
|
+
if (!zoomContainer) {
|
|
32
|
+
console.error('[PanelDrag] zoom-container not found!');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Add mouse event listeners to zoom container
|
|
37
|
+
zoomContainer.addEventListener('mousedown', handlePanelDragStart);
|
|
38
|
+
document.addEventListener('mousemove', handlePanelDragMove);
|
|
39
|
+
document.addEventListener('mouseup', handlePanelDragEnd);
|
|
40
|
+
console.log('[PanelDrag] Event listeners attached');
|
|
41
|
+
|
|
42
|
+
// Create drag overlay for axis bbox (inner, subtle orange)
|
|
43
|
+
panelDragOverlay = document.createElement('div');
|
|
44
|
+
panelDragOverlay.id = 'panel-drag-overlay';
|
|
45
|
+
panelDragOverlay.style.cssText = `
|
|
46
|
+
position: absolute;
|
|
47
|
+
border: 2px dashed #f59e0b;
|
|
48
|
+
background: rgba(245, 158, 11, 0.08);
|
|
49
|
+
pointer-events: none;
|
|
50
|
+
display: none;
|
|
51
|
+
z-index: 999;
|
|
52
|
+
`;
|
|
53
|
+
zoomContainer.appendChild(panelDragOverlay);
|
|
54
|
+
|
|
55
|
+
// Create outer panel bbox overlay (prominent blue)
|
|
56
|
+
panelBboxDragOverlay = document.createElement('div');
|
|
57
|
+
panelBboxDragOverlay.id = 'panel-bbox-drag-overlay';
|
|
58
|
+
panelBboxDragOverlay.style.cssText = `
|
|
59
|
+
position: absolute;
|
|
60
|
+
border: 2px dashed #2563eb;
|
|
61
|
+
background: rgba(37, 99, 235, 0.05);
|
|
62
|
+
pointer-events: none;
|
|
63
|
+
display: none;
|
|
64
|
+
z-index: 1000;
|
|
65
|
+
`;
|
|
66
|
+
zoomContainer.appendChild(panelBboxDragOverlay);
|
|
67
|
+
|
|
68
|
+
// Create hover overlay element for visual feedback
|
|
69
|
+
panelHoverOverlay = document.createElement('div');
|
|
70
|
+
panelHoverOverlay.id = 'panel-hover-overlay';
|
|
71
|
+
panelHoverOverlay.style.cssText = `
|
|
72
|
+
position: absolute;
|
|
73
|
+
border: 2px solid rgba(37, 99, 235, 0.5);
|
|
74
|
+
background: rgba(37, 99, 235, 0.05);
|
|
75
|
+
pointer-events: none;
|
|
76
|
+
display: none;
|
|
77
|
+
z-index: 999;
|
|
78
|
+
transition: opacity 0.15s ease-in-out;
|
|
79
|
+
`;
|
|
80
|
+
zoomContainer.appendChild(panelHoverOverlay);
|
|
81
|
+
|
|
82
|
+
// Add hover detection on zoom container
|
|
83
|
+
zoomContainer.addEventListener('mousemove', handlePanelHover);
|
|
84
|
+
zoomContainer.addEventListener('mouseleave', hidePanelHover);
|
|
85
|
+
|
|
86
|
+
console.log('[PanelDrag] Overlays created');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handle mouse down - check if on a panel/axes (only drag from empty panel area)
|
|
90
|
+
function handlePanelDragStart(event) {
|
|
91
|
+
if (event.ctrlKey || event.metaKey || event.altKey) return; // Skip modifier keys
|
|
92
|
+
|
|
93
|
+
// Only allow drag from axes/imshow/contour/quadmesh/quiver (fills panel area)
|
|
94
|
+
const target = event.target;
|
|
95
|
+
const targetKey = target.getAttribute ? target.getAttribute('data-key') : null;
|
|
96
|
+
if (targetKey && typeof currentBboxes !== 'undefined' && currentBboxes[targetKey]) {
|
|
97
|
+
const elemType = currentBboxes[targetKey].type;
|
|
98
|
+
const dragAllowedTypes = ['axes', 'image', 'contour', 'quadmesh', 'quiver'];
|
|
99
|
+
if (elemType && !dragAllowedTypes.includes(elemType)) return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const img = document.getElementById('preview-image');
|
|
103
|
+
if (!img || !figSize.width_mm || !figSize.height_mm) return;
|
|
104
|
+
|
|
105
|
+
const rect = img.getBoundingClientRect();
|
|
106
|
+
const x = event.clientX - rect.left;
|
|
107
|
+
const y = event.clientY - rect.top;
|
|
108
|
+
|
|
109
|
+
// Convert to mm coordinates (upper-left origin)
|
|
110
|
+
const mmX = (x / rect.width) * figSize.width_mm;
|
|
111
|
+
const mmY = (y / rect.height) * figSize.height_mm;
|
|
112
|
+
|
|
113
|
+
// Find which panel was clicked (using expanded bounds including labels)
|
|
114
|
+
const panelIndex = findPanelAtPositionMm(mmX, mmY);
|
|
115
|
+
console.log('[PanelDrag] Click at mm:', mmX.toFixed(1), mmY.toFixed(1), '-> panel:', panelIndex);
|
|
116
|
+
|
|
117
|
+
if (panelIndex !== null) {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
|
|
121
|
+
// Capture state before drag for undo
|
|
122
|
+
if (typeof pushToHistory === 'function') {
|
|
123
|
+
pushToHistory();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
isDraggingPanel = true;
|
|
127
|
+
draggedPanelIndex = panelIndex;
|
|
128
|
+
dragStartPos = { x: event.clientX, y: event.clientY };
|
|
129
|
+
|
|
130
|
+
// Hide hover overlay when starting drag
|
|
131
|
+
hidePanelHover();
|
|
132
|
+
|
|
133
|
+
// Get current panel position (in mm)
|
|
134
|
+
const axKey = Object.keys(panelPositions).sort()[panelIndex];
|
|
135
|
+
const pos = panelPositions[axKey];
|
|
136
|
+
dragStartPanelPos = { ...pos };
|
|
137
|
+
|
|
138
|
+
// Get panel bbox (outer bounds including labels) and calculate offset from axis
|
|
139
|
+
const panelBboxes = currentBboxes?._meta?.panel_bboxes;
|
|
140
|
+
if (panelBboxes && panelBboxes[panelIndex] && img.naturalWidth) {
|
|
141
|
+
dragStartPanelBbox = { ...panelBboxes[panelIndex] };
|
|
142
|
+
// Convert panel bbox to mm and calculate offset from axis position
|
|
143
|
+
const pxToMmX = figSize.width_mm / img.naturalWidth;
|
|
144
|
+
const pxToMmY = figSize.height_mm / img.naturalHeight;
|
|
145
|
+
panelBboxOffset = {
|
|
146
|
+
left: dragStartPanelBbox.x * pxToMmX - pos.left,
|
|
147
|
+
top: dragStartPanelBbox.y * pxToMmY - pos.top,
|
|
148
|
+
width: dragStartPanelBbox.width * pxToMmX,
|
|
149
|
+
height: dragStartPanelBbox.height * pxToMmY
|
|
150
|
+
};
|
|
151
|
+
} else {
|
|
152
|
+
dragStartPanelBbox = null;
|
|
153
|
+
panelBboxOffset = null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Create overlay if it doesn't exist
|
|
157
|
+
if (!panelDragOverlay) {
|
|
158
|
+
console.log('[PanelDrag] Creating overlay on-demand');
|
|
159
|
+
const zoomContainer = document.getElementById('zoom-container');
|
|
160
|
+
if (zoomContainer) {
|
|
161
|
+
panelDragOverlay = document.createElement('div');
|
|
162
|
+
panelDragOverlay.id = 'panel-drag-overlay';
|
|
163
|
+
panelDragOverlay.style.cssText = `
|
|
164
|
+
position: absolute;
|
|
165
|
+
border: 2px dashed #2563eb;
|
|
166
|
+
background: rgba(37, 99, 235, 0.1);
|
|
167
|
+
pointer-events: none;
|
|
168
|
+
display: none;
|
|
169
|
+
z-index: 1000;
|
|
170
|
+
`;
|
|
171
|
+
zoomContainer.appendChild(panelDragOverlay);
|
|
172
|
+
console.log('[PanelDrag] Overlay created on-demand');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Show drag overlay (axis bbox)
|
|
177
|
+
if (panelDragOverlay) {
|
|
178
|
+
updateDragOverlayMm(pos, rect);
|
|
179
|
+
panelDragOverlay.style.display = 'block';
|
|
180
|
+
console.log('[PanelDrag] Axis overlay shown');
|
|
181
|
+
} else {
|
|
182
|
+
console.warn('[PanelDrag] Overlay still null after creation attempt');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Show panel bbox overlay (outer bounds) - follows snapped axis position
|
|
186
|
+
if (panelBboxDragOverlay && panelBboxOffset) {
|
|
187
|
+
updatePanelBboxDragOverlayMm(pos, rect);
|
|
188
|
+
panelBboxDragOverlay.style.display = 'block';
|
|
189
|
+
console.log('[PanelDrag] Panel bbox overlay shown');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Create and show panel snapshot for visual feedback
|
|
193
|
+
if (typeof startSnapshotDrag === 'function') {
|
|
194
|
+
startSnapshotDrag(panelIndex, rect, pos);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Change cursor
|
|
198
|
+
document.body.style.cursor = 'move';
|
|
199
|
+
|
|
200
|
+
console.log('Started dragging panel', panelIndex);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Find which panel contains the given position (in mm, upper-left origin)
|
|
205
|
+
// Uses expanded bounds to include title, labels, and tick areas
|
|
206
|
+
function findPanelAtPositionMm(mmX, mmY) {
|
|
207
|
+
const axKeys = Object.keys(panelPositions).sort();
|
|
208
|
+
|
|
209
|
+
// Margins in mm to expand panel bounds for labels/title/ticks
|
|
210
|
+
const marginLeft = 15; // Space for y-axis label and ticks
|
|
211
|
+
const marginRight = 5; // Small buffer on right
|
|
212
|
+
const marginTop = 8; // Space for title
|
|
213
|
+
const marginBottom = 12; // Space for x-axis label and ticks
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < axKeys.length; i++) {
|
|
216
|
+
const pos = panelPositions[axKeys[i]];
|
|
217
|
+
|
|
218
|
+
// Expanded bounds including label/title areas
|
|
219
|
+
const expandedLeft = Math.max(0, pos.left - marginLeft);
|
|
220
|
+
const expandedTop = Math.max(0, pos.top - marginTop);
|
|
221
|
+
const expandedRight = Math.min(figSize.width_mm, pos.left + pos.width + marginRight);
|
|
222
|
+
const expandedBottom = Math.min(figSize.height_mm, pos.top + pos.height + marginBottom);
|
|
223
|
+
|
|
224
|
+
// Check if point is within expanded panel bounds
|
|
225
|
+
if (mmX >= expandedLeft && mmX <= expandedRight &&
|
|
226
|
+
mmY >= expandedTop && mmY <= expandedBottom) {
|
|
227
|
+
return i;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Handle mouse hover over panels - show visual feedback
|
|
234
|
+
function handlePanelHover(event) {
|
|
235
|
+
// Skip if dragging
|
|
236
|
+
if (isDraggingPanel) {
|
|
237
|
+
hidePanelHover();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const img = document.getElementById('preview-image');
|
|
242
|
+
if (!img || !figSize.width_mm || !figSize.height_mm) return;
|
|
243
|
+
|
|
244
|
+
const rect = img.getBoundingClientRect();
|
|
245
|
+
const x = event.clientX - rect.left;
|
|
246
|
+
const y = event.clientY - rect.top;
|
|
247
|
+
|
|
248
|
+
// Check if mouse is within image bounds
|
|
249
|
+
if (x < 0 || x > rect.width || y < 0 || y > rect.height) {
|
|
250
|
+
hidePanelHover();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Convert to mm coordinates
|
|
255
|
+
const mmX = (x / rect.width) * figSize.width_mm;
|
|
256
|
+
const mmY = (y / rect.height) * figSize.height_mm;
|
|
257
|
+
|
|
258
|
+
// Find panel at position
|
|
259
|
+
const panelIndex = findPanelAtPositionMm(mmX, mmY);
|
|
260
|
+
|
|
261
|
+
if (panelIndex !== null && panelIndex !== hoveredPanelIndex) {
|
|
262
|
+
showPanelHover(panelIndex, rect);
|
|
263
|
+
} else if (panelIndex === null) {
|
|
264
|
+
hidePanelHover();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Show hover feedback for a panel
|
|
269
|
+
function showPanelHover(panelIndex, imgRect) {
|
|
270
|
+
if (!panelHoverOverlay) return;
|
|
271
|
+
|
|
272
|
+
hoveredPanelIndex = panelIndex;
|
|
273
|
+
|
|
274
|
+
// Get panel position
|
|
275
|
+
const axKey = Object.keys(panelPositions).sort()[panelIndex];
|
|
276
|
+
const pos = panelPositions[axKey];
|
|
277
|
+
if (!pos) return;
|
|
278
|
+
|
|
279
|
+
// Convert mm to screen pixels
|
|
280
|
+
const scaleX = imgRect.width / figSize.width_mm;
|
|
281
|
+
const scaleY = imgRect.height / figSize.height_mm;
|
|
282
|
+
|
|
283
|
+
const left = pos.left * scaleX;
|
|
284
|
+
const top = pos.top * scaleY;
|
|
285
|
+
const width = pos.width * scaleX;
|
|
286
|
+
const height = pos.height * scaleY;
|
|
287
|
+
|
|
288
|
+
panelHoverOverlay.style.left = `${left}px`;
|
|
289
|
+
panelHoverOverlay.style.top = `${top}px`;
|
|
290
|
+
panelHoverOverlay.style.width = `${width}px`;
|
|
291
|
+
panelHoverOverlay.style.height = `${height}px`;
|
|
292
|
+
panelHoverOverlay.style.display = 'block';
|
|
293
|
+
|
|
294
|
+
// Change cursor to indicate draggable
|
|
295
|
+
document.body.style.cursor = 'move';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Hide hover feedback
|
|
299
|
+
function hidePanelHover() {
|
|
300
|
+
if (panelHoverOverlay) {
|
|
301
|
+
panelHoverOverlay.style.display = 'none';
|
|
302
|
+
}
|
|
303
|
+
hoveredPanelIndex = null;
|
|
304
|
+
|
|
305
|
+
// Reset cursor if not dragging
|
|
306
|
+
if (!isDraggingPanel) {
|
|
307
|
+
document.body.style.cursor = '';
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Handle mouse move during drag
|
|
312
|
+
function handlePanelDragMove(event) {
|
|
313
|
+
if (!isDraggingPanel) return;
|
|
314
|
+
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
|
|
317
|
+
const img = document.getElementById('preview-image');
|
|
318
|
+
if (!img) return;
|
|
319
|
+
|
|
320
|
+
const rect = img.getBoundingClientRect();
|
|
321
|
+
|
|
322
|
+
// Calculate delta in mm
|
|
323
|
+
const deltaMmX = (event.clientX - dragStartPos.x) / rect.width * figSize.width_mm;
|
|
324
|
+
const deltaMmY = (event.clientY - dragStartPos.y) / rect.height * figSize.height_mm;
|
|
325
|
+
|
|
326
|
+
// Calculate raw new position (clamped to figure bounds)
|
|
327
|
+
let newLeft = Math.max(0, Math.min(figSize.width_mm - dragStartPanelPos.width, dragStartPanelPos.left + deltaMmX));
|
|
328
|
+
let newTop = Math.max(0, Math.min(figSize.height_mm - dragStartPanelPos.height, dragStartPanelPos.top + deltaMmY));
|
|
329
|
+
|
|
330
|
+
// Apply snapping (Alt key disables snapping for fine control)
|
|
331
|
+
let snapResult = { pos: { left: newLeft, top: newTop }, guides: [] };
|
|
332
|
+
if (typeof applySnapping === 'function' && !event.altKey) {
|
|
333
|
+
snapResult = applySnapping(newLeft, newTop, dragStartPanelPos.width, dragStartPanelPos.height, draggedPanelIndex);
|
|
334
|
+
newLeft = snapResult.pos.left;
|
|
335
|
+
newTop = snapResult.pos.top;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const newPos = {
|
|
339
|
+
left: newLeft,
|
|
340
|
+
top: newTop,
|
|
341
|
+
width: dragStartPanelPos.width,
|
|
342
|
+
height: dragStartPanelPos.height
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// Update visual overlays - both use snapped position in mm
|
|
346
|
+
updateDragOverlayMm(newPos, rect);
|
|
347
|
+
updatePanelBboxDragOverlayMm(newPos, rect);
|
|
348
|
+
|
|
349
|
+
// Update snapshot position
|
|
350
|
+
if (typeof updateSnapshotPosition === 'function') {
|
|
351
|
+
updateSnapshotPosition(newPos, rect);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Show/hide alignment guides
|
|
355
|
+
if (typeof showSnapGuides === 'function') {
|
|
356
|
+
showSnapGuides(snapResult.guides, rect);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Update axis drag overlay (mm to screen pixels)
|
|
361
|
+
function updateDragOverlayMm(pos, imgRect) {
|
|
362
|
+
if (!panelDragOverlay || !figSize.width_mm) return;
|
|
363
|
+
const scaleX = imgRect.width / figSize.width_mm, scaleY = imgRect.height / figSize.height_mm;
|
|
364
|
+
panelDragOverlay.style.left = `${pos.left * scaleX}px`;
|
|
365
|
+
panelDragOverlay.style.top = `${pos.top * scaleY}px`;
|
|
366
|
+
panelDragOverlay.style.width = `${pos.width * scaleX}px`;
|
|
367
|
+
panelDragOverlay.style.height = `${pos.height * scaleY}px`;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Update panel bbox overlay based on snapped axis position (in mm)
|
|
371
|
+
function updatePanelBboxDragOverlayMm(axisPos, imgRect) {
|
|
372
|
+
if (!panelBboxDragOverlay || !panelBboxOffset || !figSize.width_mm) return;
|
|
373
|
+
const scaleX = imgRect.width / figSize.width_mm, scaleY = imgRect.height / figSize.height_mm;
|
|
374
|
+
// Panel bbox position = axis position + offset (both in mm, converted to screen pixels)
|
|
375
|
+
panelBboxDragOverlay.style.left = `${(axisPos.left + panelBboxOffset.left) * scaleX}px`;
|
|
376
|
+
panelBboxDragOverlay.style.top = `${(axisPos.top + panelBboxOffset.top) * scaleY}px`;
|
|
377
|
+
panelBboxDragOverlay.style.width = `${panelBboxOffset.width * scaleX}px`;
|
|
378
|
+
panelBboxDragOverlay.style.height = `${panelBboxOffset.height * scaleY}px`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Handle mouse up - complete the drag
|
|
382
|
+
async function handlePanelDragEnd(event) {
|
|
383
|
+
console.log('[PanelDrag] handlePanelDragEnd called, isDraggingPanel:', isDraggingPanel);
|
|
384
|
+
if (!isDraggingPanel) return;
|
|
385
|
+
|
|
386
|
+
// Hide overlays, snapshot, and snap guides
|
|
387
|
+
if (panelDragOverlay) panelDragOverlay.style.display = 'none';
|
|
388
|
+
if (panelBboxDragOverlay) panelBboxDragOverlay.style.display = 'none';
|
|
389
|
+
if (typeof endSnapshotDrag === 'function') endSnapshotDrag();
|
|
390
|
+
if (typeof hideSnapGuides === 'function') hideSnapGuides();
|
|
391
|
+
document.body.style.cursor = '';
|
|
392
|
+
dragStartPanelBbox = null;
|
|
393
|
+
panelBboxOffset = null;
|
|
394
|
+
|
|
395
|
+
const img = document.getElementById('preview-image');
|
|
396
|
+
if (!img) {
|
|
397
|
+
isDraggingPanel = false;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const rect = img.getBoundingClientRect();
|
|
402
|
+
|
|
403
|
+
// Calculate final position in mm
|
|
404
|
+
const deltaMmX = (event.clientX - dragStartPos.x) / rect.width * figSize.width_mm;
|
|
405
|
+
const deltaMmY = (event.clientY - dragStartPos.y) / rect.height * figSize.height_mm;
|
|
406
|
+
|
|
407
|
+
let newLeft = Math.max(0, Math.min(figSize.width_mm - dragStartPanelPos.width, dragStartPanelPos.left + deltaMmX));
|
|
408
|
+
let newTop = Math.max(0, Math.min(figSize.height_mm - dragStartPanelPos.height, dragStartPanelPos.top + deltaMmY));
|
|
409
|
+
|
|
410
|
+
// Apply snapping to final position (unless Alt was held)
|
|
411
|
+
if (typeof applySnapping === 'function' && !event.altKey) {
|
|
412
|
+
const snapResult = applySnapping(newLeft, newTop, dragStartPanelPos.width, dragStartPanelPos.height, draggedPanelIndex);
|
|
413
|
+
newLeft = snapResult.pos.left;
|
|
414
|
+
newTop = snapResult.pos.top;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Only update if position actually changed (threshold in mm)
|
|
418
|
+
const threshold = 1.0; // 1mm threshold
|
|
419
|
+
const deltaLeft = Math.abs(newLeft - dragStartPanelPos.left);
|
|
420
|
+
const deltaTop = Math.abs(newTop - dragStartPanelPos.top);
|
|
421
|
+
console.log('[PanelDrag] Delta: left=', deltaLeft.toFixed(2), 'top=', deltaTop.toFixed(2), 'threshold=', threshold);
|
|
422
|
+
|
|
423
|
+
if (deltaLeft > threshold || deltaTop > threshold) {
|
|
424
|
+
console.log('[PanelDrag] Applying new position:', newLeft.toFixed(2), newTop.toFixed(2));
|
|
425
|
+
// Apply the new position (in mm)
|
|
426
|
+
await applyDraggedPanelPosition(
|
|
427
|
+
draggedPanelIndex,
|
|
428
|
+
newLeft,
|
|
429
|
+
newTop,
|
|
430
|
+
dragStartPanelPos.width,
|
|
431
|
+
dragStartPanelPos.height
|
|
432
|
+
);
|
|
433
|
+
} else {
|
|
434
|
+
console.log('[PanelDrag] Movement below threshold, not updating');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Reset state
|
|
438
|
+
isDraggingPanel = false;
|
|
439
|
+
draggedPanelIndex = null;
|
|
440
|
+
dragStartPos = null;
|
|
441
|
+
dragStartPanelPos = null;
|
|
442
|
+
console.log('[PanelDrag] Drag state reset');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Apply the dragged panel position to the server (values in mm)
|
|
446
|
+
async function applyDraggedPanelPosition(axIndex, left, top, width, height) {
|
|
447
|
+
document.body.classList.add('loading');
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
const response = await fetch('/update_axes_position', {
|
|
451
|
+
method: 'POST',
|
|
452
|
+
headers: { 'Content-Type': 'application/json' },
|
|
453
|
+
body: JSON.stringify({
|
|
454
|
+
ax_index: axIndex,
|
|
455
|
+
left: left,
|
|
456
|
+
top: top,
|
|
457
|
+
width: width,
|
|
458
|
+
height: height
|
|
459
|
+
})
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
const data = await response.json();
|
|
463
|
+
|
|
464
|
+
if (data.success) {
|
|
465
|
+
// Update preview image and wait for it to load
|
|
466
|
+
const img = document.getElementById('preview-image');
|
|
467
|
+
if (img) {
|
|
468
|
+
await new Promise((resolve) => {
|
|
469
|
+
img.onload = resolve;
|
|
470
|
+
img.src = 'data:image/png;base64,' + data.image;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Update image size
|
|
475
|
+
if (data.img_size) {
|
|
476
|
+
currentImgWidth = data.img_size.width;
|
|
477
|
+
currentImgHeight = data.img_size.height;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Update bboxes and hitmap
|
|
481
|
+
currentBboxes = data.bboxes;
|
|
482
|
+
loadHitmap();
|
|
483
|
+
updateHitRegions();
|
|
484
|
+
|
|
485
|
+
// Reload positions (now with correct image dimensions)
|
|
486
|
+
await loadPanelPositions();
|
|
487
|
+
|
|
488
|
+
console.log('Panel position updated via drag');
|
|
489
|
+
} else {
|
|
490
|
+
console.error('Failed to update position:', data.error);
|
|
491
|
+
}
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.error('Failed to update position:', error);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
document.body.classList.remove('loading');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Initialize on DOMContentLoaded
|
|
500
|
+
document.addEventListener('DOMContentLoaded', initPanelDrag);
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
__all__ = ["SCRIPTS_PANEL_DRAG"]
|
|
504
|
+
|
|
505
|
+
# EOF
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Panel drag snapshot functionality with server-side isolated rendering.
|
|
4
|
+
|
|
5
|
+
This module provides clean panel snapshots rendered in isolation (no overlap)
|
|
6
|
+
by fetching from the server, with async caching for smooth UX.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
SCRIPTS_PANEL_DRAG_SNAPSHOT = """
|
|
10
|
+
// ===== PANEL DRAG SNAPSHOT (DISABLED - corrupts figure state) =====
|
|
11
|
+
// Server-side snapshot rendering was disabled because matplotlib figures
|
|
12
|
+
// are not thread-safe. Modifying visibility to render isolated panels
|
|
13
|
+
// corrupts the shared figure state in Flask's threaded mode.
|
|
14
|
+
|
|
15
|
+
// No-op stubs to prevent errors from panel_drag.py calls
|
|
16
|
+
function startSnapshotDrag(panelIndex, imgRect, initialPos) {
|
|
17
|
+
// Disabled - no snapshot during drag
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function updateSnapshotPosition(pos, imgRect) {
|
|
21
|
+
// Disabled - no snapshot during drag
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function endSnapshotDrag() {
|
|
25
|
+
// Disabled - no snapshot during drag
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// No initialization needed
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
__all__ = ["SCRIPTS_PANEL_DRAG_SNAPSHOT"]
|
|
32
|
+
|
|
33
|
+
# EOF
|