@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
360 lines (359 loc) • 11.2 kB
JavaScript
"use strict";
import { isArray } from "../Type";
import { addToSetAtEntry } from "../MapUtils";
const _idStack = [];
const _idsSet = /* @__PURE__ */ new Set();
export class CoreGraph {
constructor() {
this._nextId = 0;
this._successors = /* @__PURE__ */ new Map();
this._predecessors = /* @__PURE__ */ new Map();
this._nodesById = /* @__PURE__ */ new Map();
this._forbiddenTriggerNodeIds = /* @__PURE__ */ new Map();
this._selfDirtyForbidden = /* @__PURE__ */ new Set();
this._nodesCount = 0;
this._debugging = false;
this._addedNodesDuringDebugging = /* @__PURE__ */ new Map();
this._boundPredecessorIds = this.predecessorIds.bind(this);
this._boundSuccessorIds = this.successorIds.bind(this);
}
startDebugging() {
this._debugging = true;
}
stopDebugging() {
this._debugging = false;
}
printDebug() {
this._addedNodesDuringDebugging.forEach((node, nodeId) => {
console.log(nodeId, node, node.graphPredecessors(), node.graphSuccessors());
});
}
// print() {
// this._nodesById.forEach((node, nodeId) => {
// console.log(nodeId, node, node.graphPredecessors(), node.graphSuccessors());
// });
// }
setScene(scene) {
this._scene = scene;
}
scene() {
return this._scene;
}
nextId() {
this._nextId += 1;
return this._nextId;
}
nodesFromIds(ids, target) {
target.length = 0;
for (const id of ids) {
const node = this.nodeFromId(id);
if (node) {
target.push(node);
}
}
}
nodeFromId(id) {
return this._nodesById.get(id);
}
hasNode(node) {
return this._nodesById.get(node.graphNodeId()) != null;
}
addNode(node) {
this._nodesById.set(node.graphNodeId(), node);
this._nodesCount += 1;
if (this._debugging) {
this._addedNodesDuringDebugging.set(node.graphNodeId(), node);
}
}
removeNode(node) {
this.disconnectPredecessors(node);
this.disconnectSuccessors(node);
const nodeId = node.graphNodeId();
this._nodesById.delete(nodeId);
this._successors.delete(nodeId);
this._predecessors.delete(nodeId);
this._nodesCount -= 1;
this._forbiddenTriggerNodeIds.delete(nodeId);
this._forbiddenTriggerNodeIds.forEach((set, nodeId2) => {
if (set.has(nodeId2)) {
set.delete(nodeId2);
}
});
this._selfDirtyForbidden.delete(nodeId);
if (this._debugging) {
this._addedNodesDuringDebugging.delete(nodeId);
}
}
nodesCount() {
return this._nodesCount;
}
connect(src, dest, checkCycle = true) {
const srcId = src.graphNodeId();
const destId = dest.graphNodeId();
if (!(this.hasNode(src) && this.hasNode(dest))) {
console.warn(`attempt to connect non existing node ${srcId} or ${destId}`);
return false;
}
if (checkCycle) {
const sceneLoading = this._scene ? this._scene.loadingController.isLoading() : true;
checkCycle = !sceneLoading;
}
const graphWouldHaveCycle = checkCycle ? src.hasPredecessor(dest) : false;
if (graphWouldHaveCycle) {
return false;
} else {
this._createConnection(srcId, destId);
src.clearCachesWithPredecessorsAndSuccessors();
return true;
}
}
disconnect(src, dest) {
this._removeConnection(src.graphNodeId(), dest.graphNodeId());
src.clearCachesWithPredecessorsAndSuccessors();
dest.clearCachesWithPredecessorsAndSuccessors();
}
disconnectPredecessors(node) {
const predecessors = this.predecessors(node);
if (!predecessors) {
return;
}
for (const predecessor of predecessors) {
this.disconnect(predecessor, node);
}
}
disconnectSuccessors(node) {
const successors = this.successors(node);
if (!successors) {
return;
}
for (const successor of successors) {
this.disconnect(node, successor);
}
}
predecessorIds(id) {
var _a;
return (_a = this._predecessors.get(id)) == null ? void 0 : _a.idsArray;
}
predecessors(node) {
var _a;
return (_a = this._predecessors.get(node.graphNodeId())) == null ? void 0 : _a.nodes;
}
successorIds(id) {
var _a;
return (_a = this._successors.get(id)) == null ? void 0 : _a.idsArray;
}
successors(node) {
var _a;
return (_a = this._successors.get(node.graphNodeId())) == null ? void 0 : _a.nodes;
}
// private _allNodeIds(node: CoreGraphNode, method: TraverseCallback, target: CoreGraphNodeId[]): void {
// target.length = 0;
// _idsSet.clear();
// _idStack.length = 1;
// _idStack[0] = node.graphNodeId();
// const forbiddenIds = this._forbiddenTriggerNodeIds.get(node.graphNodeId())
// while (_idStack.length > 0) {
// const currentId = _idStack.pop()!;
// const ids = method(currentId);
// if (ids) {
// for (const id of ids) {
// if (!_idsSet.has(id)) {
// _idsSet.add(id);
// target.push(id);
// _idStack.push(id);
// }
// }
// }
// }
// }
allPredecessorIds(node, target) {
target.length = 0;
_idsSet.clear();
_idStack.length = 1;
_idStack[0] = node.graphNodeId();
while (_idStack.length > 0) {
const currentId = _idStack.pop();
const ids = this._boundPredecessorIds(currentId);
if (ids) {
for (const id of ids) {
if (!_idsSet.has(id)) {
_idsSet.add(id);
target.push(id);
_idStack.push(id);
}
}
}
}
}
allSuccessorIds(node, target) {
target.length = 0;
_idsSet.clear();
_idStack.length = 1;
_idStack[0] = node.graphNodeId();
const forbiddenIds = this._forbiddenTriggerNodeIds.get(node.graphNodeId());
while (_idStack.length > 0) {
const currentId = _idStack.pop();
const ids = this._boundSuccessorIds(currentId);
if (ids) {
for (const id of ids) {
if (!_idsSet.has(id)) {
_idsSet.add(id);
if (forbiddenIds == null || !forbiddenIds.has(id)) {
target.push(id);
_idStack.push(id);
}
}
}
}
}
}
// private _allNodes(node: CoreGraphNode, method: TraverseCallback, target: CoreGraphNode[]): void {
// target.length = 0;
// _idsSet.clear();
// _idStack.length = 1;
// _idStack[0] = node.graphNodeId();
// while (_idStack.length > 0) {
// const currentId = _idStack.pop()!;
// const ids = method(currentId);
// if (ids) {
// for (const id of ids) {
// if (!_idsSet.has(id)) {
// _idsSet.add(id);
// const otherNode = this._nodesById.get(id);
// if (otherNode) {
// target.push(otherNode);
// }
// _idStack.push(id);
// }
// }
// }
// }
// }
allPredecessors(node, target) {
target.length = 0;
_idsSet.clear();
_idStack.length = 1;
_idStack[0] = node.graphNodeId();
while (_idStack.length > 0) {
const currentId = _idStack.pop();
const ids = this._boundPredecessorIds(currentId);
if (ids) {
for (const id of ids) {
if (!_idsSet.has(id)) {
_idsSet.add(id);
const otherNode = this._nodesById.get(id);
if (otherNode) {
target.push(otherNode);
}
_idStack.push(id);
}
}
}
}
}
allSuccessors(node, target) {
target.length = 0;
_idsSet.clear();
_idStack.length = 1;
_idStack[0] = node.graphNodeId();
const forbiddenIds = this._forbiddenTriggerNodeIds.get(node.graphNodeId());
while (_idStack.length > 0) {
const currentId = _idStack.pop();
const ids = this._boundSuccessorIds(currentId);
if (ids) {
for (const id of ids) {
if (!_idsSet.has(id)) {
_idsSet.add(id);
if (forbiddenIds == null || !forbiddenIds.has(id)) {
const otherNode = this._nodesById.get(id);
if (otherNode) {
target.push(otherNode);
}
_idStack.push(id);
}
}
}
}
}
}
_createConnection(srcId, destId) {
let successorsData = this._successors.get(srcId);
let predecessorsData = this._predecessors.get(destId);
if (!successorsData) {
successorsData = { idsSet: /* @__PURE__ */ new Set(), idsArray: [], nodes: [] };
this._successors.set(srcId, successorsData);
}
if (!predecessorsData) {
predecessorsData = { idsSet: /* @__PURE__ */ new Set(), idsArray: [], nodes: [] };
this._predecessors.set(destId, predecessorsData);
}
if (!successorsData.idsSet.has(destId)) {
successorsData.idsSet.add(destId);
successorsData.idsArray.push(destId);
const destNode = this._nodesById.get(destId);
if (destNode) {
successorsData.nodes.push(destNode);
} else {
throw new Error(`creating connection with node not in graph ${destId}`);
}
}
if (!predecessorsData.idsSet.has(srcId)) {
predecessorsData.idsSet.add(srcId);
predecessorsData.idsArray.push(srcId);
const srcNode = this._nodesById.get(srcId);
if (srcNode) {
predecessorsData.nodes.push(srcNode);
} else {
throw new Error(`creating connection with node not in graph ${srcId}`);
}
}
}
_removeConnection(srcId, destId) {
const successorsData = this._successors.get(srcId);
if (successorsData && successorsData.idsSet.has(destId)) {
successorsData.idsSet.delete(destId);
const idIndex = successorsData.idsArray.indexOf(destId);
if (idIndex >= 0) {
successorsData.idsArray.splice(idIndex, 1);
successorsData.nodes.splice(idIndex, 1);
} else {
console.warn(`could not find id ${destId} in successorsData.idsArray`, successorsData.idsArray);
}
}
const predecessorsData = this._predecessors.get(destId);
if (predecessorsData && predecessorsData.idsSet.has(srcId)) {
predecessorsData.idsSet.delete(srcId);
const idIndex = predecessorsData.idsArray.indexOf(srcId);
if (idIndex >= 0) {
predecessorsData.idsArray.splice(idIndex, 1);
predecessorsData.nodes.splice(idIndex, 1);
} else {
console.warn(`could not find id ${srcId} in predecessorsData.idsArray`, predecessorsData.idsArray);
}
}
}
setForbiddenTriggerNodes(src, dest) {
var _a;
(_a = this._forbiddenTriggerNodeIds.get(src.graphNodeId())) == null ? void 0 : _a.clear();
if (isArray(dest)) {
for (const destNode of dest) {
addToSetAtEntry(this._forbiddenTriggerNodeIds, src.graphNodeId(), destNode.graphNodeId());
}
} else {
addToSetAtEntry(this._forbiddenTriggerNodeIds, src.graphNodeId(), dest.graphNodeId());
}
}
clearForbiddenTriggerNodes(src) {
this._forbiddenTriggerNodeIds.delete(src.graphNodeId());
}
setSelfDirtyForbidden(node, state) {
if (state) {
this._selfDirtyForbidden.add(node.graphNodeId());
} else {
this._selfDirtyForbidden.delete(node.graphNodeId());
}
}
selfDirtyForbidden(node) {
return this._selfDirtyForbidden.has(node.graphNodeId());
}
}