This commit is contained in:
2019-01-02 19:16:16 +01:00
commit e76aafc35a
34 changed files with 13596 additions and 0 deletions

24
ui/Components/Graph.vue Normal file
View File

@@ -0,0 +1,24 @@
<template>
<div>
<Node
v-for="(node, index) in graph"
:key="index"
:node="node"
/>
</div>
</template>
<script>
import Node from './Node.vue';
export default {
components: {
Node,
},
props: {
graph: {
type: Array,
required: true,
}
}
}
</script>

46
ui/Components/Node.vue Normal file
View File

@@ -0,0 +1,46 @@
<template>
<div
style="
border: 2px solid black;
margin: 5px;
padding: 2px;
display: flex;
flex-direction: row;
"
>
<div style="flex: 1">
<strong>Inputs:</strong>
<div v-for="(type, name) in node.in.variables" :key="name">
<div>{{ name }} <i>{{typeName(type)}}</i></div>
<Value :type="type" :value="node.in[name]" />
</div>
</div>
<div style="flex: 1">
<strong>Outputs:</strong>
<div v-for="(type, name) in node.out.variables" :key="name">
<div>{{ name }} <i>{{typeName(type)}}</i></div>
<Value :type="type" :value="node.out[name]" />
</div>
</div>
</div>
</template>
<script>
import Value from './Value';
export default {
components: {
Value,
},
props: {
node: {
type: Object,
required: true,
}
},
methods: {
typeName(type) {
return Array.isArray(type) ? 'Option' : type;
}
}
}
</script>

View File

@@ -0,0 +1,63 @@
<template>
<canvas
style="
width: 150px;
box-shadow: 0 0 6px;
"
@click="isRunning = !isRunning"
/>
</template>
<script>
import {paintToCanvas, mediaSize} from '../../../src/nodes/canvas.js'
export default {
props: {
value: {
type: null,
required: true,
}
},
watch: {
value: {
handler(value) {
this.redraw();
}
}
},
data() {
return {
isRunning: false,
};
},
mounted() {
this.$nextTick(() => {
this.redraw();
this.value.onchange(this.redraw);
})
},
methods: {
redraw() {
if (this.isRunning) {
const value = this.value.value;
/** @type {HTMLCanvasElement} */
const canvas = this.$el;
const ctx = canvas.getContext('2d');
if (!value) {
canvas.width = 1;
canvas.height = 1;
ctx.clearRect(0, 0, 1, 1);
} else {
const {width, height} = mediaSize(value);
canvas.width = width;
canvas.height = height;
paintToCanvas(canvas, value, {
top: 0,
left: 0,
width, height,
});
}
}
}
}
}
</script>

View File

@@ -0,0 +1,23 @@
<template>
<select v-model="value.value">
<option
v-for="t in type"
:value="t"
:key="t"
>{{ t }}</option>
</select>
</template>
<script>
export default {
props: {
type: {
type: Array,
required: true,
},
value: {
type: null,
required: true,
}
}
}
</script>

View File

@@ -0,0 +1,13 @@
<template>
<input v-model="value.value">
</template>
<script>
export default {
props: {
value: {
type: null,
required: true,
}
}
}
</script>

View File

@@ -0,0 +1,24 @@
import StringValue from './String.vue';
import ImageValue from './Image.vue';
import SelectValue from './Select.vue';
export default {
props: {
type: {
type: String,
required: true,
},
value: {
type: null,
required: true,
}
},
render(h) {
return h(
this.type === 'Image' ? ImageValue :
this.type === 'Number' ? StringValue :
Array.isArray(this.type) ? SelectValue :
null,
{props: {value: this.value, type: this.type}})
}
};

36
ui/graph.js Normal file
View File

@@ -0,0 +1,36 @@
import * as nodes from '../src/nodes';
import {sampleImage, getStream} from './helpers';
const resize = new nodes.Resize({
width: 1000,
height: 1000,
method: 'cover'
});
const brightnessContrast = new nodes.BrightnessContrast({})
const hsv = new nodes.HSV({})
const sepia = new nodes.Sepia({})
const compose = new nodes.Compose({
width: 1200,
height: 1800,
});
compose.in.fgX.value = 100;
compose.in.fgY.value = 100;
compose.in.bg.value = sampleImage(1200, 1800);
brightnessContrast.in.image.connect(resize.out.image);
hsv.in.image.connect(brightnessContrast.out.image);
sepia.in.image.connect(hsv.out.image);
compose.in.fg.connect(sepia.out.image);
getStream((video) => resize.in.image.value = video);
export default [
resize,
brightnessContrast,
hsv,
sepia,
compose,
];

35
ui/helpers.js Normal file
View File

@@ -0,0 +1,35 @@
export const sampleImage = (width, height) => {
const c = document.createElement('canvas');
c.width = width;
c.height = height;
const ctx = c.getContext('2d');
const grd = ctx.createLinearGradient(0, 0, width, height);
grd.addColorStop(0, "black");
grd.addColorStop(1, "white");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, width, height);
return c;
};
export const getStream = (callback) => {
navigator.getUserMedia({video: true}, (stream) => {
console.log(stream)
/** @type {HTMLVideoElement} */
const video = document.getElementById('Input');
video.srcObject = stream;
video.play();
let lastFrameTime = null;
const feedLoop = () => {
if (lastFrameTime !== video.currentTime) {
lastFrameTime = video.currentTime;
callback(video);
}
requestAnimationFrame(feedLoop);
};
feedLoop();
}, console.error.bind(console))
}

13
ui/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>UI</title>
</head>
<body>
<video id="Input" style="width: 100px"></video>
<div id="app"></div>
</body>
</html>

11
ui/index.js Normal file
View File

@@ -0,0 +1,11 @@
import Vue from 'vue';
import Graph from './Components/Graph.vue';
import graph from './graph';
new Vue({
el: '#app',
render(h) {
return h(Graph, {props: {graph}})
}
})