commit 9df2b81a29dcaa2b5091dd0a0eb26f6fb7ae2e85
parent b3fd2836210c9e9738b2e204204aa5e75961dc4c
Author: Hunter
Date:   Wed, 22 Oct 2025 18:27:39 -0400

wip grid rework

Diffstat:
Mscript.js | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 73 insertions(+), 16 deletions(-)

diff --git a/script.js b/script.js @@ -9,7 +9,64 @@ function getCellSize() { const gridRect = grid.getBoundingClientRect(); return { width: gridRect.width / GRID_COLS, - height: gridRect.height / GRID_ROWS + height: gridRect.height / GRID_ROWS, + totalWidth: gridRect.width, + totalHeight: gridRect.height + }; +} + +// Calculate pixel-perfect position for a cell range +// This accounts for grid cell borders (1px per cell) to ensure perfect alignment +function getPixelPerfectBounds(cellX, cellY, cellWidth, cellHeight) { + // Get all grid cells and measure their actual positions + const gridCells = grid.querySelectorAll('.grid-cell'); + + // Calculate the index of the top-left cell + const startCellIndex = cellY * GRID_COLS + cellX; + const startCell = gridCells[startCellIndex]; + + if (!startCell) { + // Fallback if cell doesn't exist + const cellSize = getCellSize(); + return { + left: cellX * cellSize.width, + top: cellY * cellSize.height, + width: cellWidth * cellSize.width, + height: cellHeight * cellSize.height + }; + } + + // Get the actual position of the start cell relative to the grid + const gridRect = grid.getBoundingClientRect(); + const startCellRect = startCell.getBoundingClientRect(); + + const left = startCellRect.left - gridRect.left; + const top = startCellRect.top - gridRect.top; + + // Calculate end position by finding the bottom-right cell + const endCellIndex = (cellY + cellHeight - 1) * GRID_COLS + (cellX + cellWidth - 1); + const endCell = gridCells[endCellIndex]; + + if (!endCell) { + // Fallback if end cell doesn't exist + const cellSize = getCellSize(); + return { + left: left, + top: top, + width: cellWidth * cellSize.width, + height: cellHeight * cellSize.height + }; + } + + const endCellRect = endCell.getBoundingClientRect(); + const right = endCellRect.right - gridRect.left; + const bottom = endCellRect.bottom - gridRect.top; + + return { + left: left, + top: top, + width: right - left, + height: bottom - top }; } @@ -193,11 +250,9 @@ function addImage(src, xCell, yCell, widthCells, heightCells) { imageData.naturalHeight = img.naturalHeight; // Calculate base scale to cover container (mimics object-fit: cover) - const cellSize = getCellSize(); - const containerWidth = imageData.widthCells * cellSize.width; - const containerHeight = imageData.heightCells * cellSize.height; - const scaleX = containerWidth / img.naturalWidth; - const scaleY = containerHeight / img.naturalHeight; + const bounds = getPixelPerfectBounds(imageData.xCell, imageData.yCell, imageData.widthCells, imageData.heightCells); + const scaleX = bounds.width / img.naturalWidth; + const scaleY = bounds.height / img.naturalHeight; imageData.baseScale = Math.max(scaleX, scaleY); updateImagePosition(imageData); @@ -214,9 +269,9 @@ function addImage(src, xCell, yCell, widthCells, heightCells) { function calculatePanBounds(imageData) { // Container dimensions in the current (possibly swapped) grid orientation - const cellSize = getCellSize(); - const containerWidth = imageData.widthCells * cellSize.width; - const containerHeight = imageData.heightCells * cellSize.height; + const bounds = getPixelPerfectBounds(imageData.xCell, imageData.yCell, imageData.widthCells, imageData.heightCells); + const containerWidth = bounds.width; + const containerHeight = bounds.height; if (imageData.naturalWidth === 0 || imageData.naturalHeight === 0) { return { maxPanX: 0, maxPanY: 0 }; @@ -281,11 +336,12 @@ function adjustPanForZoom(imageData, cursorX, cursorY, oldTotalScale, newTotalSc } function updateImagePosition(img) { - const cellSize = getCellSize(); - img.container.style.left = img.xCell * cellSize.width + 'px'; - img.container.style.top = img.yCell * cellSize.height + 'px'; - img.container.style.width = img.widthCells * cellSize.width + 'px'; - img.container.style.height = img.heightCells * cellSize.height + 'px'; + // Use pixel-perfect bounds to prevent subpixel accumulation + const bounds = getPixelPerfectBounds(img.xCell, img.yCell, img.widthCells, img.heightCells); + img.container.style.left = bounds.left + 'px'; + img.container.style.top = bounds.top + 'px'; + img.container.style.width = bounds.width + 'px'; + img.container.style.height = bounds.height + 'px'; // Store cell positions as CSS variables for print styles img.container.style.setProperty('--x-cell', img.xCell); @@ -313,8 +369,9 @@ function updateImagePosition(img) { // Recalculate baseScale if container size changed if (img.naturalWidth > 0 && img.naturalHeight > 0) { - const containerWidth = img.widthCells * cellSize.width; - const containerHeight = img.heightCells * cellSize.height; + // Use pixel-perfect bounds for container dimensions + const containerWidth = bounds.width; + const containerHeight = bounds.height; // When rotated 90° or 270°, the image dimensions are effectively swapped const isRotated90or270 = img.rotation % 180 !== 0;