major refactor (add segments) in preparation for recursive collages
This commit is contained in:
		
							parent
							
								
									3866522404
								
							
						
					
					
						commit
						7cf393af71
					
				
					 4 changed files with 189 additions and 135 deletions
				
			
		
							
								
								
									
										241
									
								
								src/collages.ts
									
										
									
									
									
								
							
							
						
						
									
										241
									
								
								src/collages.ts
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import {CollageMode} from "@/types";
 | 
					import {CollageConfig, CollageMode, Segment} from "@/types";
 | 
				
			||||||
import {choice, randint, shuffle} from "@/utils";
 | 
					import {choice, randint, range, shuffle} from "@/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const collageModeType = [
 | 
					const collageModeType = [
 | 
				
			||||||
    "clean_grid", "chaos_grid",
 | 
					    "clean_grid", "chaos_grid",
 | 
				
			||||||
| 
						 | 
					@ -22,27 +22,40 @@ function cleanDraw(ctx: CanvasRenderingContext2D, image: ImageBitmap,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getGridPoints(ctx: CanvasRenderingContext2D, idx: number) {
 | 
					function getGridSegments(ctx: CanvasRenderingContext2D, config?: CollageConfig) {
 | 
				
			||||||
    let x!: number, y!: number;
 | 
					    return [0, 1, 2, 3].map((idx) => {
 | 
				
			||||||
    switch (idx) {
 | 
					        let x!: number, y!: number;
 | 
				
			||||||
        case 0:
 | 
					        switch (idx) {
 | 
				
			||||||
            x = ctx.canvas.width * .25;
 | 
					            case 0:
 | 
				
			||||||
            y = ctx.canvas.height * .25;
 | 
					                x = ctx.canvas.width * .25;
 | 
				
			||||||
            break;
 | 
					                y = ctx.canvas.height * .25;
 | 
				
			||||||
        case 1:
 | 
					                break;
 | 
				
			||||||
            x = ctx.canvas.width * .75;
 | 
					            case 1:
 | 
				
			||||||
            y = ctx.canvas.height * .25;
 | 
					                x = ctx.canvas.width * .75;
 | 
				
			||||||
            break;
 | 
					                y = ctx.canvas.height * .25;
 | 
				
			||||||
        case 2:
 | 
					                break;
 | 
				
			||||||
            x = ctx.canvas.width * .25;
 | 
					            case 2:
 | 
				
			||||||
            y = ctx.canvas.height * .75;
 | 
					                x = ctx.canvas.width * .25;
 | 
				
			||||||
            break;
 | 
					                y = ctx.canvas.height * .75;
 | 
				
			||||||
        case 3:
 | 
					                break;
 | 
				
			||||||
            x = ctx.canvas.width * .75;
 | 
					            case 3:
 | 
				
			||||||
            y = ctx.canvas.height * .75;
 | 
					                x = ctx.canvas.width * .75;
 | 
				
			||||||
            break;
 | 
					                y = ctx.canvas.height * .75;
 | 
				
			||||||
    }
 | 
					                break;
 | 
				
			||||||
    return [x, y];
 | 
					        }
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            x, y,
 | 
				
			||||||
 | 
					            w: ctx.canvas.width / 2,
 | 
				
			||||||
 | 
					            h: ctx.canvas.height / 2
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function cleanPlace(ctx: CanvasRenderingContext2D, images: ImageBitmap[], segments: Segment[]) {
 | 
				
			||||||
 | 
					    segments.forEach((segment, idx) => {
 | 
				
			||||||
 | 
					        cleanDraw(ctx, images[idx], segment.x, segment.y, segment.w, segment.h);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const modes: { [key in CollageModeType]: CollageMode } = {
 | 
					const modes: { [key in CollageModeType]: CollageMode } = {
 | 
				
			||||||
| 
						 | 
					@ -52,14 +65,8 @@ const modes: { [key in CollageModeType]: CollageMode } = {
 | 
				
			||||||
        forceConfig: {
 | 
					        forceConfig: {
 | 
				
			||||||
            numImages: 4
 | 
					            numImages: 4
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: getGridSegments,
 | 
				
			||||||
            const quadrantSize = [ctx.canvas.width / 2, ctx.canvas.height / 2];
 | 
					        place: cleanPlace
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, 4);
 | 
					 | 
				
			||||||
            selectedImages.forEach((image, idx) => {
 | 
					 | 
				
			||||||
                const [x, y] = getGridPoints(ctx, idx);
 | 
					 | 
				
			||||||
                cleanDraw(ctx, image, x, y, quadrantSize[0], quadrantSize[1]);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "chaos_grid": {
 | 
					    "chaos_grid": {
 | 
				
			||||||
        name: "Irregular Grid",
 | 
					        name: "Irregular Grid",
 | 
				
			||||||
| 
						 | 
					@ -67,125 +74,149 @@ const modes: { [key in CollageModeType]: CollageMode } = {
 | 
				
			||||||
        forceConfig: {
 | 
					        forceConfig: {
 | 
				
			||||||
            numImages: 4
 | 
					            numImages: 4
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: getGridSegments,
 | 
				
			||||||
            const quadrantSize = [ctx.canvas.width / 2, ctx.canvas.height / 2];
 | 
					        place: (ctx, images, segments) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, 4);
 | 
					            const shuffledImages = shuffle(images);
 | 
				
			||||||
            shuffle(selectedImages.map((image, idx) => [image, idx] as [ImageBitmap, number]))
 | 
					            shuffle(segments.map((segment, idx) => [segment, idx] as [Segment, number]))
 | 
				
			||||||
              .forEach(([image, idx]) => {
 | 
					              .forEach(([segment, idx]) => {
 | 
				
			||||||
                  const [x, y] = getGridPoints(ctx, idx);
 | 
					                  const image = shuffledImages[idx];
 | 
				
			||||||
                  const scaleRatio = Math.max(quadrantSize[0] / image.width, quadrantSize[1] / image.height);
 | 
					                  const scaleRatio = Math.max(segment.w / image.width, segment.h / image.height);
 | 
				
			||||||
                  ctx.drawImage(image,
 | 
					                  ctx.drawImage(image,
 | 
				
			||||||
                    x - (image.width * scaleRatio / 2), y - (image.height * scaleRatio / 2),
 | 
					                    segment.x - (image.width * scaleRatio / 2), segment.y - (image.height * scaleRatio / 2),
 | 
				
			||||||
                    image.width * scaleRatio, image.height * scaleRatio);
 | 
					                    image.width * scaleRatio, image.height * scaleRatio);
 | 
				
			||||||
              });
 | 
					              });
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "row": {
 | 
					    "row": {
 | 
				
			||||||
        name: "Regular Row",
 | 
					        name: "Regular Row",
 | 
				
			||||||
        minImages: 2,
 | 
					        minImages: 2,
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: (ctx, config, images) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2);
 | 
					            const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
 | 
				
			||||||
            const quadrantSize = [ctx.canvas.width / selectedImages.length, ctx.canvas.height];
 | 
					            const segmentSize = [ctx.canvas.width / numImages, ctx.canvas.height];
 | 
				
			||||||
            selectedImages.forEach((image, idx) => {
 | 
					            return range(numImages).map((idx) => {
 | 
				
			||||||
                const x = idx * quadrantSize[0] + quadrantSize[0] / 2;
 | 
					                return {
 | 
				
			||||||
                const y = quadrantSize[1] / 2;
 | 
					                    x: idx * segmentSize[0] + segmentSize[0] / 2,
 | 
				
			||||||
                cleanDraw(ctx, image, x, y, quadrantSize[0], quadrantSize[1]);
 | 
					                    y: segmentSize[1] / 2,
 | 
				
			||||||
 | 
					                    w: segmentSize[0],
 | 
				
			||||||
 | 
					                    h: segmentSize[1]
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        place: cleanPlace
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "irow": {
 | 
					    "irow": {
 | 
				
			||||||
        name: "Irregular Row",
 | 
					        name: "Irregular Row",
 | 
				
			||||||
        minImages: 2,
 | 
					        minImages: 2,
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: (ctx, config, images) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2);
 | 
					            const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
 | 
				
			||||||
            const quadrantSize = [ctx.canvas.width / selectedImages.length, ctx.canvas.height];
 | 
					            const segmentSize = [ctx.canvas.width / numImages, ctx.canvas.height];
 | 
				
			||||||
            selectedImages.forEach((image, idx) => {
 | 
					            return range(numImages).map((idx) => {
 | 
				
			||||||
                const x = idx * quadrantSize[0] + quadrantSize[0] / 2;
 | 
					                const irregularWidth = images ?
 | 
				
			||||||
                const y = quadrantSize[1] / 2;
 | 
					                  segmentSize[0] + Math.random() * ((segmentSize[1] / images[idx].height * images[idx].width) - segmentSize[0]) :
 | 
				
			||||||
                const w = Math.min(ctx.canvas.width / 2,
 | 
					                  segmentSize[0] + Math.random() * segmentSize[0] * .5;
 | 
				
			||||||
                  quadrantSize[1] + Math.random() * (quadrantSize[1] - (quadrantSize[1] / image.height) * image.width));
 | 
					                return {
 | 
				
			||||||
                cleanDraw(ctx, image, x, y, w, quadrantSize[1]);
 | 
					                    x: idx * segmentSize[0] + segmentSize[0] / 2,
 | 
				
			||||||
 | 
					                    y: segmentSize[1] / 2,
 | 
				
			||||||
 | 
					                    w: Math.min(ctx.canvas.width / 2, irregularWidth),
 | 
				
			||||||
 | 
					                    h: segmentSize[1],
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        place: cleanPlace
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "col": {
 | 
					    "col": {
 | 
				
			||||||
        name: "Regular Column",
 | 
					        name: "Regular Column",
 | 
				
			||||||
        minImages: 2,
 | 
					        minImages: 2,
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: (ctx, config, images) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2);
 | 
					            const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
 | 
				
			||||||
            const quadrantSize = [ctx.canvas.width, ctx.canvas.height / selectedImages.length];
 | 
					            const segmentSize = [ctx.canvas.width, ctx.canvas.height / numImages];
 | 
				
			||||||
            selectedImages.forEach((image, idx) => {
 | 
					            return range(numImages).map((idx) => {
 | 
				
			||||||
                const x = quadrantSize[0] / 2;
 | 
					                return {
 | 
				
			||||||
                const y = idx * quadrantSize[1] + quadrantSize[1] / 2;
 | 
					                    x: segmentSize[0] / 2,
 | 
				
			||||||
                cleanDraw(ctx, image, x, y, quadrantSize[0], quadrantSize[1]);
 | 
					                    y: idx * segmentSize[1] + segmentSize[1] / 2,
 | 
				
			||||||
 | 
					                    w: segmentSize[0],
 | 
				
			||||||
 | 
					                    h: segmentSize[1]
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        place: cleanPlace
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "icol": {
 | 
					    "icol": {
 | 
				
			||||||
        name: "Irregular Column",
 | 
					        name: "Irregular Column",
 | 
				
			||||||
        minImages: 2,
 | 
					        minImages: 2,
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: (ctx, config, images) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2);
 | 
					            const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
 | 
				
			||||||
            const quadrantSize = [ctx.canvas.width, ctx.canvas.height / selectedImages.length];
 | 
					            const segmentSize = [ctx.canvas.width, ctx.canvas.height / numImages];
 | 
				
			||||||
            selectedImages.forEach((image, idx) => {
 | 
					            return range(numImages).map((idx) => {
 | 
				
			||||||
                const x = quadrantSize[0] / 2;
 | 
					                const irregularHeight = images ?
 | 
				
			||||||
                const y = idx * quadrantSize[1] + quadrantSize[1] / 2;
 | 
					                  segmentSize[1] + Math.random() * ((segmentSize[0] / images[idx].width * images[idx].height) - segmentSize[1]) :
 | 
				
			||||||
                const h = Math.min(ctx.canvas.height / 2,
 | 
					                  segmentSize[1] + Math.random() * segmentSize[1] * .5;
 | 
				
			||||||
                  quadrantSize[0] + Math.random() * (quadrantSize[0] - (quadrantSize[0] / image.width) * image.height));
 | 
					                return {
 | 
				
			||||||
                cleanDraw(ctx, image, x, y, quadrantSize[0], h);
 | 
					                    x: segmentSize[0] / 2,
 | 
				
			||||||
 | 
					                    y: idx * segmentSize[1] + segmentSize[1] / 2,
 | 
				
			||||||
 | 
					                    w: segmentSize[0],
 | 
				
			||||||
 | 
					                    h: Math.min(ctx.canvas.height / 2, irregularHeight),
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        place: cleanPlace
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "concentric_factor": {
 | 
					    "concentric_factor": {
 | 
				
			||||||
        name: "Constant factor concentric",
 | 
					        name: "Constant factor concentric",
 | 
				
			||||||
        minImages: 2,
 | 
					        minImages: 2,
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: (ctx, config, images) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2);
 | 
					            const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
 | 
				
			||||||
            const x = ctx.canvas.width / 2;
 | 
					 | 
				
			||||||
            const y = ctx.canvas.height / 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let factor: number;
 | 
					            let factor: number;
 | 
				
			||||||
            if (Math.random() > .5) {
 | 
					            if (Math.random() > .5) {
 | 
				
			||||||
                factor = choice([1 / Math.sqrt(2), .5, .88]);
 | 
					                factor = choice([1 / Math.sqrt(2), .5, .88]);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                factor = 1 - (1 / selectedImages.length);
 | 
					                factor = 1 - (1 / numImages);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            selectedImages.forEach((image, idx) => {
 | 
					            return range(numImages).map((idx) => {
 | 
				
			||||||
                const ratio = Math.pow(factor, idx);
 | 
					                const ratio = Math.pow(factor, idx);
 | 
				
			||||||
                cleanDraw(ctx, image, x, y, ctx.canvas.width * ratio, ctx.canvas.height * ratio);
 | 
					                return {
 | 
				
			||||||
 | 
					                    x: ctx.canvas.width / 2,
 | 
				
			||||||
 | 
					                    y: ctx.canvas.height / 2,
 | 
				
			||||||
 | 
					                    w: ctx.canvas.width * ratio,
 | 
				
			||||||
 | 
					                    h: ctx.canvas.height * ratio
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        place: cleanPlace
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "concentric_spaced": {
 | 
					    "concentric_spaced": {
 | 
				
			||||||
        name: "Equally spaced concentric",
 | 
					        name: "Equally spaced concentric",
 | 
				
			||||||
        minImages: 2,
 | 
					        minImages: 2,
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: (ctx, config, images) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, config.numImages || randint(4) + 2);
 | 
					            const numImages = Math.min(images?.length || 0, config?.numImages || randint(2) + 2);
 | 
				
			||||||
 | 
					            return range(numImages).map((idx) => {
 | 
				
			||||||
            selectedImages.forEach((image, idx) => {
 | 
					                return {
 | 
				
			||||||
                cleanDraw(
 | 
					                    x: ctx.canvas.width / 2,
 | 
				
			||||||
                  ctx, image,
 | 
					                    y: ctx.canvas.height / 2,
 | 
				
			||||||
                  ctx.canvas.width / 2,
 | 
					                    w: ctx.canvas.width - (ctx.canvas.width / numImages * idx),
 | 
				
			||||||
                  ctx.canvas.height / 2,
 | 
					                    h: ctx.canvas.height - (ctx.canvas.height / numImages * idx),
 | 
				
			||||||
                  ctx.canvas.width - (ctx.canvas.width / selectedImages.length * idx),
 | 
					                };
 | 
				
			||||||
                  ctx.canvas.height - (ctx.canvas.height / selectedImages.length * idx),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        place: cleanPlace
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "blend": {
 | 
					    "blend": {
 | 
				
			||||||
        name: "Blending",
 | 
					        name: "Blending",
 | 
				
			||||||
        minImages: 2,
 | 
					        minImages: 2,
 | 
				
			||||||
        place: (ctx, images, config) => {
 | 
					        getSegments: (ctx, config, images) => {
 | 
				
			||||||
            const selectedImages = shuffle(images).slice(0, config.numImages || randint(2) + 2);
 | 
					            const numImages = Math.min(images?.length || 0, config?.numImages || randint(2) + 2);
 | 
				
			||||||
            ctx.globalCompositeOperation = choice(["difference", "saturation", "soft-light", "overlay"]);
 | 
					            return range(numImages).map((_) => {
 | 
				
			||||||
            selectedImages.forEach((image) => {
 | 
					                return {
 | 
				
			||||||
                cleanDraw(
 | 
					                    x: ctx.canvas.width / 2,
 | 
				
			||||||
                  ctx, image,
 | 
					                    y: ctx.canvas.height / 2,
 | 
				
			||||||
                  ctx.canvas.width / 2, ctx.canvas.height / 2,
 | 
					                    w: ctx.canvas.width,
 | 
				
			||||||
                  ctx.canvas.width, ctx.canvas.height
 | 
					                    h: ctx.canvas.height
 | 
				
			||||||
                );
 | 
					                };
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        place: (ctx, images, segments) => {
 | 
				
			||||||
 | 
					            ctx.globalCompositeOperation = choice(["difference", "saturation", "soft-light", "overlay"]);
 | 
				
			||||||
 | 
					            cleanPlace(ctx, images, segments);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,13 +22,19 @@
 | 
				
			||||||
                {{mode.name}}
 | 
					                {{mode.name}}
 | 
				
			||||||
                <input type="radio" :value="idx" v-model="currentModeType">
 | 
					                <input type="radio" :value="idx" v-model="currentModeType">
 | 
				
			||||||
            </label>
 | 
					            </label>
 | 
				
			||||||
 | 
					            <hr>
 | 
				
			||||||
 | 
					            <label :class="{disabled: images.length < minImages,
 | 
				
			||||||
 | 
					                            selected: 'shuffle' === currentModeType}">
 | 
				
			||||||
 | 
					                !SHUFFLE ALL!
 | 
				
			||||||
 | 
					                <input type="radio" value="shuffle" v-model="currentModeType">
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <button :disabled="images.length < currentMode.minImages" @click="renderCollage">REPAINT</button>
 | 
					        <button :disabled="images.length < minImages" @click="renderCollage">REPAINT</button>
 | 
				
			||||||
        <hr>
 | 
					        <hr>
 | 
				
			||||||
        <div class="config">
 | 
					        <div class="config">
 | 
				
			||||||
            <label class="config-numimages">
 | 
					            <label class="config-numimages">
 | 
				
			||||||
                #N of images:
 | 
					                #N of images:
 | 
				
			||||||
                <input type="number" :min="currentMode.minImages" :max="images.length"
 | 
					                <input type="number" :min="minImages" :max="images.length"
 | 
				
			||||||
                       placeholder="RND"
 | 
					                       placeholder="RND"
 | 
				
			||||||
                       :disabled="Object.keys(forceConfig).includes('numImages')"
 | 
					                       :disabled="Object.keys(forceConfig).includes('numImages')"
 | 
				
			||||||
                       v-model="forceConfig.numImages || collageConfig.numImages">
 | 
					                       v-model="forceConfig.numImages || collageConfig.numImages">
 | 
				
			||||||
| 
						 | 
					@ -42,6 +48,7 @@
 | 
				
			||||||
import {Component, Prop, Vue, Watch} from "vue-property-decorator";
 | 
					import {Component, Prop, Vue, Watch} from "vue-property-decorator";
 | 
				
			||||||
import collageModes, {CollageModeType} from "../collages";
 | 
					import collageModes, {CollageModeType} from "../collages";
 | 
				
			||||||
import {CollageConfig, CollageMode} from "@/types";
 | 
					import {CollageConfig, CollageMode} from "@/types";
 | 
				
			||||||
 | 
					import {choice, shuffle} from "@/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DisplayCollageModeType = CollageModeType | & "shuffle";
 | 
					type DisplayCollageModeType = CollageModeType | & "shuffle";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,30 +64,15 @@ export default class Collage extends Vue {
 | 
				
			||||||
        numImages: undefined
 | 
					        numImages: undefined
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    private currentModeType: DisplayCollageModeType = "shuffle";
 | 
					    private currentModeType: DisplayCollageModeType = "shuffle";
 | 
				
			||||||
    private lastActiveModeType: DisplayCollageModeType | null = null;
 | 
					    private lastActiveModeType: CollageModeType | null = null;
 | 
				
			||||||
    private modes: { [key in DisplayCollageModeType]: CollageMode } = {
 | 
					    private modes = collageModes;
 | 
				
			||||||
        ...collageModes,
 | 
					
 | 
				
			||||||
        "shuffle": {
 | 
					    private get minImages() {
 | 
				
			||||||
            name: "Shuffle all!",
 | 
					        if (this.currentModeType === "shuffle") {
 | 
				
			||||||
            minImages: Math.min(...Object.values(collageModes).map(m => m.minImages)),
 | 
					            return Math.min(...Object.values(this.modes).map((mode) => mode.minImages));
 | 
				
			||||||
            place: (ctx, images, config) => {
 | 
					        } else {
 | 
				
			||||||
                const permissibleModeKeys = Object.keys(collageModes)
 | 
					            return this.modes[this.currentModeType].minImages;
 | 
				
			||||||
                  .filter(k => collageModes[k as CollageModeType].minImages <= images.length) as CollageModeType[];
 | 
					 | 
				
			||||||
                const randomModeType = permissibleModeKeys[Math.floor(Math.random() * permissibleModeKeys.length)];
 | 
					 | 
				
			||||||
                const randomMode = collageModes[randomModeType];
 | 
					 | 
				
			||||||
                this.setLastActiveModeType(randomModeType);
 | 
					 | 
				
			||||||
                randomMode.place(ctx, images, config);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // wtf vue?
 | 
					 | 
				
			||||||
    private setLastActiveModeType(lastActiveModeType: any) {
 | 
					 | 
				
			||||||
        this.lastActiveModeType = lastActiveModeType;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private get currentMode() {
 | 
					 | 
				
			||||||
        return this.modes[this.currentModeType];
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private get lastMode() {
 | 
					    private get lastMode() {
 | 
				
			||||||
| 
						 | 
					@ -97,13 +89,26 @@ export default class Collage extends Vue {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Watch("images")
 | 
					    @Watch("images")
 | 
				
			||||||
    @Watch("currentMode")
 | 
					    @Watch("currentModeType")
 | 
				
			||||||
    @Watch("collageConfig", {deep: true})
 | 
					    @Watch("collageConfig", {deep: true})
 | 
				
			||||||
    private renderCollage() {
 | 
					    private renderCollage() {
 | 
				
			||||||
        if (this.images.length >= this.currentMode.minImages) {
 | 
					        if (this.images.length >= this.minImages) {
 | 
				
			||||||
            this.lastActiveModeType = this.currentModeType;
 | 
					 | 
				
			||||||
            this.reset();
 | 
					            this.reset();
 | 
				
			||||||
            this.currentMode.place(this.context, this.images, this.collageConfig);
 | 
					            this.lastActiveModeType = this.currentModeType === "shuffle" ? null : this.currentModeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mode: CollageMode;
 | 
				
			||||||
 | 
					            if (this.currentModeType === "shuffle") {
 | 
				
			||||||
 | 
					                const permissibleModeKeys = Object.keys(collageModes)
 | 
				
			||||||
 | 
					                  .filter(k => collageModes[k as CollageModeType].minImages <= this.images.length) as CollageModeType[];
 | 
				
			||||||
 | 
					                const randomModeType = choice(permissibleModeKeys);
 | 
				
			||||||
 | 
					                this.lastActiveModeType = randomModeType;
 | 
				
			||||||
 | 
					                mode = collageModes[randomModeType];
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                mode = this.modes[this.currentModeType];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const shuffledImages = shuffle(this.images);
 | 
				
			||||||
 | 
					            const segments = mode.getSegments(this.context, this.collageConfig, shuffledImages);
 | 
				
			||||||
 | 
					            mode.place(this.context, shuffledImages, segments);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,6 +161,12 @@ export default class Collage extends Vue {
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.controls .modes hr {
 | 
				
			||||||
 | 
					    margin-top: .5rem;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    color: lightgray;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.controls label {
 | 
					.controls label {
 | 
				
			||||||
    font-size: 14pt;
 | 
					    font-size: 14pt;
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/types.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								src/types.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,10 +1,18 @@
 | 
				
			||||||
export interface CollageMode {
 | 
					export interface CollageMode {
 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
    minImages: number;
 | 
					    minImages: number;
 | 
				
			||||||
    place: (ctx: CanvasRenderingContext2D, images: ImageBitmap[], config: CollageConfig) => void;
 | 
					    getSegments: (ctx: CanvasRenderingContext2D, config?: CollageConfig, images?: ImageBitmap[]) => Segment[];
 | 
				
			||||||
 | 
					    place: (ctx: CanvasRenderingContext2D, images: ImageBitmap[], segments: Segment[]) => void;
 | 
				
			||||||
    forceConfig?: CollageConfig;
 | 
					    forceConfig?: CollageConfig;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CollageConfig {
 | 
					export interface CollageConfig {
 | 
				
			||||||
    numImages?: number;
 | 
					    numImages?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface Segment {
 | 
				
			||||||
 | 
					    x: number;
 | 
				
			||||||
 | 
					    y: number;
 | 
				
			||||||
 | 
					    w: number;
 | 
				
			||||||
 | 
					    h: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,3 +21,7 @@ export function randint(n: number) {
 | 
				
			||||||
export function choice<T>(arr: T[]): T {
 | 
					export function choice<T>(arr: T[]): T {
 | 
				
			||||||
    return arr[randint(arr.length)];
 | 
					    return arr[randint(arr.length)];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function range(n: number): number[] {
 | 
				
			||||||
 | 
					    return Array.from({length: n}, (x, i) => i);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue