Files
pick-lunch/template/assets/branding/generate-logos.js

137 lines
3.7 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const { PNG } = require('pngjs');
const SIZE = 600;
function hexToRgb(hex) {
const clean = hex.replace('#', '');
return {
r: parseInt(clean.substring(0, 2), 16),
g: parseInt(clean.substring(2, 4), 16),
b: parseInt(clean.substring(4, 6), 16),
};
}
function setPixel(png, x, y, color) {
if (x < 0 || y < 0 || x >= png.width || y >= png.height) return;
const idx = (png.width * y + x) << 2;
png.data[idx] = color.r;
png.data[idx + 1] = color.g;
png.data[idx + 2] = color.b;
png.data[idx + 3] = 255;
}
function fillRect(png, x0, y0, x1, y1, color) {
const sx = Math.max(0, Math.floor(x0));
const sy = Math.max(0, Math.floor(y0));
const ex = Math.min(png.width - 1, Math.ceil(x1));
const ey = Math.min(png.height - 1, Math.ceil(y1));
for (let y = sy; y <= ey; y++) {
for (let x = sx; x <= ex; x++) setPixel(png, x, y, color);
}
}
function fillCircle(png, cx, cy, r, color) {
const rr = r * r;
const x0 = Math.floor(cx - r);
const x1 = Math.ceil(cx + r);
const y0 = Math.floor(cy - r);
const y1 = Math.ceil(cy + r);
for (let y = y0; y <= y1; y++) {
for (let x = x0; x <= x1; x++) {
const dx = x - cx;
const dy = y - cy;
if (dx * dx + dy * dy <= rr) setPixel(png, x, y, color);
}
}
}
function fillEllipse(png, cx, cy, rx, ry, color) {
const rx2 = rx * rx;
const ry2 = ry * ry;
const x0 = Math.floor(cx - rx);
const x1 = Math.ceil(cx + rx);
const y0 = Math.floor(cy - ry);
const y1 = Math.ceil(cy + ry);
for (let y = y0; y <= y1; y++) {
for (let x = x0; x <= x1; x++) {
const dx = x - cx;
const dy = y - cy;
if ((dx * dx) / rx2 + (dy * dy) / ry2 <= 1) setPixel(png, x, y, color);
}
}
}
function drawThickLine(png, x0, y0, x1, y1, thickness, color) {
const steps = Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0));
for (let i = 0; i <= steps; i++) {
const t = i / (steps || 1);
const x = Math.round(x0 + (x1 - x0) * t);
const y = Math.round(y0 + (y1 - y0) * t);
fillCircle(png, x, y, thickness / 2, color);
}
}
function drawLogo(filename, theme) {
const png = new PNG({ width: SIZE, height: SIZE });
const bg = hexToRgb(theme.bg);
const card = hexToRgb(theme.card);
const bowl = hexToRgb(theme.bowl);
const accent = hexToRgb(theme.accent);
fillRect(png, 0, 0, SIZE - 1, SIZE - 1, bg);
// Card background shape
fillRect(png, 80, 80, 520, 520, card);
fillCircle(png, 80, 80, 70, card);
fillCircle(png, 520, 80, 70, card);
fillCircle(png, 80, 520, 70, card);
fillCircle(png, 520, 520, 70, card);
// Bowl
fillEllipse(png, 300, 350, 165, 90, bowl);
fillRect(png, 135, 350, 465, 430, bowl);
fillEllipse(png, 300, 430, 165, 45, bowl);
// Bowl top cut
fillEllipse(png, 300, 340, 175, 68, card);
fillEllipse(png, 300, 346, 175, 4, accent);
// Noodles
fillEllipse(png, 250, 372, 50, 12, accent);
fillEllipse(png, 300, 374, 55, 13, accent);
fillEllipse(png, 350, 372, 50, 12, accent);
// Steam
fillEllipse(png, 245, 255, 14, 32, accent);
fillEllipse(png, 300, 235, 16, 38, accent);
fillEllipse(png, 355, 255, 14, 32, accent);
// Chopsticks
drawThickLine(png, 360, 205, 485, 330, 11, accent);
drawThickLine(png, 390, 188, 515, 313, 11, accent);
const outPath = path.join(__dirname, filename);
fs.writeFileSync(outPath, PNG.sync.write(png));
return outPath;
}
const light = drawLogo('lunch-pick-logo-light-600.png', {
bg: '#F8FAFC',
card: '#E0F2FE',
bowl: '#0EA5E9',
accent: '#0C4A6E',
});
const dark = drawLogo('lunch-pick-logo-dark-600.png', {
bg: '#0B1220',
card: '#13233C',
bowl: '#38BDF8',
accent: '#E2E8F0',
});
console.log(light);
console.log(dark);