feat: add extension implementation and docs
Add manifest.json, sidepanel components, and scripts. Include project assets and documentation files. Remove placeholder blank file.
This commit is contained in:
112
scripts/grid_generator.js
Normal file
112
scripts/grid_generator.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// Grid Generator logic
|
||||
// Ported from python engine/grid_generator.py
|
||||
|
||||
class GridGenerator {
|
||||
static BORDER_WIDTH = 5;
|
||||
static BORDER_COLOR = '#FFFFFF';
|
||||
|
||||
/**
|
||||
* Generates a grid image
|
||||
* @param {Array<HTMLImageElement>} images - Array of loaded image elements
|
||||
* @param {GridTemplate} template - The template to use
|
||||
* @param {Object} offsets - Optional offsets map { slotIndex: {x, y} }
|
||||
* @param {string} fakeNumber - Optional text to display on the last slot (e.g. "+5")
|
||||
* @returns {HTMLCanvasElement} The canvas with the generated grid
|
||||
*/
|
||||
static generate(images, template, offsets = {}, fakeNumber = null) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = template.canvasSize[0];
|
||||
canvas.height = template.canvasSize[1];
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Fill background with border color
|
||||
ctx.fillStyle = this.BORDER_COLOR;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const count = Math.min(images.length, template.slots.length);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const slot = template.slots[i];
|
||||
const img = images[i];
|
||||
|
||||
// Calculate the actual image size (subtract borders)
|
||||
const border = this.BORDER_WIDTH;
|
||||
const actualW = slot.w - (border * 2);
|
||||
const actualH = slot.h - (border * 2);
|
||||
const actualX = slot.x + border;
|
||||
const actualY = slot.y + border;
|
||||
|
||||
// Get offset for this slot (default to center 0,0)
|
||||
// Range [-1, 1]
|
||||
const offset = offsets[i] || { x: 0, y: 0 };
|
||||
|
||||
// Calculate crop
|
||||
const imgAspect = img.naturalWidth / img.naturalHeight;
|
||||
const slotAspect = actualW / actualH;
|
||||
|
||||
let sourceX, sourceY, sourceW, sourceH;
|
||||
|
||||
if (imgAspect > slotAspect) {
|
||||
// Image is wider - fit to height
|
||||
sourceH = img.naturalHeight;
|
||||
sourceW = sourceH * slotAspect;
|
||||
|
||||
const maxOffset = (img.naturalWidth - sourceW) / 2;
|
||||
const panX = maxOffset * offset.x;
|
||||
|
||||
sourceX = (img.naturalWidth - sourceW) / 2 + panX;
|
||||
sourceY = 0;
|
||||
} else {
|
||||
// Image is taller - fit to width
|
||||
sourceW = img.naturalWidth;
|
||||
sourceH = sourceW / slotAspect;
|
||||
|
||||
const maxOffset = (img.naturalHeight - sourceH) / 2;
|
||||
const panY = maxOffset * offset.y;
|
||||
|
||||
sourceX = 0;
|
||||
sourceY = (img.naturalHeight - sourceH) / 2 + panY;
|
||||
}
|
||||
|
||||
// Draw image to canvas
|
||||
ctx.drawImage(
|
||||
img,
|
||||
sourceX, sourceY, sourceW, sourceH, // Source
|
||||
actualX, actualY, actualW, actualH // Destination
|
||||
);
|
||||
}
|
||||
|
||||
// Draw fake number in last slot if provided
|
||||
if (template.slots.length > 0 && typeof fakeNumber === 'string' && fakeNumber.length > 0) {
|
||||
const lastSlotIndex = template.slots.length - 1;
|
||||
const lastSlot = template.slots[lastSlotIndex];
|
||||
|
||||
// Calculate slot dimensions (including border logic)
|
||||
const border = this.BORDER_WIDTH;
|
||||
const actualW = lastSlot.w - (border * 2);
|
||||
const actualH = lastSlot.h - (border * 2);
|
||||
const actualX = lastSlot.x + border;
|
||||
const actualY = lastSlot.y + border;
|
||||
|
||||
// 1. Draw Semi-Transparent Overlay
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; // 50% opacity black
|
||||
ctx.fillRect(actualX, actualY, actualW, actualH);
|
||||
|
||||
// 2. Draw Text centered
|
||||
// Responsive font size: 20% of slot height
|
||||
const fontSize = Math.floor(actualH * 0.2);
|
||||
ctx.font = `bold ${fontSize}px Arial, sans-serif`;
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
|
||||
// Calculate center of slot
|
||||
const centerX = actualX + (actualW / 2);
|
||||
const centerY = actualY + (actualH / 2);
|
||||
|
||||
ctx.fillText(fakeNumber, centerX, centerY);
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user