@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
495 lines (494 loc) • 16.4 kB
JavaScript
"use strict";
import { TypedNodeConnection } from "./NodeConnection";
import { CoreGraphNode } from "../../../../core/graph/CoreGraphNode";
import { NodeEvent } from "../../../poly/NodeEvent";
import { ClonedStatesController } from "./utils/ClonedStatesController";
import { isString } from "../../../../core/Type";
import { arrayShallowClone } from "../../../../core/ArrayUtils";
const _existingInputIndices = [];
const MAX_INPUTS_COUNT_UNSET = 0;
export class NodeInputsController {
// private _user_inputs_clonable_states: InputCloneMode[] | undefined;
// private _inputs_clonable_states: InputCloneMode[] | undefined;
// private _inputs_cloned_state: boolean[] = [];
// private _override_clonable_state: boolean = false;
constructor(node) {
this.node = node;
this._graphNodeInputs = [];
this._inputs = [];
this._has_named_inputs = false;
this._minInputsCount = 0;
this._maxInputsCount = MAX_INPUTS_COUNT_UNSET;
this._maxInputsCountOnInput = MAX_INPUTS_COUNT_UNSET;
this._depends_on_inputs = true;
this._singleInputIndexListenedTo = null;
}
// clonable
dispose() {
if (this._graphNode) {
this._graphNode.dispose();
}
for (const graph_node of this._graphNodeInputs) {
if (graph_node) {
graph_node.dispose();
}
}
this._on_update_hooks = void 0;
this._on_update_hook_names = void 0;
}
setDependsOnInputs(depends_on_inputs) {
this._depends_on_inputs = depends_on_inputs;
}
setMinCount(minInputsCount) {
this._minInputsCount = minInputsCount;
}
minCount() {
return this._minInputsCount;
}
setMaxCount(maxInputsCount) {
if (this._maxInputsCount == MAX_INPUTS_COUNT_UNSET) {
this._maxInputsCountOnInput = maxInputsCount;
}
this._maxInputsCount = maxInputsCount;
this._initGraphNodeInputs();
this._updateCloneRequiredState();
}
listenToSingleInputIndex(index) {
this._singleInputIndexListenedTo = index;
}
onEnsureListenToSingleInputIndexUpdated(callback) {
this._onEnsureListenToSingleInputIndexUpdatedCallback = callback;
}
namedInputConnectionPointsByName(name) {
if (this._named_input_connection_points) {
for (const connection_point of this._named_input_connection_points) {
if (connection_point && connection_point.name() == name) {
return connection_point;
}
}
}
}
setNamedInputConnectionPoints(newConnectionPoints) {
var _a;
this._has_named_inputs = true;
const connectionPointsToKeep = ((_a = this._named_input_connection_points) == null ? void 0 : _a.filter((cp) => cp == null ? void 0 : cp.inNodeDefinition())) || [];
const allNewConnectionPoints = arrayShallowClone(connectionPointsToKeep);
const currentNames = /* @__PURE__ */ new Set();
for (const connectionPointToKeep of connectionPointsToKeep) {
if (connectionPointToKeep) {
currentNames.add(connectionPointToKeep.name());
}
}
for (const newConnectionPoint of newConnectionPoints) {
if (newConnectionPoint) {
if (!currentNames.has(newConnectionPoint.name())) {
currentNames.add(newConnectionPoint.name());
allNewConnectionPoints.push(newConnectionPoint);
}
}
}
const connections = this.node.io.connections.inputConnections();
if (connections) {
for (const connection of connections) {
if (connection) {
if (connection.inputIndex() >= allNewConnectionPoints.length) {
connection.disconnect({ setInput: true, ignoreLockedState: true });
}
}
}
}
this._named_input_connection_points = allNewConnectionPoints;
this.setMinCount(0);
this.setMaxCount(this._named_input_connection_points.length);
this._initGraphNodeInputs();
this.node.emit(NodeEvent.NAMED_INPUTS_UPDATED);
}
// private _has_connected_inputs() {
// for (let input of this._inputs) {
// if (input != null) {
// return true;
// }
// }
// return false;
// }
// private _check_name_changed(connection_points: ConnectionPointTypeMap[NC][]) {
// if (this._named_input_connection_points) {
// if (this._named_input_connection_points.length != connection_points.length) {
// return true;
// } else {
// for (let i = 0; i < this._named_input_connection_points.length; i++) {
// if (this._named_input_connection_points[i]?.name != connection_points[i]?.name) {
// return true;
// }
// }
// }
// }
// return false;
// }
hasNamedInputs() {
return this._has_named_inputs;
}
namedInputConnectionPoints() {
return this._named_input_connection_points;
}
_initGraphNodeInputs() {
for (let i = 0; i < this._maxInputsCount; i++) {
this._graphNodeInputs[i] = this._graphNodeInputs[i] || this._createGraphNodeInput(i);
}
}
_createGraphNodeInput(index) {
const graphInputNode = new CoreGraphNode(this.node.scene(), `input_${index}`);
this.graphNode().addGraphInput(graphInputNode, false);
return graphInputNode;
}
graphNode() {
return this._graphNode = this._graphNode || this._createGraphNode();
}
_createGraphNode() {
const graphNode = new CoreGraphNode(this.node.scene(), "inputs");
this.node.addGraphInput(graphNode, false);
return graphNode;
}
maxInputsCount() {
return this._maxInputsCount || 0;
}
maxInputsCountOverriden() {
return this._maxInputsCount != this._maxInputsCountOnInput;
}
inputGraphNode(input_index) {
return this._graphNodeInputs[input_index];
}
setCount(min, max) {
if (max == null) {
max = min;
}
this.setMinCount(min);
this.setMaxCount(max);
this._initConnectionControllerInputs();
}
_initConnectionControllerInputs() {
this.node.io.connections.initInputs();
}
isGraphNodeDirty() {
var _a;
return ((_a = this._graphNode) == null ? void 0 : _a.isDirty()) || false;
}
_isAnyInputDirty() {
for (const input of this._inputs) {
if (input && input.isDirty()) {
return true;
}
}
return false;
}
containersWithoutEvaluation(target) {
target.length = 0;
for (let i = 0; i < this._inputs.length; i++) {
const inputNode = this._inputs[i];
let container = null;
if (inputNode) {
container = inputNode.containerController.containerUnlessBypassed();
}
target.push(container);
}
return target;
}
_existingInputIndices(target) {
target.length = 0;
if (this._maxInputsCount > 0) {
for (let i = 0; i < this._inputs.length; i++) {
if (this._inputs[i]) {
target.push(i);
}
}
}
return target;
}
async evalRequiredInputs(target) {
var _a;
target.length = 0;
if (this.node.disposed() == true) {
return target;
}
if (this._maxInputsCount > 0) {
this._existingInputIndices(_existingInputIndices);
if (_existingInputIndices.length < this._minInputsCount) {
this.node.states.error.set("inputs are missing");
} else {
if (_existingInputIndices.length > 0) {
if (this._onEnsureListenToSingleInputIndexUpdatedCallback) {
await this._onEnsureListenToSingleInputIndexUpdatedCallback();
}
if (this._maxInputsCount == 1) {
const container = await this.evalRequiredInput(0);
target.push(container);
} else {
const promises = [];
if (this._singleInputIndexListenedTo != null) {
promises.push(
this.evalRequiredInput(this._singleInputIndexListenedTo)
);
} else {
const lastExistingInputIndex = _existingInputIndices[_existingInputIndices.length - 1];
for (let i = 0; i < this._inputs.length; i++) {
const input = this._inputs[i];
if (input) {
promises.push(this.evalRequiredInput(i));
} else {
if (i <= lastExistingInputIndex) {
promises.push(void 0);
}
}
}
}
const results = await Promise.all(promises);
for (const result of results) {
target.push(result);
}
}
if (!this._isAnyInputDirty()) {
(_a = this._graphNode) == null ? void 0 : _a.removeDirtyState();
}
}
}
}
return target;
}
async evalRequiredInput(inputIndex) {
let container = void 0;
const inputNode = this.input(inputIndex);
if (inputNode) {
container = await inputNode.compute();
this._graphNodeInputs[inputIndex].removeDirtyState();
}
if (container && container.coreContent()) {
} else {
if (inputNode) {
const inputErrorMessage = inputNode.states.error.message();
if (inputErrorMessage && this.node.disposed() == false) {
this.node.states.error.set(`input ${inputIndex} is invalid (error: ${inputErrorMessage})`);
}
}
}
return container;
}
getNamedInputIndex(name) {
var _a;
if (this._named_input_connection_points) {
for (let i = 0; i < this._named_input_connection_points.length; i++) {
if (((_a = this._named_input_connection_points[i]) == null ? void 0 : _a.name()) == name) {
return i;
}
}
}
return -1;
}
getInputIndex(input_index_or_name) {
if (isString(input_index_or_name)) {
if (this.hasNamedInputs()) {
return this.getNamedInputIndex(input_index_or_name);
} else {
throw new Error(`node ${this.node.path()} has no named inputs`);
}
} else {
return input_index_or_name;
}
}
setInput(inputIndexOrName, node, outputIndexOrName, options) {
const ignoreLockedState = (options == null ? void 0 : options.ignoreLockedState) || false;
if (ignoreLockedState == false && this.node.insideALockedParent()) {
const lockedParent = this.node.lockedParent();
console.warn(
`node '${this.node.path()}' cannot have its inputs changed, since it is inside '${lockedParent ? lockedParent.path() : ""}', which is locked`
);
return;
}
if (outputIndexOrName == null) {
outputIndexOrName = 0;
}
const noExceptionOnInvalidInput = (options == null ? void 0 : options.noExceptionOnInvalidInput) || false;
const inputIndex = this.getInputIndex(inputIndexOrName) || 0;
if (inputIndex < 0) {
const message = `invalid input (${inputIndexOrName}) for node ${this.node.path()}`;
if (!noExceptionOnInvalidInput) {
console.warn(message);
throw new Error(message);
} else {
return;
}
}
let outputIndex = 0;
if (node) {
if (node.io.outputs.hasNamedOutputs()) {
outputIndex = node.io.outputs.getOutputIndex(outputIndexOrName);
if (outputIndex == null || outputIndex < 0) {
const connection_points = node.io.outputs.namedOutputConnectionPoints();
const names = connection_points ? connection_points.map((cp) => cp.name()) : [];
console.warn(
`node ${node.path()} does not have an output named ${outputIndexOrName}. inputs are: ${names.join(
", "
)}`
);
return;
}
}
const nodeParent = node.parent();
const currentNodeParent = this.node.parent();
if (!(nodeParent && currentNodeParent && nodeParent.graphNodeId() == currentNodeParent.graphNodeId())) {
console.warn(`node ${node.path()} does not have the same parent as ${this.node.path()}`);
return;
}
}
const graphInputNode = this._graphNodeInputs[inputIndex];
if (graphInputNode == null) {
const message = `no input at index ${inputIndex} (name: ${inputIndexOrName}) for node '${this.node.name()}' at path '${this.node.path()}'`;
console.warn(message);
throw new Error(message);
}
if (node && this.node.parent() != node.parent()) {
return;
}
const oldInputNode = this._inputs[inputIndex];
let oldOutputIndex = null;
let oldConnection = void 0;
if (this.node.io.connections) {
oldConnection = this.node.io.connections.inputConnection(inputIndex);
}
if (oldConnection) {
oldOutputIndex = oldConnection.outputIndex();
}
if (node !== oldInputNode || outputIndex != oldOutputIndex) {
if (oldInputNode != null) {
if (this._depends_on_inputs) {
graphInputNode.removeGraphInput(oldInputNode);
}
}
if (node != null) {
const connectionResult = graphInputNode.addGraphInput(node);
if (connectionResult) {
if (!this._depends_on_inputs) {
graphInputNode.removeGraphInput(node);
}
if (oldConnection) {
oldConnection.disconnect({ setInput: false });
}
this._inputs[inputIndex] = node;
new TypedNodeConnection(
node,
this.node,
outputIndex,
inputIndex
);
} else {
console.warn(`cannot connect ${node.path()} to ${this.node.path()}`);
}
} else {
this._inputs[inputIndex] = null;
if (oldConnection) {
oldConnection.disconnect({ setInput: false });
}
}
this._run_on_set_input_hooks();
graphInputNode.setSuccessorsDirty();
this.node.emit(NodeEvent.INPUTS_UPDATED);
}
}
// remove_input(node: BaseNodeByContextMap[NC]) {
// const inputs = this.inputs();
// let input: BaseNodeByContextMap[NC] | null;
// for (let i = 0; i < inputs.length; i++) {
// input = inputs[i];
// if (input != null && node != null) {
// if (input.graphNodeId() === node.graphNodeId()) {
// this.setInput(i, null);
// }
// }
// }
// }
input(input_index) {
return this._inputs[input_index];
}
named_input(input_name) {
if (this.hasNamedInputs()) {
const input_index = this.getInputIndex(input_name);
return this._inputs[input_index];
} else {
return null;
}
}
named_input_connection_point(input_name) {
if (this.hasNamedInputs() && this._named_input_connection_points) {
const input_index = this.getInputIndex(input_name);
return this._named_input_connection_points[input_index];
}
}
has_named_input(name) {
return this.getNamedInputIndex(name) >= 0;
}
hasInput(input_index) {
return this._inputs[input_index] != null;
}
inputs() {
return this._inputs;
}
initInputsClonedState(states) {
if (!this._clonedStatesController) {
this._clonedStatesController = new ClonedStatesController(this);
this._clonedStatesController.initInputsClonedState(states);
}
}
overrideClonedStateAllowed() {
var _a;
return ((_a = this._clonedStatesController) == null ? void 0 : _a.overrideClonedStateAllowed()) || false;
}
overrideClonedState(state) {
var _a;
(_a = this._clonedStatesController) == null ? void 0 : _a.overrideClonedState(state);
}
clonedStateOverriden() {
var _a;
return ((_a = this._clonedStatesController) == null ? void 0 : _a.overriden()) || false;
}
cloneRequired(index) {
var _a;
const state = (_a = this._clonedStatesController) == null ? void 0 : _a.cloneRequiredState(index);
if (state != null) {
return state;
}
return true;
}
cloneRequiredStates() {
var _a;
const states = (_a = this._clonedStatesController) == null ? void 0 : _a.cloneRequiredStates();
if (states != null) {
return states;
}
return true;
}
_updateCloneRequiredState() {
var _a;
(_a = this._clonedStatesController) == null ? void 0 : _a.updateCloneRequiredState();
}
//
//
// HOOKS
//
//
add_on_set_input_hook(name, hook) {
this._on_update_hooks = this._on_update_hooks || [];
this._on_update_hook_names = this._on_update_hook_names || [];
if (!this._on_update_hook_names.includes(name)) {
this._on_update_hooks.push(hook);
this._on_update_hook_names.push(name);
} else {
console.warn(`hook with name ${name} already exists`, this.node);
}
}
_run_on_set_input_hooks() {
if (this._on_update_hooks) {
for (const hook of this._on_update_hooks) {
hook();
}
}
}
}