From 4cd6fda91587d0020e78bc218242bc1bf88cc780 Mon Sep 17 00:00:00 2001 From: Jacob Brooks Date: Fri, 10 Apr 2026 10:07:38 -0400 Subject: [PATCH] feat: add auto-pack for left panel source images - Shelf packing algorithm arranges images with no overlap - Zoom-to-fit after packing to show all images - New 'Auto Pack Images' button in left panel toolbar Co-Authored-By: Claude Opus 4.6 (1M context) --- index.html | 1 + scripts/leftPanelManager.js | 59 +++++++++++++++++++++++++++++++++++++ scripts/main.js | 4 +++ 3 files changed, 64 insertions(+) diff --git a/index.html b/index.html index 39bc353..5a3b273 100644 --- a/index.html +++ b/index.html @@ -30,6 +30,7 @@
+ diff --git a/scripts/leftPanelManager.js b/scripts/leftPanelManager.js index a666e4c..d06df28 100644 --- a/scripts/leftPanelManager.js +++ b/scripts/leftPanelManager.js @@ -358,6 +358,65 @@ const LeftPanelManager = { PanZoomManager.initPanning(stage); PanZoomManager.initZooming(stage); + window.leftPanel = { + autoPackImages: () => { + if (bgImages.length === 0) return; + + const padding = 10; + const getDims = (img) => ({ + width: img.width() * Math.abs(img.scaleX()), + height: img.height() * Math.abs(img.scaleY()) + }); + + const sorted = [...bgImages].sort((a, b) => { + return getDims(b).height - getDims(a).height; + }); + + let totalArea = 0; + sorted.forEach(img => { + const d = getDims(img); + totalArea += d.width * d.height; + }); + const targetRowWidth = Math.max( + stage.width(), + Math.sqrt(totalArea) * 1.4 + ); + + let cursorX = 0; + let cursorY = 0; + let rowHeight = 0; + + sorted.forEach(img => { + const { width, height } = getDims(img); + if (cursorX > 0 && cursorX + width > targetRowWidth) { + cursorX = 0; + cursorY += rowHeight + padding; + rowHeight = 0; + } + img.position({ x: cursorX, y: cursorY }); + cursorX += width + padding; + rowHeight = Math.max(rowHeight, height); + }); + + tr.nodes([]); + + const viewWidth = stage.width(); + const viewHeight = stage.height(); + const layoutWidth = targetRowWidth; + const layoutHeight = cursorY + rowHeight; + const scale = Math.min( + viewWidth / (layoutWidth + padding * 2), + viewHeight / (layoutHeight + padding * 2), + 1 + ); + stage.scale({ x: scale, y: scale }); + stage.position({ x: padding * scale, y: padding * scale }); + + bgLayer.batchDraw(); + FeedbackManager.show('Arranged ' + bgImages.length + ' image(s)'); + } + }; + return stage; } }; \ No newline at end of file diff --git a/scripts/main.js b/scripts/main.js index bcaac40..69eece0 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -38,6 +38,10 @@ document.addEventListener('DOMContentLoaded', () => { RightPanelManager.autoPackTextures(stageRight, false); }); + document.getElementById('autoPackLeft').addEventListener('click', () => { + if (window.leftPanel) window.leftPanel.autoPackImages(); + }); + // Export button document.getElementById('exportRight').addEventListener('click', () => { const exportWidth = parseInt(document.getElementById('rightWidth').value);