♻️ better types system
This commit is contained in:
@@ -5,54 +5,89 @@ export default class Compose extends Node {
|
|||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super('Compose', {
|
super('Compose', {
|
||||||
fg: 'Image',
|
fg: {
|
||||||
fgX: 'Number',
|
type: 'Image'
|
||||||
fgY: 'Number',
|
},
|
||||||
bg: 'Image',
|
fgX: {
|
||||||
bgX: 'Number',
|
type: 'Number',
|
||||||
bgY: 'Number',
|
default: 0,
|
||||||
width: 'Number',
|
step: 1,
|
||||||
height: 'Number',
|
},
|
||||||
mode: [
|
fgY: {
|
||||||
'source-over',
|
type: 'Number',
|
||||||
'source-in',
|
default: 0,
|
||||||
'source-out',
|
step: 1,
|
||||||
'source-atop',
|
},
|
||||||
'destination-over',
|
bg: {
|
||||||
'destination-in',
|
type: 'Image',
|
||||||
'destination-out',
|
},
|
||||||
'destination-atop',
|
bgX: {
|
||||||
'lighter',
|
type: 'Number',
|
||||||
'copy',
|
default: 0,
|
||||||
'xor',
|
step: 1,
|
||||||
'multiply',
|
},
|
||||||
'screen',
|
bgY: {
|
||||||
'overlay',
|
type: 'Number',
|
||||||
'darken',
|
default: 0,
|
||||||
'lighten',
|
step: 1,
|
||||||
'color-dodge',
|
},
|
||||||
'color-burn',
|
width: {
|
||||||
'hard-light',
|
type: 'Number',
|
||||||
'soft-light',
|
default: 100,
|
||||||
'difference',
|
step: 1,
|
||||||
'exclusion',
|
min: 1
|
||||||
'hue',
|
},
|
||||||
'saturation',
|
height: {
|
||||||
'color',
|
type: 'Number',
|
||||||
'luminosity',
|
default: 100,
|
||||||
]
|
step: 1,
|
||||||
|
min: 1,
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: 'String',
|
||||||
|
default: 'source-over',
|
||||||
|
enum: [
|
||||||
|
'source-over',
|
||||||
|
'source-in',
|
||||||
|
'source-out',
|
||||||
|
'source-atop',
|
||||||
|
'destination-over',
|
||||||
|
'destination-in',
|
||||||
|
'destination-out',
|
||||||
|
'destination-atop',
|
||||||
|
'lighter',
|
||||||
|
'copy',
|
||||||
|
'xor',
|
||||||
|
'multiply',
|
||||||
|
'screen',
|
||||||
|
'overlay',
|
||||||
|
'darken',
|
||||||
|
'lighten',
|
||||||
|
'color-dodge',
|
||||||
|
'color-burn',
|
||||||
|
'hard-light',
|
||||||
|
'soft-light',
|
||||||
|
'difference',
|
||||||
|
'exclusion',
|
||||||
|
'hue',
|
||||||
|
'saturation',
|
||||||
|
'color',
|
||||||
|
'luminosity',
|
||||||
|
]
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
image: 'Image'
|
image: {
|
||||||
});
|
type: 'Image'
|
||||||
this.options = options;
|
}
|
||||||
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
return this.__in.width.value || this.options.width;
|
return this.__in.width.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get height() {
|
get height() {
|
||||||
return this.__in.height.value || this.options.height;
|
return this.__in.height.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fg() {
|
get fg() {
|
||||||
@@ -85,11 +120,10 @@ export default class Compose extends Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get mode() {
|
|
||||||
return this.__in.mode.value || 'source-over';
|
|
||||||
}
|
|
||||||
|
|
||||||
__update() {
|
__update() {
|
||||||
|
if (!this.width || !this.height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const canvas = createCanvas(this.width, this.height);
|
const canvas = createCanvas(this.width, this.height);
|
||||||
|
|
||||||
if (this.bg) {
|
if (this.bg) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import {Inputs, Outputs} from './io.js';
|
import {Inputs, Outputs} from './io';
|
||||||
import uuidv4 from 'uuid/v4';
|
import uuidv4 from 'uuid/v4';
|
||||||
|
|
||||||
export default class Node {
|
export default class Node {
|
||||||
|
|
||||||
constructor(name, inputNames, outputNames) {
|
constructor(name, inputDefinition, outputDefiniton, options) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.id = uuidv4();
|
this.id = uuidv4();
|
||||||
this.__in = new Inputs(inputNames, this);
|
this.__in = new Inputs(inputDefinition, this);
|
||||||
this.__out = new Outputs(outputNames, this);
|
this.__out = new Outputs(outputDefiniton, this);
|
||||||
this.__in.update = (name) => this.__update([name]);
|
this.__in.update = (name) => this.__update([name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,21 +5,33 @@ export default class Resize extends Node {
|
|||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super('Resize', {
|
super('Resize', {
|
||||||
image: 'Image',
|
image: {
|
||||||
width: 'Number',
|
type: 'Image',
|
||||||
height: 'Number',
|
},
|
||||||
|
width: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 100,
|
||||||
|
min: 1,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 100,
|
||||||
|
min: 1,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
image: 'Image'
|
image: {
|
||||||
|
type: 'Image',
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
return this.__in.width.value || this.options.width;
|
return this.__in.width.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get height() {
|
get height() {
|
||||||
return this.__in.height.value || this.options.height;
|
return this.__in.height.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
__update() {
|
__update() {
|
||||||
|
|||||||
@@ -5,8 +5,14 @@ export default class BrightnessContrast extends WebGL {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('BrightnessContrast', {
|
super('BrightnessContrast', {
|
||||||
brightness: 'Number',
|
brightness: {
|
||||||
contrast: 'Number',
|
type: 'Number',
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
contrast: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,23 +29,24 @@ export default class BrightnessContrast extends WebGL {
|
|||||||
uniform vec2 u_brightnessContrast;
|
uniform vec2 u_brightnessContrast;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 c = texture2D(u_image, v_texCoord).rgb * u_brightnessContrast[0];
|
vec4 pixelColor = texture2D(u_image, v_texCoord);
|
||||||
|
vec3 c = pixelColor.rgb * u_brightnessContrast[0];
|
||||||
gl_FragColor = vec4(
|
gl_FragColor = vec4(
|
||||||
clamp(
|
clamp(
|
||||||
(c - 0.5) * u_brightnessContrast[1] + 0.5,
|
(c - 0.5) * u_brightnessContrast[1] + 0.5,
|
||||||
0.0, 1.0),
|
0.0, 1.0),
|
||||||
1
|
pixelColor.a
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
get contrast() {
|
get contrast() {
|
||||||
return this.in.contrast.value || 1;
|
return this.in.contrast.value
|
||||||
}
|
}
|
||||||
|
|
||||||
get brightness() {
|
get brightness() {
|
||||||
return this.in.brightness.value || 1;
|
return this.in.brightness.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setParams(gl, program) {
|
_setParams(gl, program) {
|
||||||
|
|||||||
@@ -13,11 +13,30 @@ export default class GreenScreen extends WebGL {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('GreenScreen', {
|
super('GreenScreen', {
|
||||||
balance: 'Number',
|
balance: {
|
||||||
screen: 'Color',
|
type: 'Number',
|
||||||
screenWeight: 'Number',
|
default: 0.5,
|
||||||
clipBlack: 'Number',
|
step: 0.1,
|
||||||
clipWhite: 'Number',
|
},
|
||||||
|
screen: {
|
||||||
|
type: 'Color',
|
||||||
|
default: '#2CD6A4',
|
||||||
|
},
|
||||||
|
screenWeight: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 1,
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
step: 0.1,
|
||||||
|
},
|
||||||
|
clipBlack: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
clipWhite: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,13 +71,13 @@ export default class GreenScreen extends WebGL {
|
|||||||
float screenFmax = max(max(screen.r, screen.g), screen.b); //Max. value of RGB
|
float screenFmax = max(max(screen.r, screen.g), screen.b); //Max. value of RGB
|
||||||
vec3 screenPrimary = step(screenFmax, screen.rgb);
|
vec3 screenPrimary = step(screenFmax, screen.rgb);
|
||||||
float screenSecondaryComponents = dot(1.0 - screenPrimary, screen.rgb);
|
float screenSecondaryComponents = dot(1.0 - screenPrimary, screen.rgb);
|
||||||
float screenSat = fmax - mix(secondaryComponents - fmin, secondaryComponents / 2.0, balance);
|
float screenSat = screenFmax - mix(screenSecondaryComponents - screenFmin, screenSecondaryComponents / 2.0, balance);
|
||||||
|
|
||||||
pixelSat = fmax - mix(secondaryComponents - fmin, secondaryComponents / 2.0, balance);
|
pixelSat = fmax - mix(secondaryComponents - fmin, secondaryComponents / 2.0, balance);
|
||||||
|
|
||||||
// solid pixel if primary color component is not the same as the screen color
|
// solid pixel if primary color component is not the same as the screen color
|
||||||
float diffPrimary = dot(abs(pixelPrimary - screenPrimary), vec3(1.0));
|
float diffPrimary = dot(abs(pixelPrimary - screenPrimary), vec3(1.0));
|
||||||
float solid = step(1.0, step(pixelSat, 0.1) + step(fmax, 0.1) + diffPrimary);
|
float solid = step(1.0, pixelSat + step(fmax, 0.1) + diffPrimary);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Semi-transparent pixel if the primary component matches but if saturation is less
|
Semi-transparent pixel if the primary component matches but if saturation is less
|
||||||
@@ -77,26 +96,27 @@ export default class GreenScreen extends WebGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get clipBlack() {
|
get clipBlack() {
|
||||||
return (this.in.clipBlack.value || 0);
|
return this.in.clipBlack.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get clipWhite() {
|
get clipWhite() {
|
||||||
return (this.in.clipWhite.value || 0);
|
return this.in.clipWhite.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get screenWeight() {
|
get screenWeight() {
|
||||||
return (this.in.screenWeight.value || 1);
|
return this.in.screenWeight.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get screen() {
|
get screen() {
|
||||||
return hexColorTOvec3(this.in.screen.value || '#00FF00');
|
return hexColorTOvec3(this.in.screen.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get balance() {
|
get balance() {
|
||||||
return (this.in.balance.value || 0.5);
|
return this.in.balance.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setParams(gl, program) {
|
_setParams(gl, program) {
|
||||||
|
console.log(this.balance, this.clipBlack, this.clipWhite, this.screenWeight)
|
||||||
gl.uniform3f(gl.getUniformLocation(program, 'screen'), ...this.screen);
|
gl.uniform3f(gl.getUniformLocation(program, 'screen'), ...this.screen);
|
||||||
gl.uniform1f(gl.getUniformLocation(program, 'balance'), this.balance);
|
gl.uniform1f(gl.getUniformLocation(program, 'balance'), this.balance);
|
||||||
gl.uniform1f(gl.getUniformLocation(program, 'clipBlack'), this.clipBlack);
|
gl.uniform1f(gl.getUniformLocation(program, 'clipBlack'), this.clipBlack);
|
||||||
|
|||||||
@@ -5,9 +5,18 @@ export default class HSV extends WebGL {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('HSV', {
|
super('HSV', {
|
||||||
hue: 'Number',
|
hue: {
|
||||||
saturation: 'Number',
|
type: 'Number',
|
||||||
value: 'Number',
|
default: 0,
|
||||||
|
},
|
||||||
|
saturation: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 1,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,15 +59,15 @@ export default class HSV extends WebGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get hue() {
|
get hue() {
|
||||||
return ((this.in.hue.value || 0) % 360) / 360;
|
return ((this.in.hue.value) % 360) / 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
get saturation() {
|
get saturation() {
|
||||||
return this.in.saturation.value || 1;
|
return this.in.saturation.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return this.in.value.value || 1;
|
return this.in.value.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setParams(gl, program) {
|
_setParams(gl, program) {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ export default class Sepia extends WebGL {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Sepia', {
|
super('Sepia', {
|
||||||
amount: 'Number',
|
amount: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +25,7 @@ export default class Sepia extends WebGL {
|
|||||||
uniform float amount;
|
uniform float amount;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 color = texture2D(u_image, v_texCoord).rgb;
|
vec4 color = texture2D(u_image, v_texCoord);
|
||||||
float r = color.r;
|
float r = color.r;
|
||||||
float g = color.g;
|
float g = color.g;
|
||||||
float b = color.b;
|
float b = color.b;
|
||||||
@@ -31,13 +34,13 @@ export default class Sepia extends WebGL {
|
|||||||
color.g = min(1.0, (r * 0.349 * amount) + (g * (1.0 - (0.314 * amount))) + (b * 0.168 * amount));
|
color.g = min(1.0, (r * 0.349 * amount) + (g * (1.0 - (0.314 * amount))) + (b * 0.168 * amount));
|
||||||
color.b = min(1.0, (r * 0.272 * amount) + (g * 0.534 * amount) + (b * (1.0 - (0.869 * amount))));
|
color.b = min(1.0, (r * 0.272 * amount) + (g * 0.534 * amount) + (b * (1.0 - (0.869 * amount))));
|
||||||
|
|
||||||
gl_FragColor = vec4(color, 1);
|
gl_FragColor = color;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
get amount() {
|
get amount() {
|
||||||
return this.in.amount.value || 0;
|
return this.in.amount.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ export default class WebGL extends Node {
|
|||||||
|
|
||||||
constructor(name, inputs={}) {
|
constructor(name, inputs={}) {
|
||||||
super(name, Object.assign({
|
super(name, Object.assign({
|
||||||
image: 'Image',
|
image: {
|
||||||
|
type: 'Image',
|
||||||
|
},
|
||||||
}, inputs), {
|
}, inputs), {
|
||||||
image: 'Image'
|
image: {
|
||||||
|
type: 'Image',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ export default class ToBlob extends Node {
|
|||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super('Webcam', {}, {
|
super('Webcam', {}, {
|
||||||
image: 'Image',
|
image: {
|
||||||
|
type: 'Image'
|
||||||
|
},
|
||||||
});
|
});
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
@@ -16,8 +18,8 @@ export default class ToBlob extends Node {
|
|||||||
console.log(last(devices));
|
console.log(last(devices));
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: {
|
video: {
|
||||||
width: { min: 1024, ideal: 1280, max: 1920 },
|
width: { min: 1024, ideal: 1920, max: 1920 },
|
||||||
height: { min: 776, ideal: 720, max: 1080 },
|
height: { min: 776, ideal: 1080, max: 1080 },
|
||||||
deviceId: last(devices).deviceId
|
deviceId: last(devices).deviceId
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
12
src/nodes/io.d.ts
vendored
12
src/nodes/io.d.ts
vendored
@@ -1,12 +0,0 @@
|
|||||||
import * as types from './types'
|
|
||||||
|
|
||||||
|
|
||||||
export class Inputs<T extends string> {
|
|
||||||
constructor(variables: T[]);
|
|
||||||
[k: string]: types.Input<any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Outputs<T extends string> {
|
|
||||||
constructor(variables: T[]);
|
|
||||||
[k: string]: types.Input<any>
|
|
||||||
}
|
|
||||||
175
src/nodes/io.js
175
src/nodes/io.js
@@ -1,175 +0,0 @@
|
|||||||
import * as types from './types';
|
|
||||||
|
|
||||||
|
|
||||||
export class Input {
|
|
||||||
|
|
||||||
constructor(name, owner) {
|
|
||||||
this.__output = null;
|
|
||||||
this.__value = null;
|
|
||||||
this.__name = name;
|
|
||||||
this.__owner = owner
|
|
||||||
this.__listeners = [];
|
|
||||||
this.__onchangelistener = (value) => {
|
|
||||||
this.value = value;
|
|
||||||
this.__notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
|
||||||
return `${this.__owner.id}-${this.__name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this.__name;
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
return this.__value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set value(value) {
|
|
||||||
this.__value = value;
|
|
||||||
this.__owner.update(this.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(output) {
|
|
||||||
this.disconnect();
|
|
||||||
this.__output = output
|
|
||||||
this.__output.onchange(this.__onchangelistener);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(output) {
|
|
||||||
if (this.__output) {
|
|
||||||
this.__output.offchange(this.__onchangelistener);
|
|
||||||
this.__output = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get output() {
|
|
||||||
return this.__output;
|
|
||||||
}
|
|
||||||
|
|
||||||
__notifyListeners() {
|
|
||||||
for (let listener of this.__listeners) {
|
|
||||||
listener(this.value, this.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onchange(listener) {
|
|
||||||
this.__listeners.push(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
offchange(listener) {
|
|
||||||
this.__listeners = this.__listeners
|
|
||||||
.filter((l) => l !== listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
return {
|
|
||||||
value: this.__output ? null : this.__value,
|
|
||||||
output: this.__output ? this.__output.id: null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Output {
|
|
||||||
|
|
||||||
constructor(name, owner) {
|
|
||||||
this.__value = null;
|
|
||||||
this.__name = name;
|
|
||||||
this.__owner = owner
|
|
||||||
this.__listeners = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
|
||||||
return `${this.__owner.id}-${this.__name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this.__name;
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
return this.__value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set value(value) {
|
|
||||||
this.__value = value;
|
|
||||||
this.__notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
__notifyListeners() {
|
|
||||||
for (let listener of this.__listeners) {
|
|
||||||
listener(this.value, this.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onchange(listener) {
|
|
||||||
this.__listeners.push(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
offchange(listener) {
|
|
||||||
this.__listeners = this.__listeners
|
|
||||||
.filter((l) => l !== listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class Inputs {
|
|
||||||
|
|
||||||
constructor(variables, owner) {
|
|
||||||
this.__values = {};
|
|
||||||
this.__owner = owner;
|
|
||||||
this.variables = variables;
|
|
||||||
|
|
||||||
for (let name of Object.keys(this.variables)) {
|
|
||||||
this.__values[name] = new Input(name, this);
|
|
||||||
Object.defineProperty(this, name, {
|
|
||||||
get() {
|
|
||||||
return this.__values[name];
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
|
||||||
return `${this.__owner.id}-in`;
|
|
||||||
}
|
|
||||||
|
|
||||||
update(changed) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
const res = {};
|
|
||||||
for (let name of Object.keys(this.variables)) {
|
|
||||||
res[name] = this.__values[name].serialize();
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Outputs {
|
|
||||||
constructor(variables, owner) {
|
|
||||||
this.__values = {};
|
|
||||||
this.__owner = owner;
|
|
||||||
this.variables = variables;
|
|
||||||
|
|
||||||
for (let name of Object.keys(this.variables)) {
|
|
||||||
this.__values[name] = new Output(name, this);
|
|
||||||
Object.defineProperty(this, name, {
|
|
||||||
get() {
|
|
||||||
return this.__values[name];
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
|
||||||
return `${this.__owner.id}-out`;
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
97
src/nodes/io/AbstractIO.js
Normal file
97
src/nodes/io/AbstractIO.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
const isNil = (val) => val === null || val === undefined;
|
||||||
|
|
||||||
|
export const defaultConstrainSatisfied = (value) => {
|
||||||
|
return !isNil(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const imageConstrainSatisfied = (value) => {
|
||||||
|
return value instanceof HTMLCanvasElement ||
|
||||||
|
value instanceof Image ||
|
||||||
|
value instanceof HTMLVideoElement ||
|
||||||
|
value instanceof ImageData ||
|
||||||
|
value instanceof ImageBitmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const colorConstrainSatisfied = (value) => {
|
||||||
|
return /#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/.test(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const numberConstrainsSatisfied = (definition, value) => {
|
||||||
|
return typeof(value) === 'number' && !isNaN(value) &&
|
||||||
|
(isNil(definition.min) || (value >= definition.min)) &&
|
||||||
|
(isNil(definition.max) || (value <= definition.max))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const valueConstrainsSatisfied = (definition, value) => {
|
||||||
|
return defaultConstrainSatisfied(value) && (
|
||||||
|
definition.type === 'Number' ? numberConstrainsSatisfied(definition, value) :
|
||||||
|
definition.type === 'Color' ? colorConstrainSatisfied(value) :
|
||||||
|
definition.type === 'Image' ? imageConstrainSatisfied(value) :
|
||||||
|
true
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class AbstractIO {
|
||||||
|
|
||||||
|
constructor(name, definition, owner) {
|
||||||
|
this.__name = name;
|
||||||
|
this.__owner = owner
|
||||||
|
this.__definition = definition;
|
||||||
|
this.__value = null;
|
||||||
|
this.__listeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get definition() {
|
||||||
|
return this.__definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
get __defaultValue() {
|
||||||
|
return this.__definition.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return `${this.__owner.id}-${this.__name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.__name;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() {
|
||||||
|
return this.__definition.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.__getValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(val) {
|
||||||
|
this.__setValue(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
__getValue() {
|
||||||
|
return (valueConstrainsSatisfied(this.__definition, this.__value)) ? this.__value : this.__defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
__setValue(value) {
|
||||||
|
if (valueConstrainsSatisfied(this.__definition, value)) {
|
||||||
|
this.__value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__notifyListeners() {
|
||||||
|
for (let listener of this.__listeners) {
|
||||||
|
listener(this.value, this.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onchange(listener) {
|
||||||
|
this.__listeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
offchange(listener) {
|
||||||
|
this.__listeners = this.__listeners
|
||||||
|
.filter((l) => l !== listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
28
src/nodes/io/AbstractIOSet.js
Normal file
28
src/nodes/io/AbstractIOSet.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export default class AbstractIOSet {
|
||||||
|
|
||||||
|
constructor(variables, owner) {
|
||||||
|
this.__values = {};
|
||||||
|
this.__owner = owner;
|
||||||
|
this.variables = variables;
|
||||||
|
|
||||||
|
for (let name of Object.keys(this.variables)) {
|
||||||
|
this.__values[name] = this.__createProperty(name, variables[name]);
|
||||||
|
Object.defineProperty(this, name, {
|
||||||
|
get() {
|
||||||
|
return this.__values[name];
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__createProperty(name, definition) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
update(changed) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/nodes/io/Input.js
Normal file
49
src/nodes/io/Input.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import AbstractIO from './AbstractIO';
|
||||||
|
|
||||||
|
export default class Input extends AbstractIO {
|
||||||
|
|
||||||
|
constructor(name, definition, owner) {
|
||||||
|
super(name, definition, owner);
|
||||||
|
this.__output = null;
|
||||||
|
|
||||||
|
this.__onchangelistener = (value) => {
|
||||||
|
this.value = value;
|
||||||
|
this.__notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.__getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value) {
|
||||||
|
this.__setValue(value);
|
||||||
|
this.__owner.update(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
get output() {
|
||||||
|
return this.__output;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(output) {
|
||||||
|
if (output && output.type === this.type) {
|
||||||
|
this.disconnect();
|
||||||
|
this.__output = output
|
||||||
|
this.__output.onchange(this.__onchangelistener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect(output) {
|
||||||
|
if (this.__output) {
|
||||||
|
this.__output.offchange(this.__onchangelistener);
|
||||||
|
this.__output = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
return {
|
||||||
|
value: this.__output ? null : this.__value,
|
||||||
|
output: this.__output ? this.__output.id: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/nodes/io/Inputs.js
Normal file
25
src/nodes/io/Inputs.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import Input from './Input';
|
||||||
|
import AbstractIOSet from './AbstractIOSet';
|
||||||
|
|
||||||
|
export default class Inputs extends AbstractIOSet {
|
||||||
|
|
||||||
|
constructor(variables, owner) {
|
||||||
|
super(variables, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
__createProperty(name, definition) {
|
||||||
|
return new Input(name, definition, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return `${this.__owner.id}-in`;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
const res = {};
|
||||||
|
for (let name of Object.keys(this.variables)) {
|
||||||
|
res[name] = this.__values[name].serialize();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/nodes/io/Inputs.spec.js
Normal file
36
src/nodes/io/Inputs.spec.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import Inputs from './Inputs';
|
||||||
|
|
||||||
|
describe('Inputs', () => {
|
||||||
|
it('should create with default values', () => {
|
||||||
|
const owner = {
|
||||||
|
id: 'owner',
|
||||||
|
update(name) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const inputs = new Inputs({
|
||||||
|
x: {
|
||||||
|
type: 'Number',
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
foo: {
|
||||||
|
type: 'String',
|
||||||
|
default: 'foo'
|
||||||
|
},
|
||||||
|
}, owner)
|
||||||
|
|
||||||
|
expect(inputs.x.type).toBe('Number');
|
||||||
|
expect(inputs.x.value).toBe(0);
|
||||||
|
expect(inputs.y.value).toBe(1);
|
||||||
|
|
||||||
|
inputs.x.value = 1;
|
||||||
|
expect(inputs.x.value).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
40
src/nodes/io/Output.js
Normal file
40
src/nodes/io/Output.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import AbstractIO from './AbstractIO';
|
||||||
|
|
||||||
|
export default class Output extends AbstractIO {
|
||||||
|
|
||||||
|
constructor(name, definition, owner) {
|
||||||
|
super(name, definition, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return `${this.__owner.id}-${this.__name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.__name;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.__getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value) {
|
||||||
|
this.__setValue(value);
|
||||||
|
this.__notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
__notifyListeners() {
|
||||||
|
for (let listener of this.__listeners) {
|
||||||
|
listener(this.value, this.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onchange(listener) {
|
||||||
|
this.__listeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
offchange(listener) {
|
||||||
|
this.__listeners = this.__listeners
|
||||||
|
.filter((l) => l !== listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/nodes/io/Outputs.js
Normal file
20
src/nodes/io/Outputs.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import Output from './Output';
|
||||||
|
import AbstractIOSet from './AbstractIOSet';
|
||||||
|
|
||||||
|
export default class Outputs extends AbstractIOSet {
|
||||||
|
constructor(variables, owner) {
|
||||||
|
super(variables, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
__createProperty(name, definition) {
|
||||||
|
return new Output(name, definition, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return `${this.__owner.id}-out`;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/nodes/io/Outputs.spec.js
Normal file
30
src/nodes/io/Outputs.spec.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import Outputs from './Outputs';
|
||||||
|
|
||||||
|
describe('Outputs', () => {
|
||||||
|
it('should create with default values', () => {
|
||||||
|
const owner = {
|
||||||
|
id: 'owner',
|
||||||
|
};
|
||||||
|
|
||||||
|
const inputs = new Outputs({
|
||||||
|
x: {
|
||||||
|
type: 'Number',
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
foo: {
|
||||||
|
type: 'String',
|
||||||
|
default: 'foo'
|
||||||
|
},
|
||||||
|
}, owner)
|
||||||
|
|
||||||
|
expect(inputs.x.type).toBe('Number');
|
||||||
|
expect(inputs.x.value).toBe(0);
|
||||||
|
expect(inputs.y.value).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
4
src/nodes/io/index.js
Normal file
4
src/nodes/io/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export {default as Input} from './Input';
|
||||||
|
export {default as Inputs} from './Inputs';
|
||||||
|
export {default as Output} from './Output';
|
||||||
|
export {default as Outputs} from './Outputs';
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
:selectedOutput="selectedOutput"
|
:selectedOutput="selectedOutput"
|
||||||
@outputSelected="selectedOutput = $event"
|
@outputSelected="selectedOutput = $event"
|
||||||
@inputSelected="selectedInput = $event"
|
@inputSelected="selectedInput = $event"
|
||||||
|
@removeNode="removeNode"
|
||||||
/>
|
/>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
@@ -140,6 +141,11 @@ export default {
|
|||||||
node: new node(),
|
node: new node(),
|
||||||
});
|
});
|
||||||
this.contextMenuPosition = null;
|
this.contextMenuPosition = null;
|
||||||
|
},
|
||||||
|
removeNode(nodeToRemove) {
|
||||||
|
const index = this.graph.findIndex(({node}) => node === nodeToRemove);
|
||||||
|
console.log('nodeIndex', index);
|
||||||
|
this.graph.splice(index, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -21,8 +21,9 @@
|
|||||||
>×</span>
|
>×</span>
|
||||||
</div>
|
</div>
|
||||||
<Value
|
<Value
|
||||||
:type="type"
|
:io="node.in[name]"
|
||||||
:value="node.in[name]"
|
direction="input"
|
||||||
|
@change="(value) => node.in[name].value = value"
|
||||||
class="node-var__value"
|
class="node-var__value"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,12 +44,18 @@
|
|||||||
@click="$emit('outputSelected', node.out[name])"
|
@click="$emit('outputSelected', node.out[name])"
|
||||||
>{{ name }} <!--<i>{{typeName(type)}}</i>--></div>
|
>{{ name }} <!--<i>{{typeName(type)}}</i>--></div>
|
||||||
<Value
|
<Value
|
||||||
:type="type"
|
:io="node.out[name]"
|
||||||
:value="node.out[name]"
|
direction="output"
|
||||||
class="node-var__value"
|
class="node-var__value"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="node__remove"
|
||||||
|
@click="removeNode"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -95,6 +102,9 @@ export default {
|
|||||||
}
|
}
|
||||||
return ins;
|
return ins;
|
||||||
},
|
},
|
||||||
|
removeNode() {
|
||||||
|
this.$emit('removeNode', this.node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -144,4 +154,18 @@ export default {
|
|||||||
.node__var {
|
.node__var {
|
||||||
margin-bottom: 2px
|
margin-bottom: 2px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.node__remove {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
right: -20px;
|
||||||
|
background-color: red;
|
||||||
|
color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="image-value"
|
class="image-value"
|
||||||
@click="download"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<canvas class="image-value__thumb" ref="thumb" />
|
<canvas class="image-value__thumb" ref="thumb" />
|
||||||
<canvas class="image-value__preview" ref="preview" />
|
<canvas class="image-value__preview" ref="preview" />
|
||||||
@@ -14,9 +14,17 @@ import {paintToCanvas, mediaSize} from '../../../src/nodes/canvas.js'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
io: {
|
||||||
|
type: null,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
value: {
|
value: {
|
||||||
type: null,
|
type: null,
|
||||||
required: true,
|
required: true,
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -34,7 +42,7 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.redraw();
|
this.redraw();
|
||||||
this.value.onchange(this.redraw);
|
this.io.onchange(this.redraw);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -44,9 +52,12 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.isRunning = false;
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
redrawCanvas(canvas) {
|
redrawCanvas(canvas) {
|
||||||
const value = this.value.value;
|
const value = this.value;
|
||||||
/** @type {HTMLCanvasElement} */
|
/** @type {HTMLCanvasElement} */
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (!value) {
|
if (!value) {
|
||||||
@@ -70,6 +81,35 @@ export default {
|
|||||||
this.redrawCanvas(this.$refs.preview)
|
this.redrawCanvas(this.$refs.preview)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onClick() {
|
||||||
|
if (this.direction === 'output') {
|
||||||
|
this.download();
|
||||||
|
} else {
|
||||||
|
this.selectFile();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectFile() {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
window.$$input = input;
|
||||||
|
input.type = 'file';
|
||||||
|
input.accept = 'image/*';
|
||||||
|
input.onchange = (event) => {
|
||||||
|
const files = event.target.files;
|
||||||
|
console.log(event);
|
||||||
|
if (files.length) {
|
||||||
|
const fr = new FileReader()
|
||||||
|
fr.onload = (event) => {
|
||||||
|
const image = new Image();
|
||||||
|
image.onload = () => {
|
||||||
|
this.$emit('change', image);
|
||||||
|
}
|
||||||
|
image.src = event.target.result;
|
||||||
|
}
|
||||||
|
fr.readAsDataURL(files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.click();
|
||||||
|
},
|
||||||
download() {
|
download() {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = this.$refs.preview.toDataURL();
|
a.href = this.$refs.preview.toDataURL();
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<select v-model="value.value">
|
<select v-model="io.value">
|
||||||
<option
|
<option
|
||||||
v-for="t in type"
|
v-for="value in io.definition.enum"
|
||||||
:value="t"
|
:value="value"
|
||||||
:key="t"
|
:key="value"
|
||||||
>{{ t }}</option>
|
>{{ value }}</option>
|
||||||
</select>
|
</select>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -14,7 +14,7 @@ export default {
|
|||||||
type: null,
|
type: null,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
value: {
|
io: {
|
||||||
type: null,
|
type: null,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<input
|
<input
|
||||||
v-model="value.value"
|
v-model="value"
|
||||||
:type="
|
:type="
|
||||||
type === 'Number' ? 'number' :
|
type === 'Number' ? 'number' :
|
||||||
type === 'Color' ? 'color' :
|
type === 'Color' ? 'color' :
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
io: {
|
||||||
type: null,
|
type: null,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
@@ -19,6 +19,26 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
value: {
|
||||||
|
get() {
|
||||||
|
return this.io.value;
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
if (this.type === 'Number') {
|
||||||
|
this.io.value = parseFloat(val)
|
||||||
|
} else {
|
||||||
|
this.io.value = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange($event) {
|
||||||
|
console.log('change', $event);
|
||||||
|
this.value = $event.target.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,22 +4,34 @@ import SelectValue from './Select.vue';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
type: {
|
io: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
value: {
|
|
||||||
type: null,
|
|
||||||
required: true,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
render(h) {
|
render(h) {
|
||||||
return h(
|
return h(
|
||||||
this.type === 'Image' ? ImageValue :
|
this.io.type === 'Image' ? ImageValue :
|
||||||
this.type === 'Number' ? StringValue :
|
this.io.type === 'Number' ? StringValue :
|
||||||
this.type === 'Color' ? StringValue :
|
this.io.type === 'Color' ? StringValue :
|
||||||
Array.isArray(this.type) ? SelectValue :
|
this.io.definition.enum ? SelectValue :
|
||||||
null,
|
null,
|
||||||
{props: {value: this.value, type: this.type}})
|
{
|
||||||
|
props: {
|
||||||
|
io: this.io,
|
||||||
|
value: this.io.value,
|
||||||
|
type: this.io.type,
|
||||||
|
direction: this.direction
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
change: (value) => {
|
||||||
|
this.$emit('change', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user