diff --git a/src/App.vue b/src/App.vue index bfc4cad..6712951 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,29 +1,37 @@ - - - - + + + + diff --git a/src/collages.ts b/src/collages.ts new file mode 100644 index 0000000..64ad7cc --- /dev/null +++ b/src/collages.ts @@ -0,0 +1,129 @@ +import {CollageMode} from "@/types"; +import {randint, shuffle} from "@/utils"; + +const collageModeType = ["grid", "row", "irow", "col", "icol"] 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); + }); + }, + }, +}; + +export default modes; diff --git a/src/components/Collage.vue b/src/components/Collage.vue new file mode 100644 index 0000000..26bdf98 --- /dev/null +++ b/src/components/Collage.vue @@ -0,0 +1,190 @@ + + + + + + + Width: + + + + Height: + + + + + + + + {{mode.name}} ({{mode.minImages}}) + + + + REPAINT + + + + #N of images: + + + + + Crop cleanly + + + + + + + + + + diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue deleted file mode 100644 index 0cf3cc3..0000000 --- a/src/components/HelloWorld.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - {{ msg }} - - For a guide and recipes on how to configure / customize this project, - check out the - vue-cli documentation. - - Installed CLI Plugins - - babel - typescript - - Essential Links - - Core Docs - Forum - Community Chat - Twitter - News - - Ecosystem - - vue-router - vuex - vue-devtools - vue-loader - awesome-vue - - - - - - - - diff --git a/src/components/Picker.vue b/src/components/Picker.vue new file mode 100644 index 0000000..4a54527 --- /dev/null +++ b/src/components/Picker.vue @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..04695d0 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,11 @@ +export interface CollageMode { + name: string; + minImages: number; + place: (ctx: CanvasRenderingContext2D, images: ImageBitmap[], config: CollageConfig) => void; + forceConfig?: CollageConfig; +} + +export interface CollageConfig { + numImages?: number; + cleanCrops?: boolean; +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..b50d3b6 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,19 @@ +/** + * Shuffles array in place. + * @param {Array} a items An array containing the items. + */ +export function shuffle(a: T[]): T[] { + let j, x, i; + let b = Array.from(a); + for (i = b.length - 1; i > 0; i--) { + j = Math.floor(Math.random() * (i + 1)); + x = b[i]; + b[i] = b[j]; + b[j] = x; + } + return b; +} + +export function randint(n: number) { + return Math.floor(Math.random() * n); +}
- For a guide and recipes on how to configure / customize this project, - check out the - vue-cli documentation. -