wip grid rework
Hunter
Wednesday, October 22nd, 2025 at 6:27 PM
9 const gridRect = grid.getBoundingClientRect();
10 return {
11 width: gridRect.width / GRID_COLS,
- height: gridRect.height / GRID_ROWS
12 + height: gridRect.height / GRID_ROWS,
13 + totalWidth: gridRect.width,
14 + totalHeight: gridRect.height
15 + };
16 +}
17 +
18 +// Calculate pixel-perfect position for a cell range
19 +// This accounts for grid cell borders (1px per cell) to ensure perfect alignment
20 +function getPixelPerfectBounds(cellX, cellY, cellWidth, cellHeight) {
21 + // Get all grid cells and measure their actual positions
22 + const gridCells = grid.querySelectorAll('.grid-cell');
23 +
24 + // Calculate the index of the top-left cell
25 + const startCellIndex = cellY * GRID_COLS + cellX;
26 + const startCell = gridCells[startCellIndex];
27 +
28 + if (!startCell) {
29 + // Fallback if cell doesn't exist
30 + const cellSize = getCellSize();
31 + return {
32 + left: cellX * cellSize.width,
33 + top: cellY * cellSize.height,
34 + width: cellWidth * cellSize.width,
35 + height: cellHeight * cellSize.height
36 + };
37 + }
38 +
39 + // Get the actual position of the start cell relative to the grid
40 + const gridRect = grid.getBoundingClientRect();
41 + const startCellRect = startCell.getBoundingClientRect();
42 +
43 + const left = startCellRect.left - gridRect.left;
44 + const top = startCellRect.top - gridRect.top;
45 +
46 + // Calculate end position by finding the bottom-right cell
47 + const endCellIndex = (cellY + cellHeight - 1) * GRID_COLS + (cellX + cellWidth - 1);
48 + const endCell = gridCells[endCellIndex];
49 +
50 + if (!endCell) {
51 + // Fallback if end cell doesn't exist
52 + const cellSize = getCellSize();
53 + return {
54 + left: left,
55 + top: top,
56 + width: cellWidth * cellSize.width,
57 + height: cellHeight * cellSize.height
58 + };
59 + }
60 +
61 + const endCellRect = endCell.getBoundingClientRect();
62 + const right = endCellRect.right - gridRect.left;
63 + const bottom = endCellRect.bottom - gridRect.top;
64 +
65 + return {
66 + left: left,
67 + top: top,
68 + width: right - left,
69 + height: bottom - top
70 };
71 }
72
250 imageData.naturalHeight = img.naturalHeight;
251
252 // 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;
253 + const bounds = getPixelPerfectBounds(imageData.xCell, imageData.yCell, imageData.widthCells, imageData.heightCells);
254 + const scaleX = bounds.width / img.naturalWidth;
255 + const scaleY = bounds.height / img.naturalHeight;
256 imageData.baseScale = Math.max(scaleX, scaleY);
257
258 updateImagePosition(imageData);
269
270 function calculatePanBounds(imageData) {
271 // Container dimensions in the current (possibly swapped) grid orientation
- const cellSize = getCellSize();
- const containerWidth = imageData.widthCells * cellSize.width;
- const containerHeight = imageData.heightCells * cellSize.height;
272 + const bounds = getPixelPerfectBounds(imageData.xCell, imageData.yCell, imageData.widthCells, imageData.heightCells);
273 + const containerWidth = bounds.width;
274 + const containerHeight = bounds.height;
275
276 if (imageData.naturalWidth === 0 || imageData.naturalHeight === 0) {
277 return { maxPanX: 0, maxPanY: 0 };
336 }
337
338 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';
339 + // Use pixel-perfect bounds to prevent subpixel accumulation
340 + const bounds = getPixelPerfectBounds(img.xCell, img.yCell, img.widthCells, img.heightCells);
341 + img.container.style.left = bounds.left + 'px';
342 + img.container.style.top = bounds.top + 'px';
343 + img.container.style.width = bounds.width + 'px';
344 + img.container.style.height = bounds.height + 'px';
345
346 // Store cell positions as CSS variables for print styles
347 img.container.style.setProperty('--x-cell', img.xCell);
369
370 // Recalculate baseScale if container size changed
371 if (img.naturalWidth > 0 && img.naturalHeight > 0) {
- const containerWidth = img.widthCells * cellSize.width;
- const containerHeight = img.heightCells * cellSize.height;
372 + // Use pixel-perfect bounds for container dimensions
373 + const containerWidth = bounds.width;
374 + const containerHeight = bounds.height;
375
376 // When rotated 90° or 270°, the image dimensions are effectively swapped
377 const isRotated90or270 = img.rotation % 180 !== 0;