UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

224 lines (223 loc) 6.2 kB
export class CoreGraph { constructor() { this._next_id = 0; this._successors = new Map(); this._predecessors = new Map(); this._nodes_by_id = new Map(); this._nodesCount = 0; this._debugging = false; this._addedNodesDuringDebugging = new Map(); } startDebugging() { this._debugging = true; console.log("CoreGraph.startDebugging", this._next_id); } stopDebugging() { this._debugging = false; console.log("CoreGraph.stopDebugging", this._next_id); } printDebug() { this._addedNodesDuringDebugging.forEach((node, nodeId) => { console.log(nodeId, node, node.graphPredecessors(), node.graphSuccessors()); }); } set_scene(scene) { this._scene = scene; } scene() { return this._scene; } next_id() { this._next_id += 1; return this._next_id; } nodes_from_ids(ids) { const nodes = []; for (let id of ids) { const node = this.node_from_id(id); if (node) { nodes.push(node); } } return nodes; } node_from_id(id) { return this._nodes_by_id.get(id); } has_node(node) { return this._nodes_by_id.get(node.graphNodeId()) != null; } add_node(node) { this._nodes_by_id.set(node.graphNodeId(), node); this._nodesCount += 1; if (this._debugging) { this._addedNodesDuringDebugging.set(node.graphNodeId(), node); } } remove_node(node) { this._nodes_by_id.delete(node.graphNodeId()); this._successors.delete(node.graphNodeId()); this._predecessors.delete(node.graphNodeId()); this._nodesCount -= 1; if (this._debugging) { this._addedNodesDuringDebugging.delete(node.graphNodeId()); } } nodesCount() { return this._nodesCount; } connect(src, dest, check_if_graph_may_have_cycle = true) { const src_id = src.graphNodeId(); const dest_id = dest.graphNodeId(); if (this.has_node(src) && this.has_node(dest)) { if (check_if_graph_may_have_cycle) { const scene_loading = this._scene ? this._scene.loadingController.isLoading() : true; check_if_graph_may_have_cycle = !scene_loading; } let graph_would_have_cycle = false; if (check_if_graph_may_have_cycle) { graph_would_have_cycle = this._has_predecessor(src_id, dest_id); } if (graph_would_have_cycle) { return false; } else { this._create_connection(src_id, dest_id); src.dirtyController.clear_successors_cache_with_predecessors(); return true; } } else { console.warn(`attempt to connect non existing node ${src_id} or ${dest_id}`); return false; } } disconnect(src, dest) { this._remove_connection(src.graphNodeId(), dest.graphNodeId()); src.dirtyController.clear_successors_cache_with_predecessors(); } disconnect_predecessors(node) { const predecessors = this.predecessors(node); for (let predecessor of predecessors) { this.disconnect(predecessor, node); } } disconnect_successors(node) { const successors = this.successors(node); for (let successor of successors) { this.disconnect(node, successor); } } predecessor_ids(id) { const map = this._predecessors.get(id); if (map) { const ids = []; map.forEach((bool, id2) => { ids.push(id2); }); return ids; } return []; } predecessors(node) { const ids = this.predecessor_ids(node.graphNodeId()); return this.nodes_from_ids(ids); } successor_ids(id) { const map = this._successors.get(id); if (map) { const ids = []; map.forEach((bool, id2) => { ids.push(id2); }); return ids; } return []; } successors(node) { const ids = this.successor_ids(node.graphNodeId()) || []; return this.nodes_from_ids(ids); } all_predecessor_ids(node) { return this.all_next_ids(node, "predecessor_ids"); } all_successor_ids(node) { return this.all_next_ids(node, "successor_ids"); } all_predecessors(node) { const ids = this.all_predecessor_ids(node); return this.nodes_from_ids(ids); } all_successors(node) { const ids = this.all_successor_ids(node); return this.nodes_from_ids(ids); } _create_connection(src_id, dest_id) { let node_successors = this._successors.get(src_id); if (!node_successors) { node_successors = new Set(); this._successors.set(src_id, node_successors); } if (node_successors.has(dest_id)) { return; } node_successors.add(dest_id); let node_predecessors = this._predecessors.get(dest_id); if (!node_predecessors) { node_predecessors = new Set(); this._predecessors.set(dest_id, node_predecessors); } node_predecessors.add(src_id); } _remove_connection(src_id, dest_id) { let node_successors = this._successors.get(src_id); if (node_successors) { node_successors.delete(dest_id); if (node_successors.size == 0) { this._successors.delete(src_id); } } let node_predecessors = this._predecessors.get(dest_id); if (node_predecessors) { node_predecessors.delete(src_id); if (node_predecessors.size == 0) { this._predecessors.delete(dest_id); } } } all_next_ids(node, method) { const ids_by_id = new Map(); const ids = []; let next_ids = this[method](node.graphNodeId()); while (next_ids.length > 0) { const next_next_ids = []; for (let next_id of next_ids) { for (let next_next_id of this[method](next_id)) { next_next_ids.push(next_next_id); } } for (let id of next_ids) { ids_by_id.set(id, true); } for (let id of next_next_ids) { next_ids.push(id); } next_ids = next_next_ids; } ids_by_id.forEach((bool, id) => { ids.push(id); }); return ids; } _has_predecessor(src_id, dest_id) { const ids = this.predecessor_ids(src_id); if (ids) { if (ids.includes(dest_id)) { return true; } else { for (let id of ids) { return this._has_predecessor(id, dest_id); } } } return false; } }