@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
336 lines (307 loc) • 12.1 kB
text/typescript
import {BaseGlConnectionPoint} from './../io/connections/Gl';
import {CoreGraph} from '../../../../core/graph/CoreGraph';
import {pushOnArrayAtEntry} from '../../../../core/MapUtils';
import {ShaderNameByContextMap} from './ShaderName';
import {TypedNode} from '../../_Base';
import {NodeContext, BaseNodeByContextMap, NetworkChildNodeType} from '../../../poly/NodeContext';
// import {NodeTypeMap} from '../../../containers/utils/ContainerMap';
import {CoreGraphNodeId} from '../../../../core/graph/CoreGraph';
import {arrayUniq, arrayDifference, arrayCompact} from '../../../../core/ArrayUtils';
// type NumberByString = Map<string, number>;
type NumberByCoreGraphNodeId = Map<CoreGraphNodeId, number>;
// type BaseNodeTypeByString = Map<string, BaseNodeType>;
// type BooleanByString = Map<string, boolean>;
type BooleanByCoreGraphNodeId = Map<CoreGraphNodeId, boolean>;
type BooleanByStringByShaderName<NC extends NodeContext> = Map<ShaderNameByContextMap[NC], BooleanByCoreGraphNodeId>;
type StringArrayByString = Map<CoreGraphNodeId, CoreGraphNodeId[]>;
type InputNamesByShaderNameMethod<NC extends NodeContext> = (
root_node: BaseNodeByContextMap[NC],
shader_name: ShaderNameByContextMap[NC]
) => string[];
interface NodeTraverserOptions {
traverseChildren?: boolean;
}
export class TypedNodeTraverser<NC extends NodeContext> {
protected _leaves_graph_id: BooleanByStringByShaderName<NC> = new Map();
protected _graph_ids_by_shader_name: BooleanByStringByShaderName<NC> = new Map();
private _outputs_by_graph_id: StringArrayByString = new Map();
private _depth_by_graph_id: NumberByCoreGraphNodeId = new Map();
private _graph_id_by_depth: Map<number, CoreGraphNodeId[]> = new Map();
protected _graph: CoreGraph;
protected _shaderName!: ShaderNameByContextMap[NC];
// private _subnets_by_id: BaseNodeTypeByString = new Map();
constructor(
private _parent_node: TypedNode<NC, any>,
private _shader_names: ShaderNameByContextMap[NC][],
private _inputNamesForShaderNameMethod: InputNamesByShaderNameMethod<NC>,
private _options?: NodeTraverserOptions
) {
this._graph = this._parent_node.scene().graph;
}
private _traverseChildren() {
return this._options?.traverseChildren || false;
}
private reset() {
this._leaves_graph_id.clear();
this._graph_ids_by_shader_name.clear();
this._outputs_by_graph_id.clear();
this._depth_by_graph_id.clear();
this._graph_id_by_depth.clear();
// this._subnets_by_id.clear();
this._shader_names.forEach((shader_name) => {
this._graph_ids_by_shader_name.set(shader_name, new Map());
});
}
shaderNames() {
return this._shader_names;
}
inputNamesForShaderName(root_node: BaseNodeByContextMap[NC], shader_name: ShaderNameByContextMap[NC]) {
return this._inputNamesForShaderNameMethod(root_node, shader_name);
}
traverse(rootNodes: BaseNodeByContextMap[NC][]) {
this.reset();
for (const shaderName of this.shaderNames()) {
this._leaves_graph_id.set(shaderName, new Map());
}
for (const shaderName of this.shaderNames()) {
this._shaderName = shaderName;
for (const rootNode of rootNodes) {
this._findLeavesFromRootNode(rootNode);
this._setNodesDepth();
}
}
// graph_ids.forEach((graph_id) => {
this._depth_by_graph_id.forEach((depth: number, graph_id: CoreGraphNodeId) => {
if (depth != null) {
// this._graph_id_by_depth.set(depth, this._graph_id_by_depth.get(depth) || []);
// this._graph_id_by_depth.get(depth)?.push(graph_id);
pushOnArrayAtEntry(this._graph_id_by_depth, depth, graph_id);
}
});
}
nodesForShaderName(shaderName: ShaderNameByContextMap[NC]) {
const depths: number[] = [];
this._graph_id_by_depth.forEach((value: CoreGraphNodeId[], key: number) => {
depths.push(key);
});
depths.sort((a, b) => a - b);
const nodes: BaseNodeByContextMap[NC][] = [];
const node_id_used_state: Map<CoreGraphNodeId, boolean> = new Map();
depths.forEach((depth) => {
const graph_ids_for_depth = this._graph_id_by_depth.get(depth);
if (graph_ids_for_depth) {
graph_ids_for_depth.forEach((graph_id: CoreGraphNodeId) => {
const is_present = this._graph_ids_by_shader_name.get(shaderName)?.get(graph_id);
if (is_present) {
const node = this._graph.nodeFromId(graph_id) as BaseNodeByContextMap[NC];
this._addNodesWithChildren(node, node_id_used_state, nodes);
}
});
}
});
return nodes;
}
sortedNodes() {
const depths: number[] = [];
this._graph_id_by_depth.forEach((ids: CoreGraphNodeId[], depth: number) => {
depths.push(depth);
});
depths.sort((a, b) => a - b);
const nodes: BaseNodeByContextMap[NC][] = [];
const node_id_used_state: Map<CoreGraphNodeId, boolean> = new Map();
depths.forEach((depth) => {
const graph_ids_for_depth = this._graph_id_by_depth.get(depth);
if (graph_ids_for_depth) {
for (const graph_id of graph_ids_for_depth) {
const node = this._graph.nodeFromId(graph_id) as BaseNodeByContextMap[NC];
if (node) {
this._addNodesWithChildren(node, node_id_used_state, nodes);
}
}
}
});
return nodes;
}
private _addNodesWithChildren(
node: BaseNodeByContextMap[NC],
node_id_used_state: Map<CoreGraphNodeId, boolean>,
accumulated_nodes: BaseNodeByContextMap[NC][]
// shader_name?: ShaderName
) {
if (!node_id_used_state.get(node.graphNodeId())) {
accumulated_nodes.push(node);
node_id_used_state.set(node.graphNodeId(), true);
}
// if (node.type() == NetworkChildNodeType.INPUT) {
// console.log('_addNodesWithChildren', node);
// const parent = node.parent();
// if (parent) {
// const nodes_with_same_parent_as_subnet_input = this._sortedNodesForShaderNameForParent(
// parent,
// shader_name
// );
// for (let child_node of nodes_with_same_parent_as_subnet_input) {
// if (child_node.graphNodeId() != node.graphNodeId()) {
// this._addNodesWithChildren(child_node, node_id_used_state, accumulated_nodes, shader_name);
// }
// }
// }
// }
}
// private _sortedNodesForShaderNameForParent(parent: BaseNodeType, shader_name?: ShaderName) {
// const depths: number[] = [];
// this._graph_id_by_depth.forEach((value: CoreGraphNodeId[], key: number) => {
// depths.push(key);
// });
// depths.sort((a, b) => a - b);
// const nodes: BaseNodeByContextMap[NC][] = [];
// depths.forEach((depth) => {
// const graph_ids_for_depth = this._graph_id_by_depth.get(depth);
// if (graph_ids_for_depth) {
// graph_ids_for_depth.forEach((graph_id: CoreGraphNodeId) => {
// const is_present = shader_name
// ? this._graph_ids_by_shader_name.get(shader_name)?.get(graph_id)
// : true;
// if (is_present) {
// const node = this._graph.nodeFromId(graph_id) as BaseNodeByContextMap[NC];
// if (node.parent() == parent) {
// nodes.push(node);
// }
// }
// });
// }
// });
// const first_node = nodes[0];
// if (parent.context() == first_node.context()) {
// nodes.push(parent as BaseNodeByContextMap[NC]);
// }
// return nodes;
// }
private _findLeavesFromRootNode(rootNode: BaseNodeByContextMap[NC]) {
this._graph_ids_by_shader_name.get(this._shaderName)?.set(rootNode.graphNodeId(), true);
// if rootNode is a subnet, traverse its children output nodes instead
if (rootNode.childrenAllowed() && this._traverseChildren()) {
const outputNode = rootNode.childrenController?.outputNode() as BaseNodeByContextMap[NC] | undefined;
if (outputNode) {
this._findLeavesFromRootNode(outputNode);
return;
}
}
//
const inputNames = this.inputNamesForShaderName(rootNode, this._shaderName);
if (inputNames) {
for (const inputName of inputNames) {
const input = rootNode.io.inputs.named_input(inputName) as BaseNodeByContextMap[NC];
if (input) {
pushOnArrayAtEntry(this._outputs_by_graph_id, input.graphNodeId(), rootNode.graphNodeId());
this._findLeaves(input);
}
}
}
this._outputs_by_graph_id.forEach((outputs: CoreGraphNodeId[], graph_id: CoreGraphNodeId) => {
const uniqIds: number[] = [];
arrayUniq(outputs, uniqIds);
this._outputs_by_graph_id.set(graph_id, uniqIds);
});
}
private _blockedInputNames: Map<string, string[]> | undefined;
setBlockedInputNames(nodeType: string, inputNames: string[]) {
this._blockedInputNames = this._blockedInputNames || new Map();
this._blockedInputNames.set(nodeType, inputNames);
}
protected _findLeaves(node: BaseNodeByContextMap[NC]) {
this._graph_ids_by_shader_name.get(this._shaderName)?.set(node.graphNodeId(), true);
const inputs = this._findInputs(node) as BaseNodeByContextMap[NC][];
const compactInputs: BaseNodeByContextMap[NC][] = [];
arrayCompact(inputs, compactInputs);
const inputGraphIds: number[] = [];
arrayUniq(
compactInputs.map((n) => n.graphNodeId()),
inputGraphIds
);
const uniqueInputs = inputGraphIds.map((graph_id) =>
this._graph.nodeFromId(graph_id)
) as BaseNodeByContextMap[NC][];
if (uniqueInputs.length > 0) {
for (const input of uniqueInputs) {
pushOnArrayAtEntry(this._outputs_by_graph_id, input.graphNodeId(), node.graphNodeId());
this._findLeaves(input);
}
} else {
this._leaves_graph_id.get(this._shaderName)!.set(node.graphNodeId(), true);
}
}
getNodeInputs(node: BaseNodeByContextMap[NC]) {
if (this._blockedInputNames == null || !this._blockedInputNames.has(node.type())) {
return node.io.inputs.inputs();
} else {
const blockedInputNames = this._blockedInputNames.get(node.type()) as string[];
const inputConnectionPoints = node.io.inputs.namedInputConnectionPoints() as BaseGlConnectionPoint[];
const inputConnectionPointNames = inputConnectionPoints.map((c) => c.name());
const allowedInputNames: string[] = [];
arrayDifference(inputConnectionPointNames, blockedInputNames, allowedInputNames);
const inputs = allowedInputNames.map((inputName) => {
const inputIndex = node.io.inputs.getNamedInputIndex(inputName);
return node.io.inputs.input(inputIndex);
});
return inputs;
}
}
private _findInputs(node: BaseNodeByContextMap[NC]) {
if (this._traverseChildren()) {
if (node.type() == NetworkChildNodeType.INPUT) {
const parent = node.parent() as BaseNodeByContextMap[NC];
return parent ? this.getNodeInputs(parent) : [];
// return node.parent()?.io.inputs.inputs() || [];
} else {
if (node.childrenAllowed()) {
// this._subnets_by_id.set(node.graphNodeId(), node);
const outputNode = node.childrenController?.outputNode();
return [outputNode];
} else {
return this.getNodeInputs(node); //node.io.inputs.inputs();
}
}
} else {
return this.getNodeInputs(node); //node.io.inputs.inputs();
}
}
private _setNodesDepth() {
this._leaves_graph_id.forEach((booleans_by_graph_id, shader_name) => {
booleans_by_graph_id.forEach((boolean, graph_id) => {
this._setNodeDepth(graph_id);
});
});
}
private _setNodeDepth(graph_id: CoreGraphNodeId, depth: number = 0) {
/*
adjust graph depth by hierarchical depth
meaning that nodes inside a subnet should add their depth to the parent (and a multiplier)
so that nodes outside of a subnet do not have a depth that ends up between the depths of 2 subnet children.
*/
// let depth_offset = 0;
// const node = this._graph.node_from_id(graph_id) as BaseNodeType;
// if (node.type == NetworkChildNodeType.INPUT) {
// const parent = node.parent;
// if (parent) {
// depth_offset = parent.children().length * 10;
// }
// }
// depth += depth_offset;
/*
end hierarchical depth adjustment
*/
const current_depth = this._depth_by_graph_id.get(graph_id);
if (current_depth != null) {
this._depth_by_graph_id.set(graph_id, Math.max(current_depth, depth));
} else {
this._depth_by_graph_id.set(graph_id, depth);
}
const output_ids = this._outputs_by_graph_id.get(graph_id);
if (output_ids) {
output_ids.forEach((output_id) => {
this._setNodeDepth(output_id, depth + 1);
});
}
}
}