💥 Graph.deserialize is now async
This commit is contained in:
28
src/graph.js
28
src/graph.js
@@ -18,17 +18,19 @@ export default class Graph {
|
||||
}));
|
||||
}
|
||||
|
||||
static deserialize(nodes) {
|
||||
static async deserialize(nodes) {
|
||||
const graph = new Graph();
|
||||
graph.nodes = createNodes(nodes);
|
||||
graph.nodes = await 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) {
|
||||
console.log('running');
|
||||
graph.subject.next('running');
|
||||
} else {
|
||||
console.log('stopped');
|
||||
graph.subject.next('stopped');
|
||||
}
|
||||
}, 16);
|
||||
@@ -65,31 +67,31 @@ export default class Graph {
|
||||
|
||||
|
||||
|
||||
export const createNode = ({name, options, id}) => {
|
||||
export const createNode = async ({name, options, id}) => {
|
||||
try {
|
||||
const node = new nodes[name](options);
|
||||
node.deserialize({id, options});
|
||||
await node.deserialize({id, options});
|
||||
return node;
|
||||
} catch (err) {
|
||||
console.error('Failed to create node', name, err)
|
||||
}
|
||||
}
|
||||
|
||||
export const createNodes = (nodes) => {
|
||||
nodes = nodes.map(({node, x, y}) => {
|
||||
const nodeInstance = createNode(node);
|
||||
export const createNodes = async (nodes) => {
|
||||
nodes = (await Promise.all(nodes.map(async ({node, x, y}) => {
|
||||
const nodeInstance = await createNode(node);
|
||||
return ({
|
||||
node: nodeInstance,
|
||||
x, y,
|
||||
connect(outputs) {
|
||||
async connect(outputs) {
|
||||
try {
|
||||
nodeInstance.reconnect(node, outputs);
|
||||
await nodeInstance.reconnect(node, outputs);
|
||||
} catch(err) {
|
||||
console.error('Can\'t recoonect', nodeInstance, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).filter(({node}) => node);
|
||||
}))).filter(({node}) => node);
|
||||
|
||||
const outputsById = nodes
|
||||
.map(({node}) => Object.values(node.out.__values))
|
||||
@@ -99,8 +101,8 @@ export const createNode = ({name, options, id}) => {
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return nodes.map(({node, x, y, connect}) => {
|
||||
connect(outputsById);
|
||||
return await Promise.all(nodes.map(async ({node, x, y, connect}) => {
|
||||
await connect(outputsById);
|
||||
return {node,x,y};
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -61,27 +61,14 @@ export default class Resize extends Canvas2d {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {{name: string, url: string, fontface: FontFace}} font
|
||||
* @param {{name: string, url: string, fontface: FontFace, type: 'font'}} font
|
||||
*/
|
||||
async loadFont(font) {
|
||||
if (typeof font === 'string') {
|
||||
return font
|
||||
return font;
|
||||
} else if (!font) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (font.name && !font.url && !font.fontface) {
|
||||
return name;
|
||||
}
|
||||
if (font.name && font.url && isEmpty(font.fontface)) {
|
||||
font.fontface = new FontFace(font.name, font.url);
|
||||
}
|
||||
|
||||
if (font.fontface) {
|
||||
await font.fontface.load();
|
||||
if (!document.fonts.has(font.fontface)) {
|
||||
await document.fonts.add(font.fontface)
|
||||
}
|
||||
} else {
|
||||
return font.name;
|
||||
}
|
||||
}
|
||||
@@ -91,6 +78,7 @@ export default class Resize extends Canvas2d {
|
||||
canvas.height = height;
|
||||
|
||||
ctx.font = `${fontStyle} ${fontSize}px "${(await this.loadFont(font)) || ''}"`;
|
||||
// ctx.font = `${fontStyle} ${fontSize}px "Arial"`;
|
||||
ctx.fillStyle = color;
|
||||
ctx.textAlign = textAlign;
|
||||
const x = textAlign === 'center' ? canvas.width /2 :
|
||||
|
||||
@@ -20,11 +20,9 @@ export default class Node {
|
||||
|
||||
__update(changes) {
|
||||
if (!this.__scheduledUpdate && !this._stopped) {
|
||||
this._running = true;
|
||||
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}`
|
||||
@@ -35,10 +33,10 @@ export default class Node {
|
||||
performance.measure(`GraphFX<${this.name}>`, startTag, endTag)
|
||||
})
|
||||
.then(() => {
|
||||
this._running = false;
|
||||
this._running = this.__scheduledUpdate;
|
||||
})
|
||||
.catch((err) => {
|
||||
this._running = false;
|
||||
this._running = this.__scheduledUpdate;
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
@@ -84,11 +82,11 @@ export default class Node {
|
||||
}
|
||||
}
|
||||
|
||||
deserialize({id, options}) {
|
||||
async deserialize({id, options}) {
|
||||
this.id = id || uuidv4();
|
||||
}
|
||||
|
||||
reconnect({options}, outputs) {
|
||||
async reconnect({options}, outputs) {
|
||||
for (let name of Object.keys(options.in)) {
|
||||
const {output} = options.in[name];
|
||||
if (output) {
|
||||
@@ -96,10 +94,10 @@ export default class Node {
|
||||
}
|
||||
}
|
||||
for (let name of Object.keys(options.out)) {
|
||||
this.out[name].deserialize(options.out[name]);
|
||||
await this.out[name].deserialize(options.out[name]);
|
||||
}
|
||||
for (let name of Object.keys(options.in)) {
|
||||
this.in[name].deserialize(options.in[name]);
|
||||
await this.in[name].deserialize(options.in[name]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
src/nodes/Synchronize.js
Normal file
57
src/nodes/Synchronize.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import Node from './Node';
|
||||
|
||||
export default class Numbers extends Node {
|
||||
constructor() {
|
||||
super('Synchronize', {
|
||||
first: {
|
||||
type: 'Image',
|
||||
default: 0,
|
||||
},
|
||||
second: {
|
||||
type: 'Image',
|
||||
default: 0,
|
||||
},
|
||||
}, {
|
||||
first: {
|
||||
type: 'Image',
|
||||
default: 0,
|
||||
},
|
||||
second: {
|
||||
type: 'Image',
|
||||
default: 0,
|
||||
},
|
||||
}, {});
|
||||
this.__changed = {
|
||||
first: false,
|
||||
second: false,
|
||||
};
|
||||
this.in.first.onchange((value) => {
|
||||
if (value) {
|
||||
console.log('first hit', value)
|
||||
this.__changed.first = true;
|
||||
}
|
||||
});
|
||||
this.in.second.onchange((value) => {
|
||||
if (value) {
|
||||
console.log('second hit', value)
|
||||
this.__changed.second = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async _update() {
|
||||
const {first, second} = this.__changed;
|
||||
if (first && second) {
|
||||
console.log('synced')
|
||||
this.out.first.value = this.in.first.value;
|
||||
this.out.second.value = this.in.second.value;
|
||||
this.__changed = {
|
||||
first: false,
|
||||
second: false,
|
||||
};
|
||||
} else {
|
||||
console.log('not synced')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,8 +51,8 @@ export default class Input extends AbstractIO {
|
||||
}
|
||||
}
|
||||
|
||||
deserialize({value, label}) {
|
||||
const deserializedValue = deserialize(value);
|
||||
async deserialize({value, label}) {
|
||||
const deserializedValue = await deserialize(value);
|
||||
if (deserializedValue) {
|
||||
this.value = deserializedValue;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,22 @@ const deserializeImage = ({src}) => {
|
||||
return img;
|
||||
}
|
||||
|
||||
export const deserialize = (value) => {
|
||||
const deserializeFont = async (font) => {
|
||||
try {
|
||||
font.fontface = new FontFace(font.name, font.url);
|
||||
await font.fontface.load();
|
||||
if (!document.fonts.has(font.fontface)) {
|
||||
await document.fonts.add(font.fontface)
|
||||
}
|
||||
} catch(err) {
|
||||
console.warn('Failed to load font', err)
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
export const deserialize = async (value) => {
|
||||
return isNil(value) ? value :
|
||||
value.type === 'image' ? deserializeImage(value) :
|
||||
value.type === 'image' ? await deserializeImage(value) :
|
||||
value.type === 'font' ? await deserializeFont(value) :
|
||||
value;
|
||||
}
|
||||
Reference in New Issue
Block a user