diff --git a/steps/index.html b/steps/index.html index 2d49b31..a3c8601 100644 --- a/steps/index.html +++ b/steps/index.html @@ -517,7 +517,7 @@
-
+
-
+
-
+
0 && staticBackgroundBuffer.height > 0) { + p.image(staticBackgroundBuffer, 0, 0); + } // Apply transformations for radar coordinate system (origin at bottom-center, Y-axis inverted) p.push(); @@ -365,7 +367,7 @@ export const radarSketch = function (p) { // 4. Draw the new legend buffer onto the main canvas // This is placed at the bottom-right corner. - if (toggleTracks.checked && !appState.isRawOnlyMode) { + if (toggleTracks.checked && !appState.isRawOnlyMode && trackLegendBuffer && trackLegendBuffer.width > 0) { p.image( trackLegendBuffer, p.width - trackLegendBuffer.width - 10, @@ -537,8 +539,8 @@ export const radarSketch = function (p) { isFirstFrame = true; // Reset for the next time zoom mode is enabled } // --- Legend Drawing --- - // Draw the SNR legend if enabled - if (toggleSnrColor.checked) { + // Draw the legend buffer if requested + if (toggleSnrColor.checked && snrLegendBuffer && snrLegendBuffer.width > 0) { p.image(snrLegendBuffer, 10, p.height - snrLegendBuffer.height - 10); } }; diff --git a/steps/src/p5/speedGraphSketch.js b/steps/src/p5/speedGraphSketch.js index 7a71fa8..f5fb3da 100644 --- a/steps/src/p5/speedGraphSketch.js +++ b/steps/src/p5/speedGraphSketch.js @@ -459,7 +459,9 @@ export const speedGraphSketch = function (p) { p.text("No data to display", p.width / 2, p.height / 2); return; } - p.image(staticBuffer, 0, 0); + if (staticBuffer && staticBuffer.width > 0 && staticBuffer.height > 0) { + p.image(staticBuffer, 0, 0); + } drawTimeIndicator(); // draw hover vertical line and tooltip if applicable diff --git a/steps/src/p5/zoomSketch.js b/steps/src/p5/zoomSketch.js index 032aefe..cc6205c 100644 --- a/steps/src/p5/zoomSketch.js +++ b/steps/src/p5/zoomSketch.js @@ -325,7 +325,8 @@ export const zoomSketch = function (p) { p.scale(appState.zoomFactor); // --- Redraw the scene from scratch --- - if (appState.p5_instance && appState.p5_instance.getStaticBackground) { + // Performance fix: Check if source has valid dimensions before drawing + if (appState.p5_instance && appState.p5_instance.width > 0 && appState.p5_instance.height > 0 && appState.p5_instance.getStaticBackground) { const bg = appState.p5_instance.getStaticBackground(); // Optimization: Only draw the visible slice of the background // Drawing the full 1920x1080 texture every frame is expensive if we only see a tiny part. @@ -345,7 +346,7 @@ export const zoomSketch = function (p) { const dW = Math.min(imgW, sX + visibleW) - dX; const dH = Math.min(imgH, sY + visibleH) - dY; - if (dW > 0 && dH > 0) { + if (dW > 0 && dH > 0 && bg.width > 0 && bg.height > 0) { // Draw only the visible sub-rectangle // Since we are transformed to World Space, destination (dx,dy) matches source (dx,dy) p.image(bg, dX, dY, dW, dH, dX, dY, dW, dH); diff --git a/steps/src/ui.js b/steps/src/ui.js index 4e9d570..20cf1dc 100644 --- a/steps/src/ui.js +++ b/steps/src/ui.js @@ -62,6 +62,50 @@ export function makeDraggableAndResizable(panel, header, minWidth = 400, minHeig let original_mouse_x = 0; let original_mouse_y = 0; + // --- Persistence Logic --- + const storageKey = `panel_pos_${panel.id}`; + + function savePosition() { + if (!panel.id) return; + const state = { + left: panel.style.left, + top: panel.style.top, + width: panel.style.width, + height: panel.style.height + }; + console.log(`Saving position for ${panel.id}`, state); + localStorage.setItem(storageKey, JSON.stringify(state)); + } + + function loadPosition() { + if (!panel.id) return; + const saved = localStorage.getItem(storageKey); + if (saved) { + try { + const state = JSON.parse(saved); + console.log(`Loading position for ${panel.id}`, state); + if (state.left) panel.style.left = state.left; + if (state.top) panel.style.top = state.top; + if (state.width) panel.style.width = state.width; + if (state.height) panel.style.height = state.height; + // Ensure it's still in view + requestAnimationFrame(() => constrainToViewport()); + } catch (e) { console.error(`Failed to load position for ${panel.id}`, e); } + } else { + console.log(`No saved position found for ${panel.id}`); + } + } + + // --- Auto-Focus (Bring to Front) --- + panel.addEventListener('mousedown', () => { + document.querySelectorAll('#zoom-panel, #data-explorer-panel').forEach(p => { + p.style.zIndex = "30"; + }); + panel.style.zIndex = "40"; + }); + + loadPosition(); + // --- Dragging Logic --- header.addEventListener('mousedown', (e) => { // Prevent drag if clicking buttons @@ -91,6 +135,7 @@ export function makeDraggableAndResizable(panel, header, minWidth = 400, minHeig document.body.classList.remove('dragging'); window.removeEventListener('mousemove', dragPanel); window.removeEventListener('mouseup', stopDrag); + savePosition(); } // --- Resizing Logic --- @@ -112,6 +157,7 @@ export function makeDraggableAndResizable(panel, header, minWidth = 400, minHeig window.addEventListener('mouseup', () => { document.body.classList.remove('resizing'); window.removeEventListener('mousemove', resizeFunc); + savePosition(); if (panel.id === 'zoom-panel' && appState.zoomSketchInstance) { appState.zoomSketchInstance.handleContainerResize(); } @@ -249,6 +295,39 @@ export function initUIEventListeners() { animate: true, handle: '.grid-stack-item-content > .cursor-grab', }); + + // Load saved layout with a small delay to ensure DOM is ready + let isInitialLoad = true; + setTimeout(() => { + const savedLayout = localStorage.getItem('gridstack_layout'); + if (savedLayout) { + try { + const layout = JSON.parse(savedLayout); + console.log("Restoring GridStack positions", layout); + // Use "soft load" to updates positions by id without replacing DOM + layout.forEach(item => { + const id = item.id || item.gsId; + if (id) { + const el = document.querySelector(`.grid-stack-item[gs-id="${id}"]`); + if (el) appState.gridStackInstance.update(el, { x: item.x, y: item.y, w: item.w, h: item.h }); + } + }); + } catch (e) { } + } + isInitialLoad = false; + }, 100); + + // Save layout on changes + const saveGrid = () => { + if (isInitialLoad) return; // Don't save while loading + // save(true, false) saves all items with their current positions/sizes + const layout = appState.gridStackInstance.save(true, false); + console.log("Saving GridStack layout", layout); + localStorage.setItem('gridstack_layout', JSON.stringify(layout)); + }; + appState.gridStackInstance.on('change', saveGrid); + appState.gridStackInstance.on('dragstop', saveGrid); + appState.gridStackInstance.on('resizestop', saveGrid); } // --- Initialize Floating Zoom Panel ---