polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
224 lines (223 loc) • 6.2 kB
JavaScript
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;
}
}