From 43736b0415ebe905985316948e9a27682555b733 Mon Sep 17 00:00:00 2001 From: Stanislav Fifik Date: Fri, 25 Jan 2019 19:27:11 +0100 Subject: [PATCH] :zap: postpone update to next microtask --- package.json | 2 +- src/nodes/Canvas2d/Canvas2d.js | 18 ++++++-- src/nodes/Canvas2d/Compose.js | 2 +- src/nodes/Canvas2d/Fill.js | 4 +- src/nodes/Canvas2d/ImageIdentity.js | 3 +- src/nodes/Canvas2d/Resize.js | 2 +- src/nodes/Canvas2d/Text.js | 4 +- src/nodes/ImageIdentity.js | 2 +- src/nodes/Math/NumberBinaryOperation.js | 56 +++++++++++++++++++++++++ src/nodes/Math/Numbers.js | 41 ++++++++++++++++++ src/nodes/Node.js | 14 ++++++- src/nodes/ToBlob.js | 2 +- src/nodes/WebGL/WebGl.js | 13 +++++- src/nodes/index.js | 9 +++- src/utils.js | 4 +- 15 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 src/nodes/Math/NumberBinaryOperation.js create mode 100644 src/nodes/Math/Numbers.js diff --git a/package.json b/package.json index 74619c9..699941f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphfx", - "version": "0.0.1", + "version": "0.0.3", "description": "Graph image processing pipeline", "main": "src/index.js", "scripts": { diff --git a/src/nodes/Canvas2d/Canvas2d.js b/src/nodes/Canvas2d/Canvas2d.js index ee62776..c357476 100644 --- a/src/nodes/Canvas2d/Canvas2d.js +++ b/src/nodes/Canvas2d/Canvas2d.js @@ -12,6 +12,12 @@ export default class Canvas2d extends Node { Object.assign({ image: { type: 'Image', + }, + width: { + type: 'Number', + }, + height: { + type: 'Number', } }, outputDefiniton), options @@ -19,7 +25,7 @@ export default class Canvas2d extends Node { this.__canvas = document.createElement('canvas'); } - async __update() { + async _update() { const values = {}; for (let name of Object.keys(this.in.variables)) { values[name] = await waitForMedia(this.in[name].value); @@ -28,7 +34,12 @@ export default class Canvas2d extends Node { if (this.__canvas.width > 0 && this.__canvas.height > 0) { ctx.clearRect(0,0,this.__canvas.width, this.__canvas.height); } - await this.render(values, this.__canvas, ctx); + const result = await this.render(values, this.__canvas, ctx); + if (result instanceof ImageBitmap) { + this.__out.image.value = result; + this.out.width.value = this.out.image.value.width; + this.out.height.value = this.out.image.value.height; + } } @@ -37,8 +48,9 @@ export default class Canvas2d extends Node { * @param {any} values * @param {HTMLCanvasElement} canvas * @param {CanvasRenderingContext2D} ctx + * @returns {Promise} */ async render(values, canvas, ctx) { - + return null; } } \ No newline at end of file diff --git a/src/nodes/Canvas2d/Compose.js b/src/nodes/Canvas2d/Compose.js index 730e0af..cee511e 100644 --- a/src/nodes/Canvas2d/Compose.js +++ b/src/nodes/Canvas2d/Compose.js @@ -145,6 +145,6 @@ export default class Compose extends Canvas2d { paintToCanvas(canvas, this.fg.image, this.fg); } - this.__out.image.value = await createImageBitmap(canvas); + return await createImageBitmap(canvas); } } \ No newline at end of file diff --git a/src/nodes/Canvas2d/Fill.js b/src/nodes/Canvas2d/Fill.js index f78b652..97b3cea 100644 --- a/src/nodes/Canvas2d/Fill.js +++ b/src/nodes/Canvas2d/Fill.js @@ -20,7 +20,7 @@ export default class Fill extends Canvas2d { default: '#FFFFFF' } }, {}); - this.__update(); + this._update(); } async render({color, width, height}, canvas, ctx) { @@ -28,6 +28,6 @@ export default class Fill extends Canvas2d { canvas.height = height; ctx.fillStyle = color; ctx.fillRect(0, 0, width, height); - this.__out.image.value = await createImageBitmap(canvas); + return await createImageBitmap(canvas); } } \ No newline at end of file diff --git a/src/nodes/Canvas2d/ImageIdentity.js b/src/nodes/Canvas2d/ImageIdentity.js index 6a91d0f..9a2817e 100644 --- a/src/nodes/Canvas2d/ImageIdentity.js +++ b/src/nodes/Canvas2d/ImageIdentity.js @@ -20,7 +20,6 @@ export default class ImageIdentity extends Node { const dataUrl = canvas.toDataURL() const i = new Image(); i.src = dataUrl; - await waitForMedia(i); - this.__out.image.value = i; + return await waitForMedia(i); } } \ No newline at end of file diff --git a/src/nodes/Canvas2d/Resize.js b/src/nodes/Canvas2d/Resize.js index 789861f..4984e57 100644 --- a/src/nodes/Canvas2d/Resize.js +++ b/src/nodes/Canvas2d/Resize.js @@ -54,6 +54,6 @@ export default class Resize extends Canvas2d { newImage.left = (destWidth - newImage.width) / 2; paintToCanvas(canvas, media, newImage); - this.__out.image.value = await createImageBitmap(canvas); + return await createImageBitmap(canvas); } } \ No newline at end of file diff --git a/src/nodes/Canvas2d/Text.js b/src/nodes/Canvas2d/Text.js index 606054b..d91cdca 100644 --- a/src/nodes/Canvas2d/Text.js +++ b/src/nodes/Canvas2d/Text.js @@ -54,7 +54,7 @@ export default class Resize extends Canvas2d { ] } }, {}); - this.__update(); + this._update(); } async render({font, fontSize, text, color, width, height, textAlign, fontStyle}, canvas, ctx) { @@ -69,6 +69,6 @@ export default class Resize extends Canvas2d { text.split('\\n').forEach((line, index) => { ctx.fillText(line, x, fontSize * (1 + index)); }); - this.__out.image.value = await createImageBitmap(canvas); + return await createImageBitmap(canvas); } } \ No newline at end of file diff --git a/src/nodes/ImageIdentity.js b/src/nodes/ImageIdentity.js index 53cba48..14aebcd 100644 --- a/src/nodes/ImageIdentity.js +++ b/src/nodes/ImageIdentity.js @@ -9,7 +9,7 @@ export default class ImageIdentity extends Node { }); } - __update() { + _update() { this.__out.image.value = this.__in.image.value; } } \ No newline at end of file diff --git a/src/nodes/Math/NumberBinaryOperation.js b/src/nodes/Math/NumberBinaryOperation.js new file mode 100644 index 0000000..e9e1316 --- /dev/null +++ b/src/nodes/Math/NumberBinaryOperation.js @@ -0,0 +1,56 @@ +import Node from '../Node'; + +export default class NumberBinaryOperation extends Node { + constructor() { + super('NumberBinaryOperation', { + x: { + type: 'Number', + default: 0, + }, + y: { + type: 'Number', + default: 0, + }, + operation: { + type: 'String', + default: '+', + enum: [ + '+', + '-', + '⨉', + '÷', + 'mod', + 'max', + 'min', + ] + } + }, { + result: { + type: 'Number' + }, + }, {}); + this.__canvas = document.createElement('canvas'); + } + + async _update() { + const x = this.in.x.value; + const y = this.in.y.value; + const op = this.in.operation.value; + const value = + op === '+' ? x + y : + op === '-' ? x - y : + op === '⨉' ? x * y : + op === '÷' ? + y === 0 ? NaN : + x / y : + op === 'mod' ? x % y : + op === 'max' ? Math.max(x, y) : + op === 'min' ? Math.min(x, y) : + NaN; + + if (!isNaN(value)) { + this.out.result.value = value; + } + } + +} \ No newline at end of file diff --git a/src/nodes/Math/Numbers.js b/src/nodes/Math/Numbers.js new file mode 100644 index 0000000..0d90129 --- /dev/null +++ b/src/nodes/Math/Numbers.js @@ -0,0 +1,41 @@ +import Node from '../Node'; + +export default class Numbers extends Node { + constructor() { + super('Numbers', { + x: { + type: 'Number', + default: 0, + }, + y: { + type: 'Number', + default: 0, + }, + z: { + type: 'Number', + default: 0, + }, + }, { + x: { + type: 'Number', + default: 0, + }, + y: { + type: 'Number', + default: 0, + }, + z: { + type: 'Number', + default: 0, + }, + }, {}); + this.__canvas = document.createElement('canvas'); + } + + async _update() { + this.out.x.value = this.in.x.value; + this.out.y.value = this.in.y.value; + this.out.z.value = this.in.z.value; + } + +} \ No newline at end of file diff --git a/src/nodes/Node.js b/src/nodes/Node.js index 5aae575..9f31ab4 100644 --- a/src/nodes/Node.js +++ b/src/nodes/Node.js @@ -9,10 +9,22 @@ export default class Node { this.__in = new Inputs(inputDefinition, this); this.__out = new Outputs(outputDefiniton, this); this.__in.update = (name) => this.__update([name]); + this.__scheduledUpdate = false; } __update(changes) { - throw new Error('__update method not implemented'); + if (!this.__scheduledUpdate) { + this.__scheduledUpdate = true; + Promise.resolve() + .then(() => { + this.__scheduledUpdate = false; + this._update(); + }); + } + } + + _update(){ + throw new Error('_update method not implemented'); } /** diff --git a/src/nodes/ToBlob.js b/src/nodes/ToBlob.js index 9bde6bd..22db00c 100644 --- a/src/nodes/ToBlob.js +++ b/src/nodes/ToBlob.js @@ -23,7 +23,7 @@ export default class ToBlob extends Node { return this.options.quality; } - __update() { + _update() { const {width, height} = this.__in.image.value; const canvas = createCanvas(width, height); const ctx = canvas.getContext('2d'); diff --git a/src/nodes/WebGL/WebGl.js b/src/nodes/WebGL/WebGl.js index 0129ef3..bea6a37 100644 --- a/src/nodes/WebGL/WebGl.js +++ b/src/nodes/WebGL/WebGl.js @@ -12,6 +12,12 @@ export default class WebGL extends Node { image: { type: 'Image', }, + width: { + type: 'Number', + }, + height: { + type: 'Number', + } }); } @@ -137,7 +143,7 @@ export default class WebGL extends Node { } } - async __update() { + async _update() { const image = this.in.image.value; if (!image) return; const {width, height} = mediaSize(image); @@ -214,7 +220,10 @@ export default class WebGL extends Node { // Draw the rectangle. gl.drawArrays(gl.TRIANGLES, 0, 6); - this.out.image.value = await createImageBitmap(canvas); + const result = await createImageBitmap(canvas); + this.out.image.value = result; + this.out.width.value = this.out.image.value.width; + this.out.height.value = this.out.image.value.height; } setRectangle(gl, x, y, width, height) { diff --git a/src/nodes/index.js b/src/nodes/index.js index 6f5ebd3..a92d6a7 100644 --- a/src/nodes/index.js +++ b/src/nodes/index.js @@ -1,11 +1,16 @@ export {default as Webcam} from './Webcam'; +// 2D export {default as Resize} from './Canvas2d/Resize'; export {default as Compose} from './Canvas2d/Compose'; export {default as Fill} from './Canvas2d/Fill'; // export {default as ImageIdentity} from './Canvas2d/ImageIdentity'; - export {default as Text} from './Canvas2d/Text'; +export {default as Text} from './Canvas2d/Text'; + // WebGL export {default as BrightnessContrast} from './WebGL/BrightnessContrast'; export {default as HSV} from './WebGL/HSV'; export {default as Sepia} from './WebGL/Sepia'; export {default as GreenScreen} from './WebGL/Greenscreen'; -export {default as Channels} from './WebGL/Channels'; +export {default as Channels} from './WebGL/Channels' +// Math +export {default as Numbers} from './Math/Numbers'; +export {default as NumberBinaryOperation} from './Math/NumberBinaryOperation'; diff --git a/src/utils.js b/src/utils.js index 1857edb..695a66f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -7,4 +7,6 @@ export const waitForMedia = async (media) => { await waitForImage(media); } return media; -} \ No newline at end of file +} + +export const setImmediate = (fn) => Promise.resolve().then(fn); \ No newline at end of file