@openhps/core
Version:
Open Hybrid Positioning System - Core component
283 lines • 9.26 kB
JavaScript
import { TimeUnit } from '../../utils';
import { GraphShape } from '../_internal/implementations/GraphShape';
import { PlaceholderNode } from '../../nodes/_internal/PlaceholderNode';
import { SourceNode } from '../../nodes/SourceNode';
import { Edge } from '../Edge';
import { GraphValidator } from '../_internal/implementations';
/**
* Graph builder
* @category Graph
*/
export class GraphBuilder {
constructor(graph = new GraphShape()) {
this.graph = graph;
this.graph.name = 'graph';
}
static create() {
return new GraphBuilder();
}
on(name, listener) {
this.graph.once(name, listener);
return this;
}
from(...nodes) {
const selectedNodes = [];
nodes.forEach(node => {
if (node === undefined) {
throw new Error('Undefined node was provided as a source!');
} else if (typeof node === 'string') {
let nodeObject = this.graph.findNodeByUID(node) || this.graph.findNodeByName(node);
if (nodeObject === undefined) {
// Add a placeholder
nodeObject = new PlaceholderNode(node);
}
this.graph.addNode(nodeObject);
selectedNodes.push(nodeObject);
} else {
this.graph.addNode(node);
if (node instanceof SourceNode) {
this.graph.addEdge(new Edge(this.graph.internalSource, node));
}
selectedNodes.push(node);
}
});
return new GraphShapeBuilder(this, this.graph, selectedNodes.length === 0 ? [this.graph.internalSource] : selectedNodes);
}
addNode(node) {
this.graph.addNode(node);
return this;
}
addEdge(edge) {
this.graph.addEdge(edge);
return this;
}
deleteEdge(edge) {
this.graph.deleteEdge(edge);
return this;
}
deleteNode(node) {
this.graph.deleteNode(node);
return this;
}
/**
* Add graph shape to graph
* @param {GraphBuilder | GraphShape} shape Graph builder or abstract graph
* @returns {GraphBuilder} Current graph builder instance
*/
addShape(shape) {
let graph;
if (shape instanceof GraphBuilder) {
graph = shape.graph;
} else {
graph = shape;
}
// Add the graph node and edges
graph.nodes.forEach(node => {
// Check if the node is a placeholder
if (node instanceof PlaceholderNode) {
// Try to find a node with the same uid/name as the placeholder node
const existingNode = this.graph.findNodeByUID(node.name) || this.graph.findNodeByName(node.name);
if (existingNode) {
// Edit the edges connected to this placeholder
const outputEdges = graph.edges.filter(edge => edge.inputNode === node);
const inputEdges = graph.edges.filter(edge => edge.outputNode === node);
outputEdges.map(edge => edge.inputNode = existingNode);
inputEdges.map(edge => edge.outputNode = existingNode);
this.addNode(existingNode);
} else {
// Add the node as a placeholder
this.addNode(node);
}
} else {
this.addNode(node);
}
});
graph.edges.forEach(edge => {
this.addEdge(edge);
});
// Connect internal and external output to shape
this.graph.addEdge(new Edge(this.graph.internalSource, graph.internalSource));
this.graph.addEdge(new Edge(graph.internalSink, this.graph.internalSink));
return this;
}
// worker(options?: WorkerOptions): Promise<Graph<In, Out>> {
// return new Promise((resolve, reject) => {
// this.build()
// .then((graph) => {
// const worker = new WorkerNode(graph, options);
// resolve(GraphBuilder.create().from().via(worker).to());
// })
// .catch(reject);
// });
// }
build() {
return new Promise((resolve, reject) => {
GraphValidator.validate(this.graph);
this.graph.once('ready', () => {
resolve(this.graph);
});
this.graph.emitAsync('build', this).catch(ex => {
// Destroy model
this.graph.emit('destroy');
reject(ex);
});
});
}
}
export class GraphShapeBuilder {
constructor(graphBuilder, graph, nodes) {
this.graphBuilder = graphBuilder;
this.previousNodes = nodes;
this.graph = graph;
}
viaGraph(graph) {
// Add graph as node
graph.nodes.forEach(node => {
node.graph = this.graph;
this.graph.addNode(node);
});
graph.edges.forEach(edge => {
this.graph.addEdge(edge);
});
this._insertNode(graph.internalSource);
return graph.internalSink;
}
via(...nodes) {
const selectedNodes = [];
nodes.forEach(node => {
if (node === undefined) {
throw new Error('Undefined node was provided!');
} else if (node instanceof GraphBuilder) {
selectedNodes.push(this.viaGraph(node.graph));
} else if (node instanceof GraphShape) {
selectedNodes.push(this.viaGraph(node));
} else {
let nodeObject;
if (typeof node === 'string') {
nodeObject = this.graph.findNodeByUID(node) || this.graph.findNodeByName(node);
if (nodeObject === undefined) {
// Add a placeholder
nodeObject = new PlaceholderNode(node);
}
} else {
nodeObject = node;
}
this.graph.addNode(nodeObject);
this._insertNode(nodeObject);
selectedNodes.push(nodeObject);
}
});
this.previousNodes = selectedNodes;
return this;
}
/**
* Insert a new node in the existing graph
* @param {Node} node Node to insert
*/
_insertNode(node) {
this.previousNodes.forEach(prevNode => {
this.graph.addEdge(new Edge(prevNode, node));
});
}
static registerShape(key, fn) {
GraphShapeBuilder.shapes.set(key, fn);
}
chunk(size, timeout, timeoutUnit) {
return this.via(GraphShapeBuilder.shapes.get('chunk')(size, timeout, timeoutUnit));
}
flatten() {
return this.via(GraphShapeBuilder.shapes.get('flatten')());
}
filter(filterFn) {
return this.via(GraphShapeBuilder.shapes.get('filter')(filterFn));
}
/**
* Filter objects inside frames
* @param {Function} filterFn Filter function (true to keep, false to remove)
* @returns {GraphShapeBuilder} Current graph builder instance
*/
filterObjects(filterFn) {
return this.via(GraphShapeBuilder.shapes.get('filterObjects')(filterFn));
}
/**
* Merge objects
* @param {Function} by Merge key
* @param {number} timeout Timeout
* @param {TimeUnit} timeoutUnit Timeout unit
* @returns {GraphShapeBuilder} Current graph shape builder
*/
merge(by = () => true, timeout = 100, timeoutUnit = TimeUnit.MILLISECOND) {
return this.via(GraphShapeBuilder.shapes.get('merge')(by, timeout, timeoutUnit));
}
debounce(timeout = 100, timeoutUnit = TimeUnit.MILLISECOND) {
return this.via(GraphShapeBuilder.shapes.get('debounce')(timeout, timeoutUnit));
}
delay(timeout = 100, timeoutUnit = TimeUnit.MILLISECOND) {
return this.via(GraphShapeBuilder.shapes.get('delay')(timeout, timeoutUnit));
}
/**
* Clone frames
* @returns {GraphShapeBuilder} Current graph shape builder
*/
clone() {
return this.via(GraphShapeBuilder.shapes.get('clone')());
}
/**
* Convert positions of all objects to a certain reference space
* @param {ReferenceSpace | string} referenceSpace Reference space to convert to
* @returns {GraphShapeBuilder} Current graph shape builder
*/
convertToSpace(referenceSpace) {
return this.via(GraphShapeBuilder.shapes.get('convertToSpace')(referenceSpace));
}
/**
* Convert positions of all objects from a certain reference space
* @param {ReferenceSpace | string} referenceSpace Reference space to convert from
* @returns {GraphShapeBuilder} Current graph shape builder
*/
convertFromSpace(referenceSpace) {
return this.via(GraphShapeBuilder.shapes.get('convertFromSpace')(referenceSpace));
}
/**
* Buffer pushed objects
* @returns {GraphShapeBuilder} Current graph shape builder
*/
buffer() {
return this.via(GraphShapeBuilder.shapes.get('buffer')());
}
/**
* Storage as sink node
* @returns {GraphBuilder} Graph builder
*/
store() {
return this.to(GraphShapeBuilder.shapes.get('store')());
}
to(...nodes) {
if (nodes.length !== 0) {
const selectedNodes = [];
nodes.forEach(node => {
let nodeObject;
if (node === undefined) {
throw new Error('Undefined node was provided as a sink!');
} else if (typeof node === 'string') {
nodeObject = this.graph.findNodeByUID(node) || this.graph.findNodeByName(node);
if (nodeObject === undefined) {
// Add a placeholder
nodeObject = new PlaceholderNode(node);
}
} else {
nodeObject = node;
}
this.graph.addNode(nodeObject);
this._insertNode(nodeObject);
this.graph.addEdge(new Edge(nodeObject, this.graph.internalSink));
selectedNodes.push(nodeObject);
});
this.previousNodes = selectedNodes;
} else {
this._insertNode(this.graph.internalSink);
}
return this.graphBuilder;
}
}
GraphShapeBuilder.shapes = new Map();