Files
layered-generative-art/index.ts
2024-03-22 01:33:37 +01:00

53 lines
1.5 KiB
TypeScript

import sharp, { type OverlayOptions } from 'sharp';
import { LAYERS, weightedChoice } from './layers';
const BASE_SHAPE_PATH = './assets/base_shape.png';
export type Trait = 'skin' | 'nose' | 'eyes' | 'mouth' | 'detail';
export type Layer = {
trait: Trait,
path: string,
chance?: number,
options: TraitOption[]
};
export type TraitOption = {
fileName: string,
weight?: number
};
async function generateRandomImage(fileName: string, orderedLayers: Layer[]) {
const layers: { input: string }[] = [];
for (const layer of orderedLayers) {
if (layer.chance && Math.random() > layer.chance) {
continue;
}
const choice = weightedChoice(layer.options);
layers.push({ input: `${layer.path}${choice}` });
}
const image = sharp(BASE_SHAPE_PATH).composite(layers);
await Bun.write(`./output/${fileName}`, await image.toBuffer());
}
for (let i = 0; i < 100; i++) {
await generateRandomImage(`characters/${i}.png`, LAYERS);
}
// combine into a single grid image
const characters: OverlayOptions[] = [];
for (let i = 0; i < 100; i++) {
characters.push({ input: `./output/characters/${i}.png`, left: (i % 10) * 16, top: Math.floor(i / 10.0) * 16 });
}
const grid = sharp({ create: { width: 160, height: 160, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } } }).composite(characters).png();
await Bun.write('./output/grid.png', await grid.toBuffer());
console.log('Finished generation');