diff --git a/package-lock.json b/package-lock.json index c95ce99..74b78dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "graphfx", - "version": "0.0.1", + "version": "0.0.8", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2405,6 +2405,535 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.13.2", + "node-pre-gyp": "0.10.3" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.3.5" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.3" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.3.5" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.24", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.4", + "nopt": "4.0.1", + "npm-packlist": "1.2.0", + "npmlog": "4.1.2", + "rc": "1.2.8", + "rimraf": "2.6.3", + "semver": "5.6.0", + "tar": "4.4.8" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.5" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.1.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.5", + "minizlib": "1.2.1", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3821,6 +4350,13 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -4676,6 +5212,7 @@ "capture-exit": "1.2.0", "exec-sh": "0.2.2", "fb-watchman": "2.0.0", + "fsevents": "1.2.7", "micromatch": "3.1.10", "minimist": "1.2.0", "walker": "1.0.7", diff --git a/package.json b/package.json index d48be09..0ea1424 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphfx", - "version": "0.0.6", + "version": "0.0.9", "description": "Graph image processing pipeline", "main": "src/index.js", "scripts": { diff --git a/src/graph.js b/src/graph.js index 6dae141..01dde98 100644 --- a/src/graph.js +++ b/src/graph.js @@ -1,10 +1,14 @@ import * as nodes from './nodes'; +import {MultiSubject} from './helpers/listener'; export default class Graph { constructor() { this.nodes = []; + this.__nodesActive = 0; + this.__nodesActiveDebounce = null; + this.subject = new MultiSubject(['running', 'stopped']); } serialize() { @@ -17,6 +21,19 @@ export default class Graph { static deserialize(nodes) { const graph = new Graph(); graph.nodes = createNodes(nodes); + for (let {node} of graph.nodes) { + node.subject.on('running', (isRunning) => { + graph.__nodesActive += isRunning ? 1 : -1; + clearTimeout(graph.__nodesActiveDebounce); + graph.__nodesActiveDebounce = setTimeout(() => { + if (graph.__nodesActive > 0) { + graph.subject.next('running'); + } else { + graph.subject.next('stopped'); + } + }, 16); + }); + } return graph; } diff --git a/src/helpers/listener.js b/src/helpers/listener.js new file mode 100644 index 0000000..1ed78af --- /dev/null +++ b/src/helpers/listener.js @@ -0,0 +1,69 @@ +export class Subject { + + constructor() { + this.__listeners = []; + } + + next(val) { + for (let listener of this.__listeners) { + listener(val); + } + } + + once() { + return new Promise((resolve) => { + const done = (val) => { + resolve(val); + this.off(done); + }; + this.on(done); + }) + } + + on(fn) { + this.__listeners.push(fn); + } + + off(fn) { + this.__listeners = this.__listeners.filter((listener) => listener !== fn); + } + + destroy() { + this.__listeners = []; + } +} + + + +export class MultiSubject { + + constructor(types) { + this.__subjects = {}; + for (let type of types) { + this.__subjects[type] = new Subject(); + } + } + + next(type, val) { + this.__subjects[type].next(val); + } + + once(type) { + return this.__subjects[type].once(); + } + + on(type, fn) { + return this.__subjects[type].on(fn); + } + + off(type, fn) { + this.__subjects[type].off(fn); + } + + destroy() { + for (let type of Object.keys(this.__subjects)) { + this.__subjects[type].destroy(); + } + } +} + diff --git a/src/nodes/Canvas2d/Compose.js b/src/nodes/Canvas2d/Compose.js index 28ac6ea..07ed447 100644 --- a/src/nodes/Canvas2d/Compose.js +++ b/src/nodes/Canvas2d/Compose.js @@ -130,6 +130,10 @@ export default class Compose extends Canvas2d { if (!width || !height) { return; } + if (!this.bg && !this.fg) { + return; + } + canvas.width = width; canvas.height = height; diff --git a/src/nodes/Node.js b/src/nodes/Node.js index 32e7760..4781fd5 100644 --- a/src/nodes/Node.js +++ b/src/nodes/Node.js @@ -1,5 +1,6 @@ import {Inputs, Outputs} from './io'; import uuidv4 from 'uuid/v4'; +import {MultiSubject} from '../helpers/listener'; export default class Node { @@ -12,13 +13,18 @@ export default class Node { this.__in.update = (name) => this.__update([name]); this.__scheduledUpdate = false; this.__currentUpdate = Promise.resolve(); + this.__running = false; this._stopped = false; + this.subject = new MultiSubject(['running']); } __update(changes) { if (!this.__scheduledUpdate && !this._stopped) { this.__scheduledUpdate = true; this.__currentUpdate = this.__currentUpdate + .then(() => { + this._running = true; + }) .then(async () => { const startTag =`graphfx-update-start:${this.uid}`; const endTag = `graphfx-update-end:${this.uid}` @@ -27,6 +33,13 @@ export default class Node { await this._update(); performance.mark(endTag) performance.measure(`GraphFX<${this.name}>`, startTag, endTag) + }) + .then(() => { + this._running = false; + }) + .catch((err) => { + this._running = false; + throw err; }); } } @@ -35,6 +48,17 @@ export default class Node { throw new Error('_update method not implemented'); } + get _running() { + return this.__running; + } + + set _running(val) { + if (this.__running !== val) { + this.__running = val; + this.subject.next('running', this.__running); + } + } + /** * Input getters */ diff --git a/src/nodes/WebGL/Channels.js b/src/nodes/WebGL/Channels.js index a63a42d..ff23a32 100644 --- a/src/nodes/WebGL/Channels.js +++ b/src/nodes/WebGL/Channels.js @@ -4,7 +4,7 @@ import WebGL from './WebGl'; export default class Channels extends WebGL { constructor() { - super('Greyscale by channel', { + super('Channels', { red: { type: 'Number', default: 1, diff --git a/src/nodes/WebGL/WebGl.js b/src/nodes/WebGL/WebGl.js index aa983fd..8821a10 100644 --- a/src/nodes/WebGL/WebGl.js +++ b/src/nodes/WebGL/WebGl.js @@ -153,17 +153,8 @@ export default class WebGL extends Node { _passes() { return [ (gl, program, image) => { - console.log('pass1') this._setParams(gl, program); }, - (gl, program, image) => { - console.log('pass2') - this._setParams(gl, program); - }, - (gl, program, image) => { - console.log('pass3') - this._setParams(gl, program); - } ] } @@ -186,7 +177,9 @@ export default class WebGL extends Node { async _update() { const image = this.in.image.value; - if (!image) return; + if (!image) { + return; + } if (image.acquire) { image.acquire(); }