@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
508 lines (473 loc) • 16.7 kB
text/typescript
import {PolyScene} from '../scene/PolyScene';
import {CoreGraphNode} from '../../core/graph/CoreGraphNode';
import {UIData} from './utils/UIData';
import {FlagsController, FlagsControllerD} from './utils/FlagsController';
import {NodeStatesController} from './utils/StatesController';
import {HierarchyParentController} from './utils/hierarchy/ParentController';
import {HierarchyChildrenController, NodeCreateOptions} from './utils/hierarchy/ChildrenController';
import {NodeLifeCycleController} from './utils/LifeCycleController';
import {TypedContainerController} from './utils/ContainerController';
import {NodeCookController, OnCookCompleteHook} from './utils/CookController';
import {NameController} from './utils/NameController';
import {CoreNodeSerializer, NodeSerializerData} from './utils/CoreNodeSerializer';
import {ParamsController} from './utils/params/ParamsController';
import {ParamConstructorMap} from '../params/types/ParamConstructorMap';
import {ParamInitValuesTypeMap} from '../params/types/ParamInitValuesTypeMap';
import {NodeParamsConfig} from './utils/params/ParamsConfig';
import {ParamsValueAccessor, ParamsValueAccessorType} from './utils/params/ParamsValueAccessor';
// import {ProcessingContext} from './utils/ProcessingContext';
import {IOController} from './utils/io/IOController';
import {NodeEvent} from '../poly/NodeEvent';
import {BaseNodeByContextMap, NodeContext} from '../poly/NodeContext';
import {ParamsAccessorType, ParamsAccessor} from './utils/params/ParamsAccessor';
// import {ContainerMap, ContainerType} from '../containers/utils/ContainerMap';
import {ContainableMap} from '../containers/utils/ContainableMap';
import {ParamOptions} from '../params/utils/OptionsController';
import {ParamType} from '../poly/ParamType';
import {DisplayNodeController} from './utils/DisplayNodeController';
// import {NodeTypeMap} from '../containers/utils/ContainerMap';
import {ParamInitValueSerialized} from '../params/types/ParamInitValueSerialized';
import {ModuleName} from '../poly/registers/modules/Common';
import {BasePersistedConfig} from './utils/BasePersistedConfig';
import {AssemblerName} from '../poly/registers/assemblers/_BaseRegister';
import {PolyNodeController} from './utils/poly/PolyNodeController';
import {CoreGraphNodeId} from '../../core/graph/CoreGraph';
import {PolyDictionary} from '../../types/GlobalTypes';
import {SetInputsOptions} from './utils/io/InputsController';
import {OnNodeRegisterCallback} from '../poly/registers/nodes/NodesRegister';
import {EventDispatcher, BaseEvent, EventListener} from 'three';
import {CoreObjectType, ObjectContent} from '../../core/geometry/ObjectContent';
export interface NodeDeletedEmitData {
parent_id: CoreGraphNodeId;
}
type AnyString= 'any-string'
const ANY_STRING:AnyString='any-string'
export interface NodeCreatedEmitData {
child_node_json: NodeSerializerData;
}
type EmitDataByNodeEventMapGeneric = {[key in NodeEvent]: any};
export interface EmitDataByNodeEventMap extends EmitDataByNodeEventMapGeneric {
[NodeEvent.CREATED]: NodeCreatedEmitData;
[NodeEvent.DELETED]: NodeDeletedEmitData;
[NodeEvent.ERROR_UPDATED]: undefined;
}
export interface IntegrationData {
name: string;
data: PolyDictionary<string>;
}
export interface BaseNodeEvent extends BaseEvent {
target?: BaseNodeType;
// [attachment: string]: any;
}
export type NodeEventListener = EventListener<BaseNodeEvent, string, EventDispatcher<BaseNodeEvent>>;
export const DEFAULT_DATA_TYPE = 'default';
/**
*
*
* TypedNode is the base class that all nodes inherit from. This inherits from [CoreGraphNode](/docs/api/CoreGraphNode).
*
*/
export class TypedNode<NC extends NodeContext, K extends NodeParamsConfig> extends CoreGraphNode {
containerController: TypedContainerController<NC> = new TypedContainerController<NC>(this);
private _parentController: HierarchyParentController | undefined;
private _uiData: UIData | undefined;
private _states: NodeStatesController<NC> | undefined;
private _lifecycle: NodeLifeCycleController | undefined;
private _serializer: CoreNodeSerializer | undefined;
private _cookController: NodeCookController<NC> | undefined;
public readonly flags: FlagsController | undefined;
public readonly displayNodeController: DisplayNodeController | undefined;
public readonly persisted_config: BasePersistedConfig | undefined;
private _paramsController: ParamsController | undefined;
readonly paramsConfig: K | undefined;
readonly pv: ParamsValueAccessorType<K> = (<unknown>new ParamsValueAccessor<K>()) as ParamsValueAccessorType<K>;
// readonly pv: ParamsValueAccessor<K> = new ParamsValueAccessor<K>(this);
readonly p: ParamsAccessorType<K> = (<unknown>new ParamsAccessor<K>()) as ParamsAccessorType<K>;
copy_param_values(node: TypedNode<NC, K>) {
const non_spare = this.params.non_spare;
for (const param of non_spare) {
const other_param = node.params.get(param.name());
if (other_param) {
param.copyValue(other_param);
}
}
}
dataType(): string {
return DEFAULT_DATA_TYPE;
}
private _nameController: NameController | undefined;
get parentController(): HierarchyParentController {
return (this._parentController = this._parentController || new HierarchyParentController(this));
}
static displayedInputNames(): string[] | undefined {
return undefined;
}
displayedInputNames(): string[] | undefined {
return (this.constructor as any as TypedNode<NC, K>).displayedInputNames();
}
private _childrenController: HierarchyChildrenController | undefined;
protected _childrenControllerContext: NodeContext | undefined;
childrenControllerContext() {
return this._childrenControllerContext;
}
private _create_childrenController(): HierarchyChildrenController | undefined {
if (this._childrenControllerContext) {
return new HierarchyChildrenController(this, this._childrenControllerContext);
}
}
get childrenController(): HierarchyChildrenController | undefined {
return (this._childrenController = this._childrenController || this._create_childrenController());
}
childrenAllowed(): boolean {
return this._childrenControllerContext != null;
}
sceneReadonly(): boolean {
return false;
}
get uiData(): UIData {
return (this._uiData = this._uiData || new UIData(this));
}
get states(): NodeStatesController<NC> {
return (this._states = this._states || new NodeStatesController(this));
}
get lifecycle(): NodeLifeCycleController {
return (this._lifecycle = this._lifecycle || new NodeLifeCycleController(this));
}
get serializer(): CoreNodeSerializer | undefined {
return this._serializer;
}
get cookController(): NodeCookController<NC> {
return (this._cookController = this._cookController || new NodeCookController(this));
}
protected _io: IOController<NC> | undefined;
get io(): IOController<NC> {
return (this._io = this._io || new IOController(this));
}
get nameController(): NameController {
return (this._nameController = this._nameController || new NameController(this));
}
/**
* sets the name of a node. Note that if a sibling node already has that name, it will be updated to be unique.
*
*/
override setName(name: string) {
this.nameController.setName(name);
}
_setCoreName(name: string) {
this._name = name;
}
get params(): ParamsController {
return (this._paramsController = this._paramsController || new ParamsController(this));
}
// get processing_context(): ProcessingContext {
// return (this._processing_context = this._processing_context || new ProcessingContext(this));
// }
constructor(scene: PolyScene, nodeName: string = 'BaseNode', public createOptions?: NodeCreateOptions) {
super(scene, nodeName);
const serializerClass = createOptions?.serializerClass;
if (serializerClass) {
this._serializer = new serializerClass(this);
}
}
private _initialized: boolean = false;
public initializeBaseAndNode() {
if (!this._initialized) {
this._initialized = true;
this.displayNodeController?.initializeNode();
this.initializeBaseNode(); // for base classes of Sop, Obj...
this.initializeNode(); // for Derivated node clases, like BoxSop, TransformSop...
if (this.polyNodeController) {
this.polyNodeController.initializeNode();
}
} else {
console.warn('node already initialized');
}
}
protected initializeBaseNode() {}
protected initializeNode() {}
static type(): string {
throw 'type to be overriden';
}
static onRegister: OnNodeRegisterCallback | undefined;
/**
* returns the type of the node.
*
*/
type() {
const c = this.constructor as typeof BaseNodeClass;
return c.type();
}
static context(): NodeContext {
console.error('node has no node_context', this);
throw 'context requires override';
}
/**
* returns the context.
*
*/
context(): NodeContext {
const c = this.constructor as typeof BaseNodeClass;
return c.context();
}
static requireWebGL2(): boolean {
return false;
}
requireWebGL2(): boolean {
const c = this.constructor as typeof BaseNodeClass;
return c.requireWebGL2();
}
setParent(parent: BaseNodeType | null) {
this.parentController.setParent(parent);
}
/**
* returns the parent.
*
*/
parent() {
return this.parentController.parent();
}
insideALockedParent(): boolean {
return this.lockedParent() != null;
}
lockedOrInsideALockedParent(): boolean {
return this.polyNodeController?.locked() || this.insideALockedParent();
}
selfOrLockedParent(): BaseNodeType | null {
if (this.polyNodeController?.locked()) {
return this;
}
return this.lockedParent();
}
lockedParent(): BaseNodeType | null {
const parent = this.parent();
if (!parent) {
return null;
}
if (parent.polyNodeController && parent.polyNodeController.locked()) {
return parent;
}
return parent.lockedParent();
}
firstAncestorWithContext<N extends NodeContext>(context: N) {
return this.parentController.firstAncestorWithContext(context);
}
root() {
return this._scene.root();
}
/**
* returns the path.
*
*/
path(relative_to_parent?: BaseNodeType): string {
return this.parentController.path(relative_to_parent);
}
// params
createParams() {}
addParam<T extends ParamType>(
type: T,
name: string,
default_value: ParamInitValuesTypeMap[T],
options?: ParamOptions
): ParamConstructorMap[T] | undefined {
return this._paramsController?.addParam(type, name, default_value, options);
}
paramDefaultValue(name: string): ParamInitValueSerialized {
return null;
}
// cook
cook(inputContents: any[]): any {
return null;
}
/**
* registers a callback that will be run every time the node finishes cooking.
*
*/
onCookEnd(callbackName: string, callback: OnCookCompleteHook) {
this.cookController.registerOnCookEnd(callbackName, callback);
}
/**
* returns a promise that will be resolved when the node finishes cooking.
*
*/
async compute() {
if (this.isDirty() || this.flags?.bypass?.active()) {
return await this.containerController.compute();
} else {
return this.containerController.container();
}
}
_setContainer(content: ContainableMap[NC] /*, message: string | null = null*/) {
// TODO: typescript: why is this a type of never
this.containerController.container().set_content(content as never); //, this.self.cook_eval_key());
// if (content != null) {
// // if (content.name==null) {
// // content.name = this.path();
// // }
// // if (!(content as any).node) {
// // (content as any).node = this;
// // }
// }
this.cookController.endCook(/*message*/);
}
/**
* create a node.
*
*/
createNode(nodeClass: any, options?: NodeCreateOptions) {
return this.childrenController?.createNode(nodeClass, options);
}
createOperationContainer(type: string, operation_container_name: string, options?: NodeCreateOptions) {
return this.childrenController?.createOperationContainer(type, operation_container_name, options);
}
/**
* removes a child node
*
*/
removeNode(node: BaseNodeType) {
this.childrenController?.removeNode(node);
}
override dispose() {
super.dispose();
this.setParent(null);
if (this._nameController) {
this._nameController.dispose();
this._nameController = undefined;
}
if (this._io) {
this._io.dispose();
this._io = undefined;
}
if (this._lifecycle) {
this._lifecycle.dispose();
this._lifecycle = undefined;
}
if (this.displayNodeController) {
this.displayNodeController.dispose();
// this.displayNodeController = undefined
}
if (this._childrenController) {
this._childrenController.dispose();
this._childrenController = undefined;
}
if (this._paramsController) {
this._paramsController.dispose();
this._paramsController = undefined;
}
if (this._cookController) {
this._cookController.dispose();
this._cookController = undefined;
}
if (this._serializer) {
this._serializer.dispose();
this._serializer = undefined;
}
if (this._uiData) {
this._uiData.dispose();
this._uiData = undefined;
}
}
/**
* returns the list of children
*
*/
children(): Readonly<BaseNodeType[]> {
return this.childrenController?.children() || [];
}
/**
* returns a child node
*
*/
node(path: string) {
return this.parentController?.findNode(path) || null;
}
/**
* returns a sibling node
*
*/
nodeSibling(name: string): BaseNodeByContextMap[NC] | null {
const parent = this.parent();
if (parent) {
const node = parent.childrenController?.childByName(name);
if (node) {
return node as BaseNodeByContextMap[NC];
}
}
return null;
}
/**
* returns the children matching the type
*
*/
nodesByType(type: string) {
return this.childrenController?.nodesByType(type) || [];
}
/**
* sets a node as input
*
*/
setInput(
inputIndexOrName: number | string,
node: BaseNodeByContextMap[NC] | null,
outputIndexOrName?: number | string,
options?: SetInputsOptions
) {
this.io.inputs.setInput(inputIndexOrName, node, outputIndexOrName, options);
}
// emit
emit(event_name: NodeEvent.CREATED, data: EmitDataByNodeEventMap[NodeEvent.CREATED]): void;
emit(event_name: NodeEvent.DELETED, data: EmitDataByNodeEventMap[NodeEvent.DELETED]): void;
emit(event_name: NodeEvent.NAME_UPDATED): void;
emit(event_name: NodeEvent.OVERRIDE_CLONABLE_STATE_UPDATE): void;
emit(event_name: NodeEvent.NAMED_INPUTS_UPDATED): void;
emit(event_name: NodeEvent.NAMED_OUTPUTS_UPDATED): void;
emit(event_name: NodeEvent.INPUTS_UPDATED): void;
emit(event_name: NodeEvent.PARAMS_UPDATED): void;
emit(event_name: NodeEvent.UI_DATA_POSITION_UPDATED): void;
emit(event_name: NodeEvent.UI_DATA_COMMENT_UPDATED): void;
emit(event_name: NodeEvent.ERROR_UPDATED): void;
emit(event_name: NodeEvent.FLAG_BYPASS_UPDATED): void;
emit(event_name: NodeEvent.FLAG_DISPLAY_UPDATED): void;
emit(event_name: NodeEvent.FLAG_OPTIMIZE_UPDATED): void;
emit(event_name: NodeEvent.SELECTION_UPDATED): void;
emit(event_name: NodeEvent.POLY_NODE_LOCK_STATE_UPDATED): void;
emit(event_name: NodeEvent, data: object | null = null): void {
this.scene().dispatchController.dispatch(this, event_name, data);
}
private __eventsDispatcher: EventDispatcher<{[ANY_STRING]:any}> | undefined;
private _eventsDispatcher() {
return (this.__eventsDispatcher = this.__eventsDispatcher || new EventDispatcher());
}
dispatchEvent(event: BaseEvent<string>) {
this._eventsDispatcher().dispatchEvent(event);
}
addEventListener(type: string, listener: NodeEventListener) {
this._eventsDispatcher().addEventListener(type as AnyString, listener);
}
removeEventListener(type: string, listener: NodeEventListener) {
this._eventsDispatcher().removeEventListener(type as AnyString, listener);
}
// serializer
toJSON(includeParamComponents: boolean = false) {
if (!this._serializer) {
return;
}
return this._serializer.toJSON(includeParamComponents);
}
// modules
public requiredModules(): ModuleName[] | void {}
public usedAssembler(): AssemblerName | void {}
public integrationData(): IntegrationData | void {}
// poly nodes
public readonly polyNodeController: PolyNodeController | undefined;
// error processing
processError(e: unknown) {
return e;
}
public updateObjectOnAdd(object: ObjectContent<CoreObjectType>, parent: ObjectContent<CoreObjectType>) {}
public updateObjectOnRemove(object: ObjectContent<CoreObjectType>, parent: ObjectContent<CoreObjectType>) {}
}
export type BaseNodeType = TypedNode<any, any>;
export class BaseNodeClass extends TypedNode<any, any> {}
export class BaseNodeClassWithDisplayFlag extends TypedNode<any, any> {
public override readonly flags: FlagsControllerD = new FlagsControllerD(this);
}