add recursion
This commit is contained in:
parent
7cf393af71
commit
64fa7098a0
2 changed files with 92 additions and 25 deletions
|
@ -1,7 +1,7 @@
|
||||||
import {CollageConfig, CollageMode, Segment} from "@/types";
|
import {CollageConfig, CollageMode, Segment} from "@/types";
|
||||||
import {choice, randint, range, shuffle} from "@/utils";
|
import {choice, randint, range, shuffle} from "@/utils";
|
||||||
|
|
||||||
const collageModeType = [
|
export const collageModeType = [
|
||||||
"clean_grid", "chaos_grid",
|
"clean_grid", "chaos_grid",
|
||||||
"row", "irow",
|
"row", "irow",
|
||||||
"col", "icol",
|
"col", "icol",
|
||||||
|
@ -91,7 +91,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
|
||||||
name: "Regular Row",
|
name: "Regular Row",
|
||||||
minImages: 2,
|
minImages: 2,
|
||||||
getSegments: (ctx, config, images) => {
|
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];
|
const segmentSize = [ctx.canvas.width / numImages, ctx.canvas.height];
|
||||||
return range(numImages).map((idx) => {
|
return range(numImages).map((idx) => {
|
||||||
return {
|
return {
|
||||||
|
@ -108,7 +108,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
|
||||||
name: "Irregular Row",
|
name: "Irregular Row",
|
||||||
minImages: 2,
|
minImages: 2,
|
||||||
getSegments: (ctx, config, images) => {
|
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];
|
const segmentSize = [ctx.canvas.width / numImages, ctx.canvas.height];
|
||||||
return range(numImages).map((idx) => {
|
return range(numImages).map((idx) => {
|
||||||
const irregularWidth = images ?
|
const irregularWidth = images ?
|
||||||
|
@ -128,7 +128,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
|
||||||
name: "Regular Column",
|
name: "Regular Column",
|
||||||
minImages: 2,
|
minImages: 2,
|
||||||
getSegments: (ctx, config, images) => {
|
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];
|
const segmentSize = [ctx.canvas.width, ctx.canvas.height / numImages];
|
||||||
return range(numImages).map((idx) => {
|
return range(numImages).map((idx) => {
|
||||||
return {
|
return {
|
||||||
|
@ -145,7 +145,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
|
||||||
name: "Irregular Column",
|
name: "Irregular Column",
|
||||||
minImages: 2,
|
minImages: 2,
|
||||||
getSegments: (ctx, config, images) => {
|
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];
|
const segmentSize = [ctx.canvas.width, ctx.canvas.height / numImages];
|
||||||
return range(numImages).map((idx) => {
|
return range(numImages).map((idx) => {
|
||||||
const irregularHeight = images ?
|
const irregularHeight = images ?
|
||||||
|
@ -165,7 +165,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
|
||||||
name: "Constant factor concentric",
|
name: "Constant factor concentric",
|
||||||
minImages: 2,
|
minImages: 2,
|
||||||
getSegments: (ctx, config, images) => {
|
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;
|
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]);
|
||||||
|
@ -188,7 +188,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
|
||||||
name: "Equally spaced concentric",
|
name: "Equally spaced concentric",
|
||||||
minImages: 2,
|
minImages: 2,
|
||||||
getSegments: (ctx, config, images) => {
|
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 range(numImages).map((idx) => {
|
||||||
return {
|
return {
|
||||||
x: ctx.canvas.width / 2,
|
x: ctx.canvas.width / 2,
|
||||||
|
@ -204,7 +204,7 @@ const modes: { [key in CollageModeType]: CollageMode } = {
|
||||||
name: "Blending",
|
name: "Blending",
|
||||||
minImages: 2,
|
minImages: 2,
|
||||||
getSegments: (ctx, config, images) => {
|
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 range(numImages).map((_) => {
|
||||||
return {
|
return {
|
||||||
x: ctx.canvas.width / 2,
|
x: ctx.canvas.width / 2,
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<label v-for="(mode, idx) in modes"
|
<label v-for="(mode, idx) in modes"
|
||||||
:class="{disabled: images.length < mode.minImages,
|
:class="{disabled: images.length < mode.minImages,
|
||||||
selected: idx === currentModeType,
|
selected: idx === currentModeType,
|
||||||
lastActive: idx === lastActiveModeType}">
|
lastActive: lastActiveModeTypes.includes(idx)}">
|
||||||
{{mode.name}}
|
{{mode.name}}
|
||||||
<input type="radio" :value="idx" v-model="currentModeType">
|
<input type="radio" :value="idx" v-model="currentModeType">
|
||||||
</label>
|
</label>
|
||||||
|
@ -28,10 +28,16 @@
|
||||||
!SHUFFLE ALL!
|
!SHUFFLE ALL!
|
||||||
<input type="radio" value="shuffle" v-model="currentModeType">
|
<input type="radio" value="shuffle" v-model="currentModeType">
|
||||||
</label>
|
</label>
|
||||||
|
<label :class="{disabled: images.length < minImages,
|
||||||
|
selected: 'recursive' === currentModeType}">
|
||||||
|
#RECURSIVE#
|
||||||
|
<input type="radio" value="recursive" v-model="currentModeType">
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button :disabled="images.length < minImages" @click="renderCollage">REPAINT</button>
|
<button :disabled="images.length < minImages" @click="renderCollage">REPAINT</button>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="config">
|
<div class="config">
|
||||||
|
<template v-if="currentModeType !== 'recursive'">
|
||||||
<label class="config-numimages">
|
<label class="config-numimages">
|
||||||
#N of images:
|
#N of images:
|
||||||
<input type="number" :min="minImages" :max="images.length"
|
<input type="number" :min="minImages" :max="images.length"
|
||||||
|
@ -39,6 +45,18 @@
|
||||||
:disabled="Object.keys(forceConfig).includes('numImages')"
|
:disabled="Object.keys(forceConfig).includes('numImages')"
|
||||||
v-model="forceConfig.numImages || collageConfig.numImages">
|
v-model="forceConfig.numImages || collageConfig.numImages">
|
||||||
</label>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,10 +65,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
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, Segment} from "@/types";
|
||||||
import {choice, shuffle} from "@/utils";
|
import {choice, shuffle} from "@/utils";
|
||||||
|
|
||||||
type DisplayCollageModeType = CollageModeType | & "shuffle";
|
type DisplayCollageModeType = CollageModeType | & "shuffle" | & "recursive";
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class Collage extends Vue {
|
export default class Collage extends Vue {
|
||||||
|
@ -63,12 +81,16 @@ export default class Collage extends Vue {
|
||||||
private collageConfig: CollageConfig = {
|
private collageConfig: CollageConfig = {
|
||||||
numImages: undefined
|
numImages: undefined
|
||||||
};
|
};
|
||||||
|
private recursiveConfig = {
|
||||||
|
level: 2,
|
||||||
|
repeat: true
|
||||||
|
};
|
||||||
private currentModeType: DisplayCollageModeType = "shuffle";
|
private currentModeType: DisplayCollageModeType = "shuffle";
|
||||||
private lastActiveModeType: CollageModeType | null = null;
|
private lastActiveModeTypes: CollageModeType[] = [];
|
||||||
private modes = collageModes;
|
private modes = collageModes;
|
||||||
|
|
||||||
private get minImages() {
|
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));
|
return Math.min(...Object.values(this.modes).map((mode) => mode.minImages));
|
||||||
} else {
|
} else {
|
||||||
return this.modes[this.currentModeType].minImages;
|
return this.modes[this.currentModeType].minImages;
|
||||||
|
@ -76,7 +98,9 @@ export default class Collage extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get lastMode() {
|
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() {
|
private get forceConfig() {
|
||||||
|
@ -91,24 +115,67 @@ export default class Collage extends Vue {
|
||||||
@Watch("images")
|
@Watch("images")
|
||||||
@Watch("currentModeType")
|
@Watch("currentModeType")
|
||||||
@Watch("collageConfig", {deep: true})
|
@Watch("collageConfig", {deep: true})
|
||||||
|
@Watch("recursiveConfig", {deep: true})
|
||||||
private renderCollage() {
|
private renderCollage() {
|
||||||
if (this.images.length >= this.minImages) {
|
if (this.images.length >= this.minImages) {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.lastActiveModeType = this.currentModeType === "shuffle" ? null : this.currentModeType;
|
|
||||||
|
|
||||||
|
if (this.currentModeType !== "recursive") {
|
||||||
let mode: CollageMode;
|
let mode: CollageMode;
|
||||||
if (this.currentModeType === "shuffle") {
|
if (this.currentModeType === "shuffle") {
|
||||||
const permissibleModeKeys = Object.keys(collageModes)
|
const permissibleModeKeys = Object.keys(collageModes)
|
||||||
.filter(k => collageModes[k as CollageModeType].minImages <= this.images.length) as CollageModeType[];
|
.filter(k => collageModes[k as CollageModeType].minImages <= this.images.length) as CollageModeType[];
|
||||||
const randomModeType = choice(permissibleModeKeys);
|
const randomModeType = choice(permissibleModeKeys);
|
||||||
this.lastActiveModeType = randomModeType;
|
this.lastActiveModeTypes = [randomModeType];
|
||||||
mode = collageModes[randomModeType];
|
mode = collageModes[randomModeType];
|
||||||
} else {
|
} else {
|
||||||
|
this.lastActiveModeTypes = [this.currentModeType];
|
||||||
mode = this.modes[this.currentModeType];
|
mode = this.modes[this.currentModeType];
|
||||||
}
|
}
|
||||||
const shuffledImages = shuffle(this.images);
|
const shuffledImages = shuffle(this.images);
|
||||||
const segments = mode.getSegments(this.context, this.collageConfig, shuffledImages);
|
const segments = mode.getSegments(this.context, this.collageConfig, shuffledImages);
|
||||||
mode.place(this.context, shuffledImages, segments);
|
mode.place(this.context, shuffledImages, segments);
|
||||||
|
} else {
|
||||||
|
const permissibleModeKeys = Object.keys(collageModes)
|
||||||
|
.filter(k => collageModes[k as CollageModeType].minImages <= this.images.length) as CollageModeType[];
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue