// spriteOperations.ts import { type Sprite } from '@/application/types'; import { sprites, cellSize, canvas, ctx, columns } from '@/application/state'; import { logger, getSpriteOffset } from '@/application/utilities'; import { updateCanvasSize, renderSpritesheetPreview } from '@/application/canvasOperations'; /** * Add new sprites to the spritesheet */ export function addSprites(newSprites: Sprite[]) { if (newSprites.length === 0) { logger.warn('Attempted to add empty sprites array'); return; } try { // Validate sprites before adding them const validSprites = newSprites.filter(sprite => { if (!sprite.img || sprite.width <= 0 || sprite.height <= 0) { logger.error('Invalid sprite detected', sprite); return false; } return true; }); if (validSprites.length === 0) { logger.error('No valid sprites to add'); return; } sprites.value.push(...validSprites); sprites.value.sort((a, b) => a.uploadOrder - b.uploadOrder); // Update cell size before arranging sprites updateCellSize(); autoArrangeSprites(); } catch (error) { logger.error('Error adding sprites:', error); } } /** * Update the cell size based on the largest sprite dimensions */ export function updateCellSize() { if (sprites.value.length === 0) { return; } try { let maxWidth = 0; let maxHeight = 0; // Find the maximum dimensions across all sprites sprites.value.forEach(sprite => { if (!sprite.img || sprite.width <= 0 || sprite.height <= 0) { logger.warn('Sprite with invalid dimensions detected', sprite); return; } maxWidth = Math.max(maxWidth, sprite.width); maxHeight = Math.max(maxHeight, sprite.height); }); if (maxWidth === 0 || maxHeight === 0) { logger.error('Failed to calculate valid cell size'); return; } // Add a small buffer to ensure sprites fit completely (optional) const buffer = 0; // Increase if you want padding between sprites cellSize.width = maxWidth + buffer; cellSize.height = maxHeight + buffer; // Ensure all sprites are within their cell bounds after resize sprites.value.forEach((sprite, index) => { const column = index % columns.value; const row = Math.floor(index / columns.value); // Calculate base position for the sprite's cell const cellX = column * cellSize.width; const cellY = row * cellSize.height; // Center the sprite within its cell if smaller than cell size const offsetX = Math.floor((cellSize.width - sprite.width) / 2); const offsetY = Math.floor((cellSize.height - sprite.height) / 2); sprite.x = cellX + offsetX; sprite.y = cellY + offsetY; }); updateCanvasSize(); renderSpritesheetPreview(); } catch (error) { logger.error('Error updating cell size:', error); } } /** * Automatically arrange sprites in a grid */ export function autoArrangeSprites() { if (sprites.value.length === 0) { return; } try { if (cellSize.width <= 0 || cellSize.height <= 0) { logger.error('Invalid cell size for auto-arranging', cellSize); return; } // First update the canvas size to ensure it's large enough updateCanvasSize(); // Then position each sprite in its grid cell sprites.value.forEach((sprite, index) => { const column = index % columns.value; const row = Math.floor(index / columns.value); sprite.x = column * cellSize.width; sprite.y = row * cellSize.height; }); // Check if the canvas is ready before attempting to render if (!ctx.value || !canvas.value) { logger.warn('Canvas or context not available for rendering after auto-arrange'); return; } renderSpritesheetPreview(); } catch (error) { logger.error('Error auto-arranging sprites:', error); } } /** * Highlight a specific sprite by its ID */ export function highlightSprite(spriteId: string) { if (!ctx.value || !canvas.value) return; const sprite = sprites.value.find(s => s.id === spriteId); if (!sprite) return; // Calculate the cell coordinates const cellX = Math.floor(sprite.x / cellSize.width); const cellY = Math.floor(sprite.y / cellSize.height); // Briefly flash the cell ctx.value.save(); ctx.value.fillStyle = 'rgba(0, 150, 255, 0.3)'; ctx.value.fillRect(cellX * cellSize.width, cellY * cellSize.height, cellSize.width, cellSize.height); ctx.value.restore(); // Reset after a short delay setTimeout(() => { renderSpritesheetPreview(); }, 500); } /** * Clear all sprites and reset the canvas */ export function clearAllSprites(animation: any) { if (!canvas.value || !ctx.value) return; sprites.value = []; canvas.value.width = 400; canvas.value.height = 300; ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height); if (animation.canvas && animation.ctx) { animation.canvas.width = 200; animation.canvas.height = 200; animation.ctx.clearRect(0, 0, animation.canvas.width, animation.canvas.height); } animation.currentFrame = 0; } /** * Apply frame-specific offsets to the main sprite positions */ export function applyOffsetsToMainView(currentSpriteOffset: any) { sprites.value.forEach((sprite, index) => { const frameOffset = getSpriteOffset(index); if (frameOffset.x !== 0 || frameOffset.y !== 0) { // Update the sprite's position to include the offset sprite.x += frameOffset.x; sprite.y += frameOffset.y; // Reset the offset frameOffset.x = 0; frameOffset.y = 0; } }); // Reset current offset currentSpriteOffset.x = 0; currentSpriteOffset.y = 0; // Re-render the main view renderSpritesheetPreview(); }