205 lines
5.6 KiB
TypeScript
205 lines
5.6 KiB
TypeScript
// 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();
|
|
}
|