2020-07-15 22:00:19 +02:00
|
|
|
import {CollageMode} from "@/types";
|
|
|
|
import {randint, shuffle} from "@/utils";
|
|
|
|
|
2020-07-15 23:05:15 +02:00
|
|
|
const collageModeType = ["grid", "row", "irow", "col", "icol", "concentric_factor", "concentric_spaced"] as const;
|
2020-07-15 22:00:19 +02:00
|
|
|
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);
|
2020-07-15 22:54:24 +02:00
|
|
|
ctx.drawImage(
|
|
|
|
image,
|
2020-07-15 22:00:19 +02:00
|
|
|
image.width / 2 - w / scaleRatio / 2, image.height / 2 - h / scaleRatio / 2,
|
|
|
|
w / scaleRatio, h / scaleRatio,
|
|
|
|
x - w / 2, y - h / 2,
|
2020-07-15 22:54:24 +02:00
|
|
|
w, h
|
|
|
|
);
|
2020-07-15 22:00:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
2020-07-15 23:05:15 +02:00
|
|
|
"concentric_factor": {
|
|
|
|
name: "Constant factor concentric",
|
2020-07-15 22:54:24 +02:00
|
|
|
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;
|
|
|
|
|
2020-07-15 23:05:15 +02:00
|
|
|
let factor: number;
|
|
|
|
if (Math.random() > .5) {
|
|
|
|
const factors = [1 / Math.sqrt(2), .5, .88];
|
|
|
|
factor = factors[Math.floor(Math.random() * factors.length)];
|
2020-07-15 22:54:24 +02:00
|
|
|
} else {
|
2020-07-15 23:05:15 +02:00
|
|
|
factor = 1 - (1 / selectedImages.length);
|
2020-07-15 22:54:24 +02:00
|
|
|
}
|
2020-07-15 23:05:15 +02:00
|
|
|
selectedImages.forEach((image, idx) => {
|
|
|
|
const ratio = Math.pow(factor, idx);
|
|
|
|
cleanDraw(ctx, image, x, y, ctx.canvas.width * ratio, ctx.canvas.height * ratio);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"concentric_spaced": {
|
|
|
|
name: "Equally spaced 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;
|
|
|
|
|
|
|
|
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),
|
|
|
|
);
|
|
|
|
});
|
2020-07-15 22:54:24 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-15 22:00:19 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export default modes;
|