UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

283 lines 9.26 kB
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();