import {CollageMode} from "@/types"; import {randint, shuffle} from "@/utils"; const collageModeType = ["grid", "row", "irow", "col", "icol", "center"] as const; export type CollageModeType = typeof collageModeType[number]; function cleanDraw(ctx: CanvasRenderingContext2D, image: ImageBitmap, x: number, y: number, w: number, h: number) { const scaleRatio = Math.max(w / image.width, h / image.height); ctx.drawImage( image, image.width / 2 - w / scaleRatio / 2, image.height / 2 - h / scaleRatio / 2, w / scaleRatio, h / scaleRatio, x - w / 2, y - h / 2, w, h ); } const modes: { [key in CollageModeType]: CollageMode } = { "grid": { name: "Regular Grid", minImages: 4, forceConfig: { numImages: 4 }, place: (ctx, images, config) => { const quadrantSize = [ctx.canvas.width / 2, ctx.canvas.height / 2]; const selectedImages = shuffle(images).slice(0, 4); shuffle(selectedImages.map((image, idx) => [image, idx] as [ImageBitmap, number])) .forEach(([image, idx]) => { let x!: number, y!: number; switch (idx) { case 0: x = ctx.canvas.width * .25; y = ctx.canvas.height * .25; break; case 1: x = ctx.canvas.width * .75; y = ctx.canvas.height * .25; break; case 2: x = ctx.canvas.width * .25; y = ctx.canvas.height * .75; break; case 3: x = ctx.canvas.width * .75; y = ctx.canvas.height * .75; break; } if (config.cleanCrops) { cleanDraw(ctx, image, x, y, quadrantSize[0], quadrantSize[1]); } else { const scaleRatio = Math.max(quadrantSize[0] / image.width, quadrantSize[1] / image.height); ctx.drawImage(image, x - (image.width * scaleRatio / 2), y - (image.height * scaleRatio / 2), image.width * scaleRatio, image.height * scaleRatio); } }); } }, "row": { name: "Regular Row", minImages: 2, forceConfig: { cleanCrops: true }, place: (ctx, images, config) => { const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2); const quadrantSize = [ctx.canvas.width / selectedImages.length, ctx.canvas.height]; selectedImages.forEach((image, idx) => { const x = idx * quadrantSize[0] + quadrantSize[0] / 2; const y = quadrantSize[1] / 2; cleanDraw(ctx, image, x, y, quadrantSize[0], quadrantSize[1]); }); } }, "irow": { name: "Irregular Row", minImages: 2, forceConfig: { cleanCrops: true }, place: (ctx, images, config) => { const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2); const quadrantSize = [ctx.canvas.width / selectedImages.length, ctx.canvas.height]; selectedImages.forEach((image, idx) => { const x = idx * quadrantSize[0] + quadrantSize[0] / 2; const y = quadrantSize[1] / 2; const w = Math.min(ctx.canvas.width / 2, quadrantSize[1] + Math.random() * (quadrantSize[1] - (quadrantSize[1] / image.height) * image.width)); cleanDraw(ctx, image, x, y, w, quadrantSize[1]); }); } }, "col": { name: "Regular Column", minImages: 2, forceConfig: { cleanCrops: true }, place: (ctx, images, config) => { const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2); const quadrantSize = [ctx.canvas.width, ctx.canvas.height / selectedImages.length]; selectedImages.forEach((image, idx) => { const x = quadrantSize[0] / 2; const y = idx * quadrantSize[1] + quadrantSize[1] / 2; cleanDraw(ctx, image, x, y, quadrantSize[0], quadrantSize[1]); }); } }, "icol": { name: "Irregular Column", minImages: 2, forceConfig: { cleanCrops: true }, place: (ctx, images, config) => { const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2); const quadrantSize = [ctx.canvas.width, ctx.canvas.height / selectedImages.length]; selectedImages.forEach((image, idx) => { const x = quadrantSize[0] / 2; const y = idx * quadrantSize[1] + quadrantSize[1] / 2; const h = Math.min(ctx.canvas.height / 2, quadrantSize[0] + Math.random() * (quadrantSize[0] - (quadrantSize[0] / image.width) * image.height)); cleanDraw(ctx, image, x, y, quadrantSize[0], h); }); }, }, "center": { name: "Concentric", minImages: 2, forceConfig: { cleanCrops: true }, place: (ctx, images, config) => { const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2); const x = ctx.canvas.width / 2; const y = ctx.canvas.height / 2; if (Math.random() < .66) { selectedImages.forEach((image, idx) => { cleanDraw( ctx, image, x, y, ctx.canvas.width - (ctx.canvas.width / selectedImages.length * idx), ctx.canvas.height - (ctx.canvas.height / selectedImages.length * idx), ); }); } else { let factor: number; if (Math.random() > .5) { const factors = [1 / Math.sqrt(2), .5, .88]; factor = factors[Math.floor(Math.random() * factors.length)]; } else { factor = 1 - (1 / selectedImages.length); } selectedImages.forEach((image, idx) => { const ratio = Math.pow(factor, idx); cleanDraw(ctx, image, x, y, ctx.canvas.width * ratio, ctx.canvas.height * ratio); }); } } } }; export default modes;