add recursion

This commit is contained in:
Tomáš Mládek 2020-07-17 19:14:42 +02:00
parent 7cf393af71
commit 64fa7098a0
2 changed files with 92 additions and 25 deletions

View file

@ -1,7 +1,7 @@
import {CollageConfig, CollageMode, Segment} from "@/types";
import {choice, randint, range, shuffle} from "@/utils";
const collageModeType = [
export const collageModeType = [
"clean_grid", "chaos_grid",
"row", "irow",
"col", "icol",
@ -91,7 +91,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
name: "Regular Row",
minImages: 2,
getSegments: (ctx, config, images) => {
const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
const numImages = Math.min(images?.length || Infinity, config?.numImages || randint(4) + 2);
const segmentSize = [ctx.canvas.width / numImages, ctx.canvas.height];
return range(numImages).map((idx) => {
return {
@ -108,7 +108,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
name: "Irregular Row",
minImages: 2,
getSegments: (ctx, config, images) => {
const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
const numImages = Math.min(images?.length || Infinity, config?.numImages || randint(4) + 2);
const segmentSize = [ctx.canvas.width / numImages, ctx.canvas.height];
return range(numImages).map((idx) => {
const irregularWidth = images ?
@ -128,7 +128,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
name: "Regular Column",
minImages: 2,
getSegments: (ctx, config, images) => {
const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
const numImages = Math.min(images?.length || Infinity, config?.numImages || randint(4) + 2);
const segmentSize = [ctx.canvas.width, ctx.canvas.height / numImages];
return range(numImages).map((idx) => {
return {
@ -145,7 +145,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
name: "Irregular Column",
minImages: 2,
getSegments: (ctx, config, images) => {
const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
const numImages = Math.min(images?.length || Infinity, config?.numImages || randint(4) + 2);
const segmentSize = [ctx.canvas.width, ctx.canvas.height / numImages];
return range(numImages).map((idx) => {
const irregularHeight = images ?
@ -165,7 +165,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
name: "Constant factor concentric",
minImages: 2,
getSegments: (ctx, config, images) => {
const numImages = Math.min(images?.length || 0, config?.numImages || randint(4) + 2);
const numImages = Math.min(images?.length || Infinity, config?.numImages || randint(4) + 2);
let factor: number;
if (Math.random() > .5) {
factor = choice([1 / Math.sqrt(2), .5, .88]);
@ -188,7 +188,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
name: "Equally spaced concentric",
minImages: 2,
getSegments: (ctx, config, images) => {
const numImages = Math.min(images?.length || 0, config?.numImages || randint(2) + 2);
const numImages = Math.min(images?.length || Infinity, config?.numImages || randint(2) + 2);
return range(numImages).map((idx) => {
return {
x: ctx.canvas.width / 2,
@ -204,7 +204,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
name: "Blending",
minImages: 2,
getSegments: (ctx, config, images) => {
const numImages = Math.min(images?.length || 0, config?.numImages || randint(2) + 2);
const numImages = Math.min(images?.length || Infinity, config?.numImages || randint(2) + 2);
return range(numImages).map((_) => {
return {
x: ctx.canvas.width / 2,

View file

@ -18,7 +18,7 @@
<label v-for="(mode, idx) in modes"
:class="{disabled: images.length < mode.minImages,
selected: idx === currentModeType,
lastActive: idx === lastActiveModeType}">
lastActive: lastActiveModeTypes.includes(idx)}">
{{mode.name}}
<input type="radio" :value="idx" v-model="currentModeType">
</label>
@ -28,10 +28,16 @@
!SHUFFLE ALL!
<input type="radio" value="shuffle" v-model="currentModeType">
</label>
<label :class="{disabled: images.length < minImages,
selected: 'recursive' === currentModeType}">
#RECURSIVE#
<input type="radio" value="recursive" v-model="currentModeType">
</label>
</div>
<button :disabled="images.length < minImages" @click="renderCollage">REPAINT</button>
<hr>
<div class="config">
<template v-if="currentModeType !== 'recursive'">
<label class="config-numimages">
#N of images:
<input type="number" :min="minImages" :max="images.length"
@ -39,6 +45,18 @@
:disabled="Object.keys(forceConfig).includes('numImages')"
v-model="forceConfig.numImages || collageConfig.numImages">
</label>
</template>
<template v-else>
<label>
Recursion levels:
<input type="number" :min="1" :max="10" v-model="recursiveConfig.level">
</label>
<label>
<input type="checkbox" v-model="recursiveConfig.repeat">
Repeat images?
</label>
</template>
</div>
</div>
</div>
@ -47,10 +65,10 @@
<script lang="ts">
import {Component, Prop, Vue, Watch} from "vue-property-decorator";
import collageModes, {CollageModeType} from "../collages";
import {CollageConfig, CollageMode} from "@/types";
import {CollageConfig, CollageMode, Segment} from "@/types";
import {choice, shuffle} from "@/utils";
type DisplayCollageModeType = CollageModeType | & "shuffle";
type DisplayCollageModeType = CollageModeType | & "shuffle" | & "recursive";
@Component
export default class Collage extends Vue {
@ -63,12 +81,16 @@ export default class Collage extends Vue {
private collageConfig: CollageConfig = {
numImages: undefined
};
private recursiveConfig = {
level: 2,
repeat: true
};
private currentModeType: DisplayCollageModeType = "shuffle";
private lastActiveModeType: CollageModeType | null = null;
private lastActiveModeTypes: CollageModeType[] = [];
private modes = collageModes;
private get minImages() {
if (this.currentModeType === "shuffle") {
if (this.currentModeType === "shuffle" || this.currentModeType === "recursive") {
return Math.min(...Object.values(this.modes).map((mode) => mode.minImages));
} else {
return this.modes[this.currentModeType].minImages;
@ -76,7 +98,9 @@ export default class Collage extends Vue {
}
private get lastMode() {
return this.lastActiveModeType ? this.modes[this.lastActiveModeType] : undefined;
if (this.lastActiveModeTypes.length === 1) {
return this.modes[this.lastActiveModeTypes[0]];
}
}
private get forceConfig() {
@ -91,24 +115,67 @@ export default class Collage extends Vue {
@Watch("images")
@Watch("currentModeType")
@Watch("collageConfig", {deep: true})
@Watch("recursiveConfig", {deep: true})
private renderCollage() {
if (this.images.length >= this.minImages) {
this.reset();
this.lastActiveModeType = this.currentModeType === "shuffle" ? null : this.currentModeType;
let mode: CollageMode;
if (this.currentModeType === "shuffle") {
if (this.currentModeType !== "recursive") {
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.lastActiveModeTypes = [randomModeType];
mode = collageModes[randomModeType];
} else {
this.lastActiveModeTypes = [this.currentModeType];
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);
} else {
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];
this.lastActiveModeTypes = [];
const shuffledImages = shuffle(this.images);
const rootSegment: Segment = {x: 0, y: 0, w: this.context.canvas.width, h: this.context.canvas.height};
const processSegment = async (segment: Segment, level: number): Promise<ImageBitmap> => {
console.log(segment, level);
if (segment === rootSegment || level <= this.recursiveConfig.level - 1) {
let canvas = document.createElement("canvas");
canvas.width = segment.w;
canvas.height = segment.h;
let modeKey = choice(permissibleModeKeys);
console.log(modeKey);
this.lastActiveModeTypes.push(modeKey);
let mode = this.modes[modeKey];
let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
let segments = mode.getSegments(ctx);
console.log(segments);
let bitmaps = await Promise.all(segments.map((segment) => processSegment(segment, level + 1)));
mode.place(ctx, bitmaps, segments);
return await createImageBitmap(canvas);
} else {
if (this.recursiveConfig.repeat) {
return choice(shuffledImages);
} else {
if (shuffledImages.length > 0) {
return shuffledImages.pop() as ImageBitmap;
} else {
throw "RAN OUT OF IMAGES";
}
}
}
};
processSegment(rootSegment, 0).then((finalCollage) => {
console.log(finalCollage);
this.context.drawImage(finalCollage, 0, 0);
}).catch((error) => {
alert(error);
});
}
const shuffledImages = shuffle(this.images);
const segments = mode.getSegments(this.context, this.collageConfig, shuffledImages);
mode.place(this.context, shuffledImages, segments);
}
}